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.
Related
I have a base class TThread which has child classes like TThreadSock and TThreadPool, which override the .Terminate() method. And childs of those childs (like TThreadSockDownload or TThreadPoolCollect) inherite those .Terminate() methods (or might even override them):
type
TThreadSock= class( TThread )
procedure Terminate; // Hides TThread.Terminate
end;
TThreadSockDownload= class( TThreadSock );
TThreadSockUpload= class( TThreadSock )
procedure Terminate; // Hides TThreadSock.Terminate
end;
TThreadPool= class( TThread )
procedure Terminate; // Hides TThread.Terminate
end;
TThreadPoolCollect= class( TThreadPool );
My problem is: I have a list which can contain everything, so the most common denominator is TThread. And from that base class I need to call the most "childish" .Terminate() method. Currently my approach is this:
var
oThread: TThread;
begin
oThread:= GetNextThread();
if oThread is TThreadSockDownload then TThreadSockDownload(oThread).Terminate() else
if oThread is TThreadSockUpload then TThreadSockUpload(oThread).Terminate() else
if oThread is TThreadPoolCollect then TThreadPoolCollect(oThread).Terminate() else ...
...and you get an idea where this leads to. Not much to speak of that I have to use this code elsewhere as well. If I'd call oThread.Terminate() then the code of the base class is executed, which is not what I want. And defining the method as virtual also won't fully work, as every child level could be the "last" one. Or not.
My ultimate goal is to generalize this as much as possible, so I don't need to ask for each class as a candidate. Maybe I'm missing something fundamental here, like GetRealClass( oThread ).Call( 'Terminate' ); and GetRealClass( oThread ).Set( 'Stop', TRUE ); would be a dream.
Am I at least able to generalize this code so I only need to write it once? Something like FindMethod on an object I also have tell its class type to?
The correct way to deal with this is to use a virtual method. This mechanism is designed to allow method dispatch based on the runtime type of an object. In other words, precisely your your laboured type checking code does.
But you are grappling with the fact that you want to name your method Terminate, which is the name of an existing method that is not virtual. So, how to get past that.
Well, if you decided on the name Terminate because your methods call the TThread.Terminate, and then do other tasks, then the framework provides you with a simple way out. Let's look at the implementation of TThread.Terminate.
procedure TThread.Terminate;
begin
if FExternalThread then
raise EThread.CreateRes(#SThreadExternalTerminate);
FTerminated := True;
TerminatedSet;
end;
Note the call to TerminatedSet. That is a virtual method whose implementation is like so:
procedure TThread.TerminatedSet;
begin
end;
It does nothing. It has been provided to allow you to override it in derived classes, and have it called whenever the non-virtual method Terminate is called.
So you would do this:
type
TMyDerivedThread = class(TThread)
protected
procedure TerminatedSet; override;
end;
....
procedure TMyDerivedThread.TerminatedSet;
begin
inherited;
// do your class specific tasks here
end;
And then the code that controls the threads can call the non-virtual Terminate method, but still have this virtual method be called.
oThread := GetNextThread;
oThread.Terminate;
Now, on the other hand, it's plausible that your Terminate methods do not call TThread.Terminate. In which case the approach would be different. You still need a virtual method, but if the TThread class does not contain an appropriate virtual already, you need to introduce one. Which means deriving a new base class in order to introduce that virtual method.
type
TBaseThread = class(TThread)
public
procedure MyTerminate; virtual; abstract;
end;
I've made this abstract but you may not want to. We can't tell because we don't know what your thread implementations do. You can decide whether or not this method should be abstract.
Now you can override this virtual method like any other, which is something I believe you already understand. The other change you need to make is that instead of holding TThread references when operating on the thread instances, you hold TBaseThread references.
I need to check if method was implemented in class.
My classes implements an interface.
Code.
IMyInterface = interface
['{538C19EB-22E3-478D-B163-741D6BB29991}']
procedure Show();
end;
TMyClassFormCustomer = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
TMyClassFormVendors = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
....
procedure TMyClassFormCustomer.Show;
var
Form: TFormCustomer;
begin
Form:= TFormCustomer.Create(nil);
try
Form.ShowModal();
finally
FreeAndNil(Form)
end;
end;
{ TMyClassFormVendors }
procedure TMyClassFormVendors.Show;
begin
end;
It is possible to check if method TMyClassFormVendors.Show have implementation?
When a method not have implementation I have disable item in menu
Thanks.
I see a couple of possibilities:
If your TMyClassFormVendors does not want to do anything in .Show then simply don't declare and implement IMyInterface. In your code you can then query for that interface with if Supports(MyClass, IMyInterface) and react accordingly (Disable Menu entry)
If your IMyInterface interface actually has more than one method declared, some of which are supported (I don't want to use the word implemented, as all methods have to be implemented) and others are not, then you should better split the interface into several different ones and proceed as described in 1
You could also declare and implement another interface "ICapabilities" that could have methods like CanShow etc. In your code you could then query your class like if (Myclass as ICapabilities).CanShow then ...
I personally would favour 2. as it is the cleanest approach IMO, but it depends on what you want to do specifically
You could use similar approach as it is used for handling the event methods. So what you want to do is instead of implementing the method directly into your class you add a property which can store a method pointer to the actual method you wanna use. And then at runtime you only check to see if the pointer to such method was already assigned or not.
The code for this would look something like this:
type
//Method type object
TMyMethod = procedure (SomeParameter: Integer) of object;
TMyClass = class(TObject)
private
//Field storing pointer to the method object
FMyMethod: TMyMethod;
protected
...
public
//proerty used for assigning and accesing the method
property MyMethod: TMyMethod read FMyMethod write FMyMethod;
end;
When you are designing the method objects you can define how many and which type of parameters you wanna use in that method.
Don't forget you need to assign method to your method property in order to use it. You can do this at any time. You can even unassign it by setting it to nil if needed.
You do this in same way as you do with event methods
MyClass.MyMethod := MyProcedure;
where MyProcedure needs to have the same parameter structure as the method object type you defined earlier.
And finally you can check to see if methods has been assigned at runtime using:
if Assigned(MyClass.MyMethod) then
begin
//Do something
end
else //Skip forward
EDIT: Another advantage of this approach is that your actual method that you use can be defined and written in completely different unit.
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 posted a question a few days ago, and the answers told me to create my own classes.
I'm an old-school programmer from the pre-OOP days my programming is well structured, efficient and organized, but lacks in any custom OOPing other than using Delphi and 3rd party objects.
I had looked at how Delphi's object oriented classes worked back when I started using Delphi 2, but they seemed foreign to my programming background. I understand how they were and are excellent for developers designing components and for visual controls on the user interface. But I never found the need to use them in the coding of my program itself.
So now I look again, 15 years later, at Delphi's classes and OOPing. If I take, for example, a structure that I have such as:
type
TPeopleIncluded = record
IndiPtr: pointer;
Relationship: string;
end;
var
PeopleIncluded: TList<TPeopleIncluded>;
Then an OOP advocator will probably tell me to make this a class. Logically, I would think this would be a class inherited from the generic TList. I would guess this would be done like this:
TPeopleIncluded<T: class> = class(TList<T>)
But that's where I get stuck, and don't have good instructions on how ot do the rest.
When I look at some class that Delphi has as an example in the Generics.Collections unit, I see:
TObjectList<T: class> = class(TList<T>)
private
FOwnsObjects: Boolean;
protected
procedure Notify(const Value: T; Action: TCollectionNotification); override;
public
constructor Create(AOwnsObjects: Boolean = True); overload;
constructor Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean = True); overload;
constructor Create(Collection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload;
property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
end;
and then their definitions of the constructors and procedures are:
{ TObjectList<T> }
constructor TObjectList<T>.Create(AOwnsObjects: Boolean);
begin
inherited;
FOwnsObjects := AOwnsObjects;
end;
constructor TObjectList<T>.Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean);
begin
inherited Create(AComparer);
FOwnsObjects := AOwnsObjects;
end;
constructor TObjectList<T>.Create(Collection: TEnumerable<T>; AOwnsObjects: Boolean);
begin
inherited Create(Collection);
FOwnsObjects := AOwnsObjects;
end;
procedure TObjectList<T>.Notify(const Value: T; Action: TCollectionNotification);
begin
inherited;
if OwnsObjects and (Action = cnRemoved) then
Value.Free;
end;
Let me tell you that this "simple" class definition may be obvious to those of you who have used OOP in Delphi for years, but to me it only provides me with hundreds of unanswered questions on what do I use and how do I use it.
To me, this does not appear to be a science. It appears to be an art of how to best structure your information into objects.
So this question, and I hope it doesn't get closed because I really need help with this, is where or how do I get the best instruction on using Delphi to create classes - and how to do it the proper Delphi way.
To me, this does not appear to be a science. It appears to be an art
of how to best structure your information into objects.
Well... Yeah. There really aren't a lot of formal requirements. It's really just a set of tools to help you organize your ideas, and eliminate a lot of duplication along the way.
Then an OOP advocator will probably tell me to make this a class. Logically, I would think this would be a class inherited from the generic TList.
Actually, the whole point of generic containers is that you don't have to make a new container class for each type of object. Instead, you'd make a new content class and then create a TList<TWhatever>.
Think of a class instance as a pointers to a record.
Now: why use a class when you could use a pointer to a record? A couple reasons:
encapsulation: You can hide some aspects of the implementation with the private keyword so that other developers (including your future self) know not to depend on implementation details that may change or that just aren't important to understanding the concept.
polymorphism: You can avoid a lot of special dispatch logic by giving each of your records a set of pointers to functions. Then, rather than having a large case statement where you do different things for each type of object, you loop through your list and send each object the same message, then it follows the function pointer to decide what to do.
inheritance: As you start making records with pointers to functions and procedures, you find that you often have cases where you need a new function-dispatch record that's very much like one you already have, except you need to change one or two of the procedures. Subclassing is just a handy way to make that happen.
So in your other post, you indicated that your overall program looks like this:
procedure PrintIndiEntry(JumpID: string);
var PeopleIncluded : TList<...>;
begin
PeopleIncluded := result_of_some_loop;
DoSomeProcess(PeopleIncluded);
end;
It's not clear to me what Indi or JumpID mean, so I'm going to pretend that your company does skydiving weddings, and that Indi means "individual" and JumpID is a primary key in a database, indicating a flight where all those individuals are in the wedding party and scheduled to jump out of the same plane... And it's vitally important to know their Relationship to the happy couple so that you can give them the right color parachute.
Obviously, that isn't going to match your domain exactly, but since you're asking a general question here, the details don't really matter.
What the people in the other post were trying to tell you (my guess anyway) wasn't to replace your list with a class, but to replace the JumpID with one.
In other words, rather than passing JumpID to a procedure and using that to fetch the list of people from a database, you create a Jump class.
And if your JumpID actually indicates a jump as in goto, then you'd probably actually a bunch of classes that all subclass the same thing, and override the same method in different ways.
In fact, let's assume that you do some parties that aren't weddings, and in that case, you don't need the Relationships, but only a simple list of people:
type TPassenger = record
FirstName, LastName: string;
end;
type TJump = class
private
JumpID : string;
manifest : TList< TPassenger >;
public
constructor Init( JumpID: string );
function GetManifest( ) : TList< TPassenger >;
procedure PrintManifest( ); virtual;
end;
So now PrintManifest() does the job of your PrintIndyEntry(), but instead of calculating the list inline, it calls Self.GetManifest().
Now maybe your database doesn't change much, and your TJump instance is always short lived, so you decide to just populate Self.manifest in the constructor. In that case, GetManifest() just returns that list.
Or maybe your database changes frequently, or the TJump sticks around long enough that the database may change underneath it. In that case, GetManifest() rebuilds the list each time it's called... Or perhaps you add another private value indicating the last time you queried, and only update after the information expires.
The point is that PrintManifest doesn't have to care how GetManifest works, because you've hidden that information away.
Of course, in Delphi, you could have done the same thing with a unit, hiding a list of cached passenger lists in your implementation section.
But clasess bring a little more to the table, when it comes time to implement the wedding-party-specific features:
type TWeddingGuest = record
public
passenger : TPassenger;
Relationship : string;
end;
type TWeddingJump = class ( TJump )
private
procedure GetWeddingManifest( ) : TList< TWeddingGuest >;
procedure PrintManifest( ); override;
end;
So here, the TWeddingJump inherits the Init and GetManifest from the TJump, but it also adds a GetWeddingManifest( );, and it's going to override the behavior of PrintManifest() with some custom implementation. (You know it's doing this because of the override marker here, which corresponds to the virtual marker in TJump.
But now, suppose that PrintManifest is actually a rather complicated procedure, and you don't want to duplicate all that code when all you want to do is add one column in the header, and another column in the body listing the relationship field. You can do that like so:
type TJump = class
// ... same as earlier, but add:
procedure PrintManfestHeader(); virtual;
procedure PrintManfiestRow(passenger:TPassenger); virtual;
end;
type TWeddingJump = class (TJump)
// ... same as earlier, but:
// * remove the PrintManifest override
// * add:
procedure PrintManfestHeader(); override;
procedure PrintManfiestRow(passenger:TPassenger); override;
end;
Now, you want to do this:
procedure TJump.PrintManifest( )
var passenger: TPassenger;
begin;
// ...
Self.PrintManifestHeader();
for guest in Self.GetManifest() do begin
Self.PrintManifestRow();
end;
// ...
end;
But you can't, yet, because GetManifest() returns TList< TPassenger >; and for TWeddingJump, you need it to return TList< TWeddingGuest >.
Well, how can you handle that?
In your original code, you have this:
IndiPtr: pointer
Pointer to what? My guess is that, just like this example, you have different types of individual, and you need them to do different things, so you just use a generic pointer, and let it point to different kinds of records, and hope you cast it to the right thing later. But classes give you several better ways to solve this problem:
You could make TPassenger a class and add a GetRelationship() method. This would eliminate the need for TWeddingGuest, but it means that GetRelationship method is always around, even when you're not talking about weddings.
You could add a GetRelationship(guest:TPassenger) in the TWeddingGuest class, and just call that inside TWeddingGuest.PrintManifestRow().
But suppose you have to query a database to populate that information. With the two methods above, you're issuing a new query for each passenger, and that might bog down your database. You really want to fetch everything in one pass, in GetManifest().
So, instead, you apply inheritance again:
type TPassenger = class
public
firstname, lastname: string;
end;
type TWeddingGuest = class (TPassenger)
public
relationship: string;
end;
Because GetManifest() returns a list of passengers, and all wedding guests are passengers, you can now do this:
type TWeddingJump = class (TJump)
// ... same as before, but:
// replace: procedure GetWeddingManfiest...
// with:
procedure GetManifest( ) : TList<TPassenger>; override;
// (remember to add the corresponding 'virtual' in TJump)
end;
And now, you fill in the details for TWeddingJump.PrintManifestRow, and the same version of PrintManifest works for both TJump and TWeddingJump.
There's still one problem: we declared PrintManifestRow(passenger:TPassenger) but we're actually passing in a TWeddingGuest. This is legal, because TWeddingGuest is a subclass of TPassenger... But we need to get at the .relationship field, and TPassenger doesn't have that field.
How can the compiler trust that inside a TWeddingJump, you're always going to pass in a TWeddingGuest rather than just an ordinary TPassenger? You have to assure it that the relationship field is actually there.
You can't just declare it as TWeddingJupmp.(passenger:TWeddingGuest) because by subclassing, you're basically promising to do all the things the parent class can do, and the parent class can handle any TPassenger.
So you could go back to checking the type by hand and casting it, just like an untyped pointer, but again, there are better ways to handle this:
Polymorphism approach: move the PrintManifestRow() method to the TPassenger class (removing the passenger:TPassenger parameter, as this is now the implicit parameter Self), override that method in TWeddingGuest, and then just have TJump.PrintManifest call passenger.PrintManifestRow().
Generic class approach: make TJump itself a generic class (type TJump<T:TPassenger> = class), and instead of having GetManifest() return a TList<TPassenger>, you have it return TList<T>. Likewise, PrintManifestRow(passenger:TPassenger) becomes PrintManifestRow(passenger:T);. Now you can say: TWeddingJump = class(TJump<TWeddingGuest>) and now you're free to declare the overridden version as PrintManifestRow(passenger:TWeddingGuest).
Anyway, that's way more than I expected to write about all this. I hope it helped. :)
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).