I come from a vb/c# background and I am having difficulty understanding the meaning of part of the following code, specifically the bit 'self.fColConsignments.Add'
TConsignment = class(TCollectionItem)
constructor Create(Collection : TCollection); override;
...
function TIFCSUMMsg.AddConsignment: TConsignment;
begin
result := TConsignment(self.fColConsignments.Add);
end;
if you background is C#, don't missinterpret that line:
result := TConsignment(self.fColConsignments.Add);
it's just a type cast and not a constructor call. In C# it would look like:
result = (TConsignment)self.fColConsignments.Add;
Presumably fcolConsignments is a collection owned by the TIFCSUMMsg instance (Self). Add adds a new item to the collection and returns the reference as the result. The result is then cast to a TConsignment to fit the result type of the AddConsignment method.
self.fColConsignments.Add probably adds a new item into fColConsignments, which must be a collection or similar, and returns it. But the declared return type may be more generic than the actual object returned, then a typecast is applied by using TConsignment(object).
The code in your example IS NOT A CONSTRUCTOR.
In C++/C#/Java/(put your C descendant language here), constructors are nameless methods. So:
class TFoo {
TFoo() { // do something }
}
....
{
TFoo myFoo;
myFoo = new TFoo()
.....
}
This a typical construction on such languages. This is NOT how Delphi works.
Constructors in Delphi have names. The convention is that they are called .Create and
they can be static or virtual ones (like any method).
The code above can be converted to:
TFoo = class
constructor Create();
end;
...
constructor TFoo.Create()
begin
// Do something;
end;
....
// Creating an object
var
myFoo: TFoo;
begin
myFoo := TFoo.Create();
...
end;
The code you exemplified were not an constructor but a
kind of typecast.
You can get more information about this (typecasts and constructors)
in the Delphi Language Guide (or Object Pascal Language Guide, depending
on the Delphi version you have available).
Related
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 TParentObj = class(TComponent) and TChildObj = class (TParentObj). In addition I have a TCustomObj = class of TParentObj.
I'm then wanting to create an object of Type parent OR Type Child depending on what a variable is. If 'child' is selected then a TChildObj should be created and so on.
In the function I have a var: obj : TCustomObj.
and then I'm trying to do:
if oReturnType = TChildObj
obj := TChildObj.create
else if oReturnType = TParentObj
obj := TParentObj.create
oReturnType is set and passed in elsewhere.
If I use 'is' instead of '=' I receive the error that the operator is not applicable to this operand type and if I use = instead I get an incompatible types error between TCustomObj and both TChildObj and TParentObj.
I've searched quite a bit but have yet to discover what I'm doing wrong and hoping someone here might be able to shed some light on it.
Thanks in advance.
First off, it appears that you've missed one of the most important points of TClass variables: they have access to constructors, and to virtual constructors.
If you put a constructor on TParentObj and declare it virtual;, and then have all descendants override; that constructor as needed, then a TClass variable declared as class of TParentObj will have access to this virtual constructor, and will behave exactly as you'd expect a virtual method to behave:
obj := oReturnType.Create();
//invoke the virtual constructor on the actual type of oReturnType
This technique is used extensively in the VCL; it's at the core of Delphi's form streaming system, for example. TComponent defines a virtual constructor whose signature is (AOwner: TComponent), and every custom component and control that needs a new constructor overrides this one. It's a very useful system, and if you look at the way it works you'll quickly gain an appreciation for the techniques involved.
But as for the actual problem, you're getting an incompatible types error between TCustomObj and both TChildObj and TParentObj because they're not compatible types.
TChildObj and TParentObj are object classes, and TCustomObj is a metaclass, or class variable. A TParentObj variable represents an object, whereas TCustomObj, which is defined as class of TParentObj, represents the class itself, not an object of that class. Declare obj as TParentObj (which will accept an object instance of TParentObj or any of its descendants) and you'll be good.
You can use the classtype TCustomObj to create the instance
function ObjFactory( AClass : TCustomObj ): TParentObj;
begin
Result := AClass.Create;
end;
procedure foo;
var
LObj : TParentObj;
begin
// instance of TParentObj
LObj := ObjFactory( TParentObj );
// or instance of TChildObj
LObj := ObjFactory( TChildObj );
end;
I've been trying to extend a bunch of library classes inheriting from the same base class by overriding a virtual method defined in that base class. The modification is always the same so instead of creating N successors of the library classes I decided to create a generic class parameterized by the library class type, which inherits from the class specified by parameter and overrides the base class' method.
The problem is that the code below doesn't compile, the compiler doesn't allow inheriting from T:
program Project1;
type
LibraryBaseClass = class
procedure foo; virtual;
end;
LibraryClassA = class(LibraryBaseClass)
end;
LibraryClassB = class(LibraryBaseClass)
end;
LibraryClassC = class(LibraryBaseClass)
end;
LibraryClassD = class(LibraryBaseClass)
end;
MyClass<T:LibraryBaseClass> = class(T) //Project1.dpr(20) Error: E2021 Class type required
procedure foo; override;
end;
procedure LibraryBaseClass.foo;
begin
end;
procedure MyClass<T>.foo;
begin
end;
begin
MyClass<LibraryClassA>.Create.foo;
MyClass<LibraryClassB>.Create.foo;
MyClass<LibraryClassC>.Create.foo;
MyClass<LibraryClassD>.Create.foo;
end.
Any ideas how to make this work? Maybe there is a way to trick the compiler into accepting something equivalent because, for example, inheriting from Dictionary<T,T> compiles without problems.
Or what would you do if you had the same goal as I? Keep in mind that in the real situation I need to override more than one method and add some data members.
Thank you
As you've been told already, this is valid with C++ templates, not with C# or Delphi generics. The fundamental difference between templates and generics is that conceptually, each template instantiation is a completely separately compiled type. Generics are compiled once, for all possible types. That simply is not possible when deriving from a type parameter, because you could get constructs such as
type
LibraryBaseClass = class
procedure foo; virtual;
end;
LibraryClassA = class(LibraryBaseClass)
procedure foo; reintroduce; virtual;
end;
LibraryClassB = class(LibraryBaseClass)
end;
MyClass<T:LibraryBaseClass> = class(T)
procedure foo; override; // overrides LibraryClass.foo or LibraryClassA.foo ?
end;
Yet this can work in C++, because in C++ MyClass<LibraryClassA> and MyClass<LibraryClassB> are completely separated, and when instantiating MyClass<LibraryClassA>, foo is looked up and found in LibraryClassA before the base class method is found.
Or what would you do if you had the same goal as I? Keep in mind that in the real situation I need to override more than one method and add some data members.
It is possible to create types at runtime, but almost certainly an extremely bad idea. I have had to make use of that once and would have loved to avoid it. It involves reading the VMT, creating a copy of it, storing a copy of the original LibraryBaseClass.foo method pointer somewhere, modifying the VMT to point to a custom method, and from that overriding function, invoking the original stored method pointer. There's certainly no built-in language support for it, and there's no way to refer to your derived type from your code.
I've had a later need for this in C# once, too, but in that case I was lucky that there were only four possible base classes. I ended up manually creating four separate derived classes, implementing the methods four times, and using a lookup structure (Dictionary<,>) to map the correct base class to the correct derived class.
Note that there is a trick for a specific case that doesn't apply to your question, but may help other readers: if your derived classes must all implement the same interface, and requires no new data members or function overrides, you can avoid writing the implementation multiple times:
type
IMySpecialInterface = interface
procedure ShowName;
end;
TMySpecialInterfaceHelper = class helper for TComponent
procedure ShowName;
end;
procedure TMySpecialInterfaceHelper.ShowName;
begin
ShowMessage(Name);
end;
type
TLabelWithShowName = class(TLabel, IMySpecialInterface);
TButtonWithShowName = class(TButton, IMySpecialInterface);
In that case, the class helper method implementation will be a valid implementation for the interface method.
In Delphi XE and higher, you could also try something completely different: TVirtualMethodInterceptor.
What you are attempting to do is simply not possible with Delphi generics.
For what it is worth, the equivalent code is also invalid in C# generics. However, your design would work with C++ templates.
I probably misunderstood your description of the problem but from your simplified example it seems you could "turn it around" and insert a class in the hierarchy in the middle like this:
program Project1;
type
LibraryBaseClass = class
procedure foo; virtual;
end;
LibraryBaseFooClass = class(LibraryBaseClass)
procedure foo; override;
end;
LibraryClassA = class(LibraryBaseFooClass)
end;
LibraryClassB = class(LibraryBaseFooClass)
end;
LibraryClassC = class(LibraryBaseFooClass)
end;
LibraryClassD = class(LibraryBaseFooClass)
end;
procedure LibraryBaseClass.foo;
begin
end;
procedure LibraryBaseFooClass.foo;
begin
end;
begin
LibraryClassA.Create.foo;
LibraryClassB.Create.foo;
LibraryClassC.Create.foo;
LibraryClassD.Create.foo;
end.
I'm pretty sure it's possible to call a class and its member function dynamically in Delphi, but I can't quite seem to make it work. What am I missing?
// Here's a list of classes (some code removed for clarity)
moClassList : TList;
moClassList.Add( TClassA );
moClassList.Add( TClassB );
// Here is where I want to call an object's member function if the
// object's class is in the list:
for i := 0 to moClassList.Count - 1 do
if oObject is TClass(moClassList[i]) then
with oObject as TClass(moClassList[i]) do
Foo();
I get an undeclared identifier for Foo() at compile.
Clarification/Additional Information:
What I'm trying to accomplish is to create a Change Notification system between business classes. Class A registers to be notified of changes in Class B, and the system stores a mapping of Class A -> Class B. Then, when a Class B object changes, the system will call a A.Foo() to process the change. I'd like the notification system to not require any hard-coded classes if possible. There will always be a Foo() for any class that registers for notification.
Maybe this can't be done or there's a completely different and better approach to my problem.
By the way, this is not exactly an "Observer" design pattern because it's not dealing with objects in memory. Managing changes between related persistent data seems like a standard problem to be solved, but I've not found very much discussion about it.
Again, any assistance would be greatly appreciated.
Jeff
First of all you're doing something very unusual with TList: TList is a list of UNTYPED POINTERS. You can add any pointer you want to that list, and when you're doing moClassList.Add( TClassA ) you're actually adding a reference to the class TClassA to the list. Technically that's not wrong, it's just very unusual: I'd expect to see TClassList if you actually want to add a class! Or TList<TClass> if you're using a Delphi version that support it.
Then you're looping over the content of the list, and you're checking if oObject is of the type in the list. So you do want classes in that list after all. The test will work properly and test rather the object is of that type, but then when you do with oObject as TClass(moClassList[i]) do you're actually casting the object to... TObject. Not what you wanted, I'm sure!
And here you have an other problem: Using Foo() in that context will probably not work. TObject doesn't contain a Foo() method, but an other Foo() method might be available in context: That's the problem with the with keyword!
And to finally answer the question in the title bar: Delphi is not an Dynamic language. The compiler can't call a method it doesn't know about at compile time. You'll need to find a OOP way of expressing what you want (using simple inheritance or interfaces), or you may call the function using RTTI.
Edited after question clarification.
All your business classes need to implement some kind of notification request management, so your design benefits allot from a base class. Declare a base class that implements all you need, then derive all your business classes from it:
TBusinessBase = class
public
procedure RegisterNotification(...);
procedure UnregisterNotification(...);
procedure Foo;virtual;abstract;
end;
In your initial example you'd no longer need the list of supported classes. You'll simply do:
oObject.Foo;
No need for type testing since Delphi is strongly typed. No need for casting since you can declare oObject": TBusinessBase.
Alternatively, if you for some reason you can't change the inheritance for all your objects, you can use interfaces.
TClass is defined:
TClass = class of TObject;
You then write oObject as TClass which is effectively a null operation since oObject already was a TObject.
What you need is something like this:
type
TFoo = class
procedure Foo();
end;
TFooClass = class of TFoo;
TBar = class(TFoo)
procedure Bar();
end;
....
if oObject is TFooClass(moClassList[i]) then
with oObject as TFooClass(moClassList[i]) do
Foo();
This explains why your attempts to call Foo() does not compile, but I simply have no idea what you are trying to achieve. Even after your clarification I'm struggling to understand the problem.
Here's a really contrived example (using an array instead of a TList) that I think is what you're trying to do (error handling and try..finally intentionally omitted for clarity).
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TBaseClass=class(TObject)
procedure Foo; virtual;
end;
TClassA=class(TBaseClass)
procedure Foo; override;
end;
TClassB=class(TBaseClass)
procedure Foo; override;
end;
TClassArray= array of TBaseClass;
{ TClassB }
procedure TClassB.Foo;
begin
Writeln('TClassB.Foo() called.');
end;
{ TClassA }
procedure TClassA.Foo;
begin
Writeln('TClassA.Foo() called.');
end;
var
Base: TBaseClass;
ClassArr: TClassArray;
{ TBaseClass }
procedure TBaseClass.Foo;
begin
Writeln('TBaseClass.Foo called!!!!!!!!');
end;
begin
ClassArr := TClassArray.Create(TClassA.Create, TClassB.Create);
for Base in ClassArr do
Base.Foo;
for Base in ClassArr do
Base.Free;
ReadLn;
end.
I have a persistence framework, and I am trying to use generics so I don't have to keep creating new list classes for each type of object I want to store in a type safe way.
I have a method that returns the class of the contained object in the list class (so I know which queries to run and which object to create.
As an example, it looks something like this:
type
TMyObject = class
end;
TMyObjectClass = class of TMyObject;
TMyObjectList = class
public
function ListClass: TMyObjectClass; virtual; abstract;
end;
TMyObjectList<T: TMyObject, constructor> = class(TMyObjectList)
public
function ListClass: TMyObjectClass; override;
end;
implementation
{ TMyObjectList<T> }
function TMyObjectList<T>.ListClass: TMyObjectClass;
begin
result := T; // <==== this wont compile
end;
end.
Is there a way of returning the class of the generic parameter in this case?
Thanks
N#
(using Delphi 2009)
This is a known issue in Delphi 2009. It's been fixed in 2010. I just tested it and your code compiles just fine there.
T is not an instance of an object.
In your specific example, you should write something like:
result := self;
I think you're looking the wrong way...