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
Related
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. :-)
I have 2 classes in Delphi XE5 and pass one to another :
TfrmBaseList = class(TForm)
private
FListOwner: TSystemBaseList<TSystemColumnEntity>;
public
constructor Create(AListOwner: TSystemBaseList<TSystemColumnEntity>); virtual;
end
TSystemBaseList<T: TSystemColumnEntity> = class(TPersistent)
public
procedure Execute;
property SelectedValues: TObjectList<T> read
end;
procedure TSystemBaseList<T>.Execute;
var
frmList: TfrmBaseList;
begin
//frmList := TfrmBaseList.Create(Self<T>)
//frmList := TfrmBaseList.Create(Self<TSystemColumnEntity>)
frmList := TfrmBaseList.Create(???????)
end;
How can I Pass TSystemBaseList to Constructor of TfrmBaseList class?
This constructor only create a Form and then assign AListOwner to FListOwner,
Can I change this constructor to property like this:
TfrmBaseList = class(TForm)
private
FListOwner: TSystemBaseList<TSystemColumnEntity>;
public
property ListOwner: TSystemBaseList<TSystemColumnEntity> read FListOwner write FListOwner;
end
And how do I set it?
The constructor expects a concrete instantiation, an instance of:
TSystemBaseList<TSystemColumnEntity>
You are supplying an uninstantiated generic instance of type:
TSystemBaseList<T>
You have to supply a concrete instance to that constructor. In its current form you cannot instantiate the form from TSystemBaseList<T>.Execute.
You might think that because T must derive from TSystemColumnEntity that TSystemBaseList<T> would be compatible with TSystemBaseList<TSystemColumnEntity>. But that is not the case because there is no generic variance supported. Read more on this topic here: Generics and variance.
One way forward would be to make the form type generic too. Although that does not play well with the IDE form designer. I suspect a more radical re-design is needed to solve your problem. I won't offer advice on that re-design because I don't know the problem.
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.
Well,I have a parent class with a nested class declared in the "protected" tab with a protected class variable.In another unit I have a child class,which inherits from the parent class.When I try to access something protected/public from the parent class -it works,but when I try to access something protected from the nested class,it doesnt work.
type
TParent = class(TObject)
protected
class var x:integer;
type
TNested = class(TObject)
protected
class var y:integer;
end;
end;
My code in the child class:
x := 10; //works
y := 10; //undeclarated idenitifier 'y'.
TNested.y := 10; //undeclarated idenitifier 'y'
the declaration of the child class is:
type
TChild = class(TParent);
How do I access y?
y:integer is a protected field of TNested class, ie. can be only used by TNested and it's own inherited classes.
You probably may use TNested from TParent but this is beacause in Delphi you may have greater access than it should be if calling from the same unit. So TParent and TNested are in the same unit, so you may call TNested protected data from TParent. But since TChild is in different unit than TNested, it is impossible.
This will actuall work if TChild and TParent are in the same unit, because of the implicit friendship between classes within the unit.
To access y in your example, you'd need to do one of two things:
Change the scope of y to public (or create a public property for it).
Use y from a nested class that is derived from TNested.
TParent.x := 10;
TParent.TNested.y := 10;
The example you are giving is using a nested class, not inheriting it.
Nested classed can be inherited in subclasses of the declaring class:
TSubParent = class(TParent)
protected
type
TSubNested = class(TNested)
public
class var Z : integer;
end;
end;