Delphi TDictionary : values are replace automatically when have the same key - delphi

I am using delphi XE5.
This a resume of my code, Next code works, but there have to be something in my code that destroy normal behaviour:
unit Class1;
type
TClass1 = class
private
FDic:TDictionary<String,String>.Create;
public
constructor create;
procedure insertValue(key,value:String);
end;
implementation
constructor TClass1.create;
begin
FDic:=TDictionary<String,String>.Create;
end;
procedure insertValue(key,value:String);
begin
if(FDic.ContainsKey(key))then
FDic[key] := value
else
begin
FDic.Add(key,value);
end;
end.
And now another unit:
unit Class2;
type
uses Class2;
TClass1 = class
public
class2 :TClass2;
TS: TStringList;
procedure DoSomething;
end;
implementation
procedure TClass1.DoSomething;
var
i: Integer;
c,test: TClass1;
begin
c := TClass1.create;
c.insertValue('height','.34cm');
c.insertValue('width','22cm');
c.insertValue('radio','2cm');
TS.AddObject('square',c);
c := TClass1.create;
c.insertValue('height','.88cm');
c.insertValue('width','11cm');
c.insertValue('top','12cm');
TS.AddObject('circle',c);
test := TS.Objects[0] as TClass1;//test.FDic height should be .34cm but gets the value of the last object create, the same for width an common keys.
//here when I look for the values of FDic test.FDic.Items['height'] the value is .88cm instead of .34cm, each time the values of any element of the Dictionary is replace with the previous of the last object created. And the memory address is the same. Why don't create a new memory address for any new element if it is a different object.
That is a resume of my code, I can put all my code because is too big, but I would like to know where I can search to resolve this problem. I am not is not easy, maybe I am not the only one with that problema, maybe some class in the uses, class variables, there is something that causes a memory problema in that dictionary, but no way to find it.

It's a little hard to be sure of the problem because you posted code that does not compile. For future reference, please don't do that. It's good to cut down the code to a small size, but you should then make it into a small console application that compiles and runs and demonstrates the fault. In spite of this, I think that I can see where the problem is.
You are creating objects and then adding them to a string list with
TS.AddObject(...);
But then you never free those objects. That, I guess, is the source of the leak. You can deal with this by setting the OwnsObjects property of the string list to True.
Specifies whether the string list owns the objects it contains.
The OwnsObjects property specifies whether the string list owns the
stored objects or not. If the OwnsObjects property is set to True,
then the Destroy destructor will free up the memory allocated for
those objects.
That I think will explain the leaks. The other part of your question is why index 0 contains the item that you added second. The only explanation for that, given the code in the question, is that your string list has the Sorted property set to True.
Again, I'm inferring this with a little intuition, but if you have only posted a complete program that compiled and run then your question could have been answered with complete certainty.
I do wonder whether or not a string list is the correct class to be using here. Perhaps you would be better with TObjectDictionary<string, TClass1>. You would create it like this:
dict := TObjectDictionary<string, TClass1>.Create([doOwnsValues]);
The use of doOwnsValues tells the dictionary that it is take assume ownership of the objects that you add as values and destroy them when they are removed from the dictionary, or when the dictionary is destroyed.
Note also that your insertValue method can be implemented in a far simpler way using the AddOrSetValue method.

Related

Does this FreeAndNil(lAux) frees my double field too?

I'm correcting some (a lot) memory leaks of a project. I have some Delphi experience but is not my main language so I'm extra careful at freeing stuff.
I've already tried it and everything looks fine, but I thought that it would free the Field too (not what I want). Does that happen just with Objects?
TMainObject = class(TObject)
public
FDoubleField : double;
*
*
*
procedure TMainObject.CalculateSomeFieldValue();
var
lAux : TAuxObject;
begin
lAux := TAuxObject.Create;
lAux.RecoverData;
if **some condition** then
begin
FDoubleField := lAux.DoubleProperty;
end
else
begin
FDoubleField := lAux.OtherDoubleProperty;
end;
FreeAndNil(lAux);
end
Thanks in advance
Edit: I do not want to lose FDoubleField because I will use it later, but I need to free lAux.
The FDoubleField field doesn't need to be freed because it will be automatically "freed" as soon it goes out of scope.
Some tips:
You don't need to use FreeAndNil(lAux) in this case, just use lAux.Free because there's no need to set a pointer to nil which will no longer be used.
At the beginning, you can set ReportMemoryLeaksOnShutDown := True (documentation). In this way, you will be notified about any memory leak when the application closes.
Use try-finally blocks for being sure to always free the object you created
Modified code using try-finally block and Free:
procedure TMainObject.CalculateSomeFieldValue();
var
lAux : TAuxObject;
begin
lAux := TAuxObject.Create;
try
lAux.RecoverData;
if **some condition** then
begin
FDoubleField := lAux.DoubleProperty;
end
else
begin
FDoubleField := lAux.OtherDoubleProperty;
end;
finally
lAux.Free;
end;
end
In the example you give, you don't need to free anything for a variable of type double in a class (Also called a "field").
Field that must be freed are classes or simple pointers of any type that have been allocated.
The normal place to free data in a class is in the destructor.
Also pay attention to interfaces. They are generally reference counted and as such MUST NOT be freed. Frequently, interfaces are assigned a value by calling a class constructor and you must really look if the value returned by the class constructor is assigned to a variable of an interface type or to a variable of some object type. Assuming TAuxObject also implement the interface IAuxInterface, your have the following valid code:
var
AuxObj : TAuxObject;
AuxIntf : IAuxInterface;
begin
AuxIntf := TAuxObject.Create; // This one must NOT be freed
AuxObj := TAuxObject.Create; // This one must be freed
try
....
finally
AuxObj.FRee;
end;
end;
Properly using interfaces is a big subject. There are cases where interfaces are not reference counted.
There are in Delphi libraries a few classes designed to be ancestor for object supporting interface (For example TInterfacedObject). This ancestor implement reference counting properly (Methods QueryInterface, _AddRef and _Release). If your object do no derive from a class already implementing reference counting, then if you need reference counting then you must implement methods QueryInterface, _AddRef and _Release. As soon as they are defined, some compiler magic will call them when it is needed. And by the way freeing the object must actually done by the _Release method implementation.
Things becomes even more complex if the developer make use of weak and unsafe keywords to mark the interface type variable.

Record in TDictionary

How can I use record in TDictionary?
TMyRec = record
a: Integer;
b: Integer;
end;
...
dictionary = TDictionary<String, TMyRec>.create();
...
dictionary[key].a := 30;<<<
Here the compiler gives an error: "Left side cannot be assigned to". How can I solve this problem without creating a separate function for writing myFunc(a, b: Integer): TMyRec?
dictionary[key] returns a copy of the record held by the dictionary. The compiler prevents you from modifying that because it would serve no purpose.
As an aside, older versions of the program would accept your code and it was very confusing that the modification to the record would be lost. You'd make an assignment but nothing visible changed because what you assigned was a nameless local variable.
Clearly you intend to modify the record held in the collection. In order to do that you need to assign the entire record. Read the record from the collection into a local variable. Modify the local variable. Write the updated value back to the collection. Like so:
var
rec: TMyRec;
...
rec := dictionary[key];
rec.a := 30;
dictionary[key] := rec;
One of the frustrating aspects of this is that the code needs to perform two dictionary lookups, even though we know that the second one will find the same record as the first one. Not even the mighty Spring4d dictionary can do this with a single lookup.
David Heffernans answer is what you're after, but I would like to offer an additional warning. Records can have properties just like classes, with getters and setters, and if your record has such properties your code will compile, but it will still not change the actual record value.
TMyRec = record
private
FA : integer;
procedure SetA(const Value: integer);
function GetA : integer;
public
{ Warning: When used on result from dictionary lookup, only the COPY will be
altered, not the actual record in the dictionary! }
property A : integer read GetA write SetA;
end;
A very simple workaround is to use the List property of the record.
You can say:
dictionary.list[key].a := 30;
This will access the dynamic array that backs up the TList via the List property. The compiler already supports direct access to a dynamic array.
If you can login to quality.embarcadero.com, you can see the full discussion of this issue raised as: RSP-23136: We should be able to assign a value to one element in a list of records - posted Dec 18, 2018 and resolved Nov 21, 2019.
The issue was closed with the comment:
"This works as expected. Alternative coding style was provided."

Delphi release interface pointer

I'm struggling with interfaces in Delphi. This question might be trivial, but I am new to Delphi, so please excuse.
I have a TreeView with customized nodes, which hold an interface to an object (essentially, just like it is proposed here: Storing interface pointer inside tree view nodes).
The problem is, once I delete a node (in order to redraw the treeview) and set the interface variable to nil (freeing won't do with interfaces for some reason I haven't fully understood), the weirdest thing happens:
In my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.
I can't explain this. Does anybody know a workaround, or the possible reason for this behavior? BTW, I am using Delphi 10.2 Tokyo.
Here's my quite unspectacular destroy method:
myNode.destroy;
begin
intf:= nil;// intf holds the interface to the object
end;
Edit: this is a simplified version of my code:
The object I'm referring to: (I have several similar classes which look like obj but are slightly different and I don't know which one will be stored in the interface, but all share these variables)
Obj = class(InterfacedObject, IMyinterface)
count: integer; //this remains the same
children: array of ChildObj; //this will be emptied
name: string; //this will be set to ''
procedure addChild;
procedure IMyInterface.add = addChild;
end;
My customized treeNode:
MyNode = class(TTreeNode)
Intf: IMyinterface;
destructor destroy; override;
end;
Inside my class manages the TreeView:
MyForm.ReloadTree;
begin
if myTreeView.Items.Count > 0 then
begin
myTreeView.Items.Clear;
end
for I:= 0 to RootObj.Count-1 do
begin
myTreeView.Items.AddChild(MyTreeview.Items[0], RootObj.Children[i].name);
(myTreeView.Items[0][i] as MyNode).Intf := Intf(RootObj.Children[i]);
//I will proceed iterating over all children and their children, doing
//the same process, a level higher in the treeView
//...
end;
end;
in my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.
This is perfectly normal behavior. Strings and interfaces are compiler-managed types. Integers are not. When an object is destructed, compiler-managed data members are automatically deallocated as needed, which in the case of strings and interfaces involves nil'ing the pointers to their referenced data. The containing object itself is not zeroed out completely, so non-managed types, like integers, are not overwritten in memory.
Now, that being said, I see some bugs in your ReloadTree() procedure.
Your for loop is exceeding the upper bound of the RootObj.Children[] list.
When calling AddChild(), the second parameter is a string. You are passing RootObj.Children[i] in that parameter. But, in the next statement, you are type-casting the same RootObj.Children[i] value to an interface when assigning the MyNode.Intf field. A string is not an interface. So, what exactly does RootObj.Children[] contain - strings or interfaces?
When assigning the MyNode.Intf field, you are always accessing the first node in the TreeView, instead of the newly added node.

Object inheritance involving enumerated types

Delphi 2007, moving to Delphi XE over the next year.
Our product makes extensive use of a third-party component. We don't use the component directly, but instead use a custom descendant of it, to which we've added quite a lot of extra behavior (the custom descendant component was developed several years ago by developers who have since have retired).
In the source unit of the third-party Parent class, some enumerated types are declared, which control various operations of the component:
TSpecialKind = (skAlpha, skBeta, skGamma);
TSpecialKinds = set of TSpecialKind;
In our descendant class, we want to add new behavior, which would require expanding the selection of enumerated types. Essentially, we want this:
TSpecialKind = (skAlpha, skBeta, skGamma, skDelta, skEpsilon);
TSpecialKinds = set of TSpecialKind;
Obviously, we want to avoid editing the third-party code. Is it valid to simply redeclare the enumerated type, repeating the original values and adding our new ones, in our own descendent unit? Will it have any effect on existing code?
Edit:
Example scenario to (hopefully) clarify. Say you've got a (parent) component for ordering vehicle parts. The parent unit has an enumerated type Tvkind for vehicle kind, with values vkCar and vkCycle defined. These values are used, among other things, to indicate how many wheels the vehicle has, 4 or 2.
Now, in your descendent component, you want to be able to handle 3-wheeled vehicles as well. Extending the Tvkind enumerated type to include a new value vkTrike seems like the obvious approach. But what if you don't have access to or don't want to modify the parent component code?
Inheritance for enumeration types doesn't work the same way it works for Classes because code makes assumptions about enumerations that it would never make about a class. For example, given your original enumeration (the TSpecialKind), the third party component likely includes code like this:
var Something: TSpecialKind;
[...]
case Something of
skAlpha: ;
skBeta: ;
skGamma: ;
end;
Even if you could cast something that's not part of that enumeration to the TSpecialKind type, the result of that code above would be undefined (and definitively not good!)
Enumerations might be used in one other way, and if the third party component only uses it that way, then you might be able to do some "wizardry", but I don't recommend it. If the original TSpecialKind is only used through it's TSpecialKinds set type, and then it's only used like this:
if skBeta in VarOfTypeSpecialKinds then
begin
...
end;
(continued) then you could introduce a new type that enumerates all of the original values, in the same order, with the same value. If after you do that SizeOf(TSpecialKind) equals SizeOf(TNewType) then you can hard-cast the new set value to the old value and the old code would work the same. But frankly this is hacky, to many conditions for it to work properly, too fragile. The better solution would be to use a new enumeration type that's only used in your descendant component:
type TExtraSpecialKind = (skDelta, skEpsilon);
TExtraSpecialKinds = set of TExtraSpecialKind;
You'll probably have this set published in a different property; The solution is clean, will mix well with the descendant code and can be used cleanly too. Example:
if (skAlpha in SpecialKind) or (skDelta in ExtraSpecialKind) then
begin
// Do extra-sepcial mixed stuff here.
end;
I don't believe that you can reasonably expect to make the change that you want without modifying the original component.
Let's take your vehicle kind example and delve a bit deeper. I expect that the original component will have code like this:
case Kind of
vkCar:
CarMethod;
vkCycle:
CycleMethod;
end;
Now, suppose you introduce an enumerated type with an extra enumeration
TExtendedVehicleKind = (vkCar, vkCycle, vkTrike);
If the case statement above runs, with ExtendedKind equal to vkTrike, no method will be called.
Now, perhaps the behaviour that you want from the original control can be achieved by setting Kind to vkCar or vkCycle when ExtendedKind is vkTrike. But that seems unlikely to me. Only you can know for sure, because only you have the code, and know what your actual problem is.
Been in the "need to extended the enumerated type of a property".
Quick First Suggestion. Add your enumeration, as a new property wrapper to the existing property:
Potential Parent class code:
unit AcmeMachines;
interface
type
FoodCanEnum =
(
None,
Fish,
Bird,
Beef
);
AcmeCanAutoOpenMachineClass= class (object)
protected
{ protected declarations }
F_FoodCanEnum: FoodCanEnum;
function getFoodEnumProperty: FoodCanEnum;
procedure setFoodEnumProperty(const AValue: FoodCanEnum);
public
{ public declarations }
property FoodEnumProperty
read getFoodEnumProperty write setFoodEnumProperty;
end;
implementation
function AcmeCanAutoOpenMachineClass.getMyFoodEnumProperty: FoodCanEnum;
begin
Result := F_FoodCanEnum;
end;
procedure AcmeCanAutoOpenMachineClass.setMyFoodEnumProperty
(const AValue: CatFoodCanEnum);
begin
FoodEnumProperty:= AValue;
// do some specific business logic
end;
end;
Descendant Class code:
unit UmbrellaMachines;
interface
uses AcmeMachines;
type
CatFoodCanEnum =
(
None, <--- matches "AcmeMachines.None"
Fish, <--- matches "AcmeMachines.Fish"
Bird, <--- matches "AcmeMachines.Bird"
Beef, <--- matches "AcmeMachines.Beef"
Tuna,
Chicken
);
UmbrellaCanAutoOpenMachineClass = class (AcmeCanAutoOpenMachineClass)
protected
{ protected declarations }
F_CatFoodCanEnum: CatFoodCanEnum;
function getMyFoodEnumProperty: CatFoodCanEnum;
procedure setMyFoodEnumProperty(const AValue: CatFoodCanEnum);
public
{ public declarations }
// new property, "wraps" existing property
property MyFoodEnumProperty
read getMyFoodEnumProperty write setMyFoodEnumProperty;
end;
implementation
function UmbrellaCanAutoOpenMachineClass.getMyFoodEnumProperty: CatFoodCanEnum;
begin
// wrap existing "FoodEnumProperty" property, using an existing value as dummy
Result := F_CatFoodCanEnum;
end;
procedure UmbrellaCanAutoOpenMachineClass.setMyFoodEnumProperty
(const AValue: CatFoodCanEnum);
begin
// wrap existing property, using an existing value as dummy
// may be another value if necessary
AcmeCanAutoOpenMachineClass.ExistingFoodEnumProperty := AcmeMachines.None;
F_CatFoodCanEnum := AValue;
// add extended business logic for this class instances
end;
end;
Extra.
If possible, always add a "null" or "dummy" value to your own enumerations, usually, the first value:
type
CatFoodCanEnum =
(
None, // <--- these one
Tuna,
Chicken,
Beef
);
Cheers.

How to Start Creating My Own Classes with Delphi?

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. :)

Resources