I have subclassed an FMX component and I want to override a (Virtual) protected procedure.
This procedure has a number of params, which are declared as "public type" in the class of the component.
When I try to override the procedure, I get an error that one of the types is not declared although my component subclasses the original one. Shouldn't I be able to get access to it?
The class is defined like this:
TTabControl = class (...)
public type
TTabBarButton = (Left, Right)
TTabBarButtons = set of TTabButton;
....
protected
procedure DoUpdate(const TabBarButtons: TTabBarButtons; ....); virtual;
...
end;
Now, I have subclassed this class and want to override DoUpdate.
TMyClass = class (TTabControl)
protected
procedure DoUpdate(const TabBarButtons: TTabBarButtons; ....); override;
....
end;
The compiler complains that TTabBarButtons in my class is not defined. If I redefine TTabBarButtons as public type in my class, then it says that the definition differs from the base class.
Can you please help me with this?
Thanks a lot.
This works for me:
TMyClass = class (TTabControl)
protected
procedure DoUpdate(const TabBarButtons: TTabControl.TTabBarButtons; ....); override;
....
end;
There are other parameters you'll need to do the same thing with; the fun comes when you start tracking all of those types and declarations down. Good luck. :-)
Related
I have written this simple code:
type
TTestA = class
strict protected
a: integer;
public
f: integer;
constructor Create(x: Integer);
end;
type
TTestB = class(TTestA)
strict private
c: integer;
end;
I use strict because these classes are in the same unit as the TForm1 class. Since a is protected by definition, it should be accessible only in subclasses, but then why this code doesn't work?
procedure TForm1.Button1Click(Sender: TObject);
var
K: TTestB;
begin
k := TTestB.Create(3);
value = k.a; //I cannot access a
end;
Also, the protected can be useful to create an abstract class. In C++, if I declare a constructor as protected, I cannot create an instance of the object and only subclasses can. Can Delphi do this?
I am having the same problem with the variable and the constructor.
protected access means "accessible in the class and any sub-class".
This means that a in your example will be accessible to methods in class TTestB but this does not extend to consumers of instances of TTestB (or TTestA).
The code in TForm1 is part of a class which is not a subclass of TTestA.
Put another way, TForm1 does not inherit from TTestA and therefore cannot access any private or protected members of k.
The answer is in your own question:
Since a is protected by definition it should be accessible only in subclasses
Regardless of the fact that TForm1 uses a local k variable of type TTestB, since TForm1 itself is not a subclass of TTestA, it does not have access to the k.a member. TTestB is a subclass of TTestA, so internally it has access to a.
This is explained in more detail in Embarcadero's documentation:
Classes and Objects (Delphi): Visibility of Class Members
I saw a code like this the other day:
type
TcxGridTableControllerAccess = class (TcxGridTableController);
TMycxGridDBTableView = class (TcxGridDBTableView)
protected
function GetViewDataClass: TcxCustomGridViewDataClass; override;
end;
TMycxGridViewData = class (TcxGridViewData)
protected
function GetFilterRowClass: TcxGridFilterRowClass; override;
end;
TMycxGridFilterRow = class (TcxGridFilterRow)
protected
procedure SetValue(Index: Integer; const Value: Variant); override;
end;
TcxGridDBTableView = class (TMycxGridDBTableView);
TMycxGridDBTableView inherited from TcxGridDBTableView that inherited from TMycxGridDBTableView. Searched for Cyclic Inheritance but only Java results.
What is this called?
Ps: I don't have the full buildable code with me.
The example code doesn't do what you think it does. You see TMycxGridDBTableView being defined as a descendant of TcxGridDBTableView, and then you see TcxGridDBTableView, defined as a descendant of TcxGridDBTableView.
However, the TcxGridDBTableView you see at the top is not the same TcxGridDBTableView that you see later. The first one refers to a class declared elsewhere, in some other unit. The next occurrence is declaring a new class in this unit that happens to have the same base name as the other unit's class.
This technique is known as an interposer class. It's used to introduce a new GetViewDataClass method, but still end up with the same class name. The form that uses controls with that name will use the new version of the class instead of the original version. It's a way to customize a VCL control without having to compile and install a custom package.
What you show is not cyclic inheritance. What happens is that dxSample.TMycxGridDBTableView inherits from a TcxGridDBTableView in another unit, probably cxGridDBTableView.TcxGridDBTableView. And dxSample.TcxGridDBtableView inherits from dxSample.TMycxGridDBTableView.
Your code is equivalent to:
type
TcxGridTableControllerAccess = class(TcxGridTableController);
{ Note: this does NOT inherit from the TcxGridDBTableView defined }
{ a little further on in the source. It inherits from the original }
{ DevEx TcxGridDBTableView. }
TMycxGridDBTableView = class(cxGridDBTableView.TcxGridDBTableView)
protected
function GetViewDataClass: TcxCustomGridViewDataClass; override;
end;
TMycxGridViewData = class(TcxGridViewData)
protected
function GetFilterRowClass: TcxGridFilterRowClass; override;
end;
TMycxGridFilterRow = class(TcxGridFilterRow)
protected
procedure SetValue(Index: Integer; const Value: Variant); override;
end;
TcxGridDBTableView = class(TMycxGridDBTableView);
So the hierarchy is:
cxGridDBTableView.TcxGridDBTableView
|
v
dxSample.TMycxGridDBTableView
|
v
dxSample.TcxGridDBTableView
So dxSample.TMycxGrdiDBTableView does not inherit from dxSample.TcxGridDBTableView, but from cxGridDBTableView.TcxGridDBTableView instead, so there is no so called cyclic inheritance there. The whole misunderstanding comes from the fact that the two classes in the different units have the same name and that the first declaration does not fully qualify the class it is inheriting from.
Now, if someone puts the unit dxSample after cxridDBTableView in his or her uses clause, then dxSample.TCxGridDBTableView is used, instead of the original DevEx class. This is called interposing.
When people want to modify the behaviour of the VCL and FireMonkey, it is not unusual to see interposer classes like
type
TVCLClass = class(OriginalVCLUnit.TVCLClass)
// modifications to the original TVCLClass
end;
or
type
TMyVCLClass = class(OriginalVCLUnit.TVCLClass)
//
end;
TVCLClass = class(TMyVCLCLass);
The code you showed does the latter.
I have a class that I want to keep private, because I only use it in the implementation section.
However it is used by a class that is declared public in the interface section.
Is there a way to do something like this:
unit x;
interface
type
TPrivate = class; //forward declaration
TPublic = class(TSomething)
private
FPrivate: TPrivate;
procedure DoStuffWithFPrivate;
public
//...
end;
implementation
type
TPrivate = class(TObject)
procedure Test;
end;
Obviously the above code gives an error:
[dcc32 Error] UnitX.pas(27): E2086 Type 'TPrivate' is not yet completely defined
I don't want to resort to cheap tricks like:
FPrivate = TObject
....
procedure TPublic.DoStuffWithFPrivate;
begin
TPrivate(FPrivate).Test;
Is there a way to do what I want without having to spill TPrivate's internal details in the interface?
I know it's possible to declare TPrivate as a strict private sub type of TPublic, however I don't like the pollution of the interface section this gives.
Is there a way to keep TPrivate out the of interface section (as much as possible) whilst maintaining type safety?
As long as TPrivate is not used anywhere in the interface of TPublic besides the field declaration (f.i. as a methods parameter type) you can use a local class helper to achieve this.
Note: FPrivate is not a good name for that field!
interface
type
THiddenActual = class
end;
TPublic = class
private
FActual: THiddenActual;
procedure DoStuffWithFPrivate;
public
end;
implementation
type
TActual = class(THiddenActual)
public
procedure Foo;
end;
type
TPublicHelper = class helper for TPublic
private
function GetActual: TActual;
procedure SetActual(const Value: TActual);
public
property Actual: TActual read GetActual write SetActual;
end;
procedure TActual.Foo;
begin
end;
function TPublicHelper.GetActual: TActual;
begin
Result := FActual as TActual;
end;
procedure TPublicHelper.SetActual(const Value: TActual);
begin
FActual := Value;
end;
procedure TPublic.DoStuffWithFPrivate;
begin
Actual.Foo;
end;
OK, it is merely a little variance of that cheap trick, but what are the alternatives? You have to take what is available, don't you?
I'm not sure what exactly your definition of "pollution" is as regards the interface section, but if it's just a matter of keeping it from drowning your TPublic class definition with noise then one option might be simply inheritance :
TBasePublic = class(TSomething)
private
type
TPrivate = class
// ...
// ... keep TPrivate definition separate
end;
end;
TPublic = class(TBasePublic)
private
FPrivate : TPrivate;
end;
This would even allow you to define TPrivate in a completely different unit if segregation is your goal.
I created a class method in a class that implements an interface, but I can't seem to define it inside of the interface.
IMyClass = interface
procedure someproc();
class function myfunc() : TMyClass; // compiler doesn't like this!
end;
TMyClass = class( TInterfacedObject, IMyClass )
public
procedure someproc();
class function myfunc() : TMyClass;
end;
I want myfunc() to create and return an instance of TMyClass. For example:
somefunc( TMyClass.myfunc(), ... );
creates an instance of TMyClass and passes it into somefunc.
I can define function myfunc() : TMyClass; in the IMyClass interface, but if I put class in front of it, the compiler gives me an error. If I leave it off, it gives me several other errors "E2291 Missing implementation of interface method xyz.myfunc" It just doesn't accept the same signature in the interface as in the class.
I thought I've seen this work before (class methods defined in interfaces) but maybe not.
If this isn't supported directly, how do you do it?
(I'm using Delphi XE5, in case it matters.)
Interfaces are not classes and do not support methods marked as class, they only support non-class methods that are implemented and called on object instances.
What you are looking for will most likely have to be implemented as a class factory instead of using an interface.
What you are trying to achieve is impossible because it violates binary specifications of Delphi interfaces.
Any interface method in Delphi have hidden first parameter - pointer to an instance. Instance methods have the same hidden parameter (Self, the same pointer to an instance) and because of this they are binary compatible with interface methods.
On the other hand the hidden parameter of a class method is a pointer to the class vtable, so Delphi class methods are binary incompartible with Delphi interface methods.
Hypothetically you could think of the following (binary compatible) declarations:
IMyClass = interface
procedure someproc();
function myfunc(): TMyClass;
end;
TMyClass = class( TInterfacedObject, IMyClass )
public
procedure someproc();
class function myfunc(Inst: TMyClass) : TMyClass; static;
end;
Here I use static specifier to remove the hidden parameter (class vtable pointer) we don't need and add the instance pointer parameter explicitely; but the compiler does support such syntax because the above is essentially the same as a simpler
IMyClass = interface
procedure someproc();
function myfunc(): TMyClass;
end;
TMyClass = class( TInterfacedObject, IMyClass )
public
procedure someproc();
function myfunc() : TMyClass;
end;
You can define a class method (class procedure in Delphi) in a class definition (TMyClass) but not in an interface (IMyClass ).
I am trying to implement an interface to convert records in a dataset to Delphi records in a pre-generics version of Delphi. I don't like the interface at the moment, as it will always need calls to Supports which I'd like to avoid if possible and was wondering if there's a better way of doing it that I'm missing.
So far I have an navigation interface and data retrieval interface defined:
IBaseRecordCollection = interface
procedure First;
procedure Next;
function BOF: boolean;
... // other dataset nav stuff
end;
IRecARecordCollection = interface
function GetRec: TRecA;
end;
IRecBRecordCollection = interface
function GetRec: TRecB;
end;
Basically I have a concrete base class that contains a private dataset and implements IBaseRecordCollection and concrete class for each RecordCollection interface which derives from an abstract class implementing the IBaseRecordCollection (handled by an implements property) with the implementation of the record retrieval routine:
TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
constructor Create(aRecordCollection: IBaseRecordCollection);
end;
TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRecARecordCollection);
public
function GetRec: TRecA;
end;
Now, to use this I'm forced to have a builder that returns a IRecARecordCollection and then mess around with Supports, which I'm not keen on as it will always be used in this fashion.
i.e.
procedure GetMyRecASet;
var
lRecARecordCollection: IRecARecordCollection;
lRecordCollection: IBaseRecordCollection;
begin
lRecARecordCollection := BuildRecACollection;
if not supports(lRecARecordCollection, IBaseRecordCollection, lRecordCollection) then
raise exception.create();
while not lRecordCollection.EOF do
begin
lRecARecordCollection.GetRec.DoStuff;
lRecordCollection.Next;
end;
end;
Although this works, I'm not keen on the supports call and mixing my lRecordCollections and my lRecARecordCollections like this. I had originally hoped to be able to do something like:
IBaseRecordCollection = interface
// DBNav stuff
end;
IRecARecordCollection = interface (IBaseRecordCollection)
function GetRec: TRecA;
end;
TRec1RecordCollection = class(TInterfacedObject, IRecARecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
function GetRec: TRecA;
end;
but unfortunately Delphi wasn't smart enough to realise that the implementation of IRecARecordCollection was split over the base IBaseRecordCollection in the Collection property implements call and the TRec1RecordCollection object.
Are there any other suggestions for neater ways to acheive this?
-- edit to give a (longer) reply to #David's answer than possible in a comment
The suggested solution of:
IBaseRecordCollection = interface ['{C910BD0A-26F4-4682-BC82-605C4C8F9173}']
function GetRecNo: integer;
function GetRecCount: integer;
function GetFieldList: TFieldList;
function EOF: boolean;
function BOF: boolean;
...
end;
IRec1RecordCollection = interface (IBaseRecordCollection) ['{E12F9F6D-6D57-4C7D-AB87-8DD50D35DCA2}']
function GetRec: TRec1;
property Rec: TRec1 read GetRec;
end;
TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
constructor Create(aRecordCollection: IBaseRecordCollection);
end;
TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRec1RecordCollection, IBaseRecordCollection)
private
function GetRec: TRec1;
public
property Rec: TRec1 read GetRec;
end;
isn't compiling. It's complaining that TRec1RecordCollection cannot find methods related to IBaseRecordCollection. I also tried moving the Collection property from Abstract to Rec1RecordCollection and redeclaring the property in TRec1RecordCollection all with the same result
Looking a bit deeper it appears that direct inheritance of a class implementing IBaseRecordCollection would work but Delphi can't handle doing it indirectly via a property using implements.
Your code is almost there. The implements directive in your code fails to compile because you only declared that your class implements the derived interface. As it stands, your class does not implement the interface that the implements directive refers to, namely IBaseRecordCollection. You might think that would be inferred from the inheritance but it is not.
To solve your problem you simply need to declare that TRec1RecordCollection implements both interfaces:
type
TRec1RecordCollection = class(TInterfacedObject, IBaseRecordCollection,
IRecARecordCollection)
....
end;
Make just the one small change and your code will compile.
Update
Your edit to the question changes this somewhat. The code in my answer does indeed compile, given the code in your original question. However, add any method into IBaseRecordCollection and the compile will not accept it.
The compiler should accept this code and the fact that it does not is because of a compiler bug. Modern versions of Delphi will accept the code in your update to the question.
Unless you upgrade your compiler you will not be able to make your intended design work.