Can I create an object of the same type as itself? - delphi

I have a class of mine, lets call it TMyObject, which should return a slightly modified copy of itself.
So, one of its functions should return an object of the same type as itself:
function TMyObject.TrimEnds: TMyObject;
begin
Result:= TMyObject.Create;
Result.DoStuff;
edn;
Can I do that? Is it legit what am I doing?
I mean, I already tried it and the compiler allows me to do it, but I wonder if there will be long time/hidden negative effects.
Any thoughts will be appreciated.
Thanks.
Edit:
The new slightly modified copy will be saved to disk. It is some kind of 'Save as...'.
How it works: The original object creates a copy of itself, instructs this copy to do some changes and to save to disk. Then the original frees the copy. This way I keep the original object in memory unchanged but I have a modified version of this to disk.
You may think that my object holds a picture. What I need is a function that returns a slightly modified copy of the picture.

but I wonder if there will be long
time/hidden negative effects.
I don't see any, and I used to do this with my own linked lists, and never had any problem. I think it is pretty much the same as creating an instance in any other place.

I guess you are right, this piece of code seems wrong to me.
You always have to make it clear who's responsible for freeing the object you return.
In most cases, this object will not be freed.
A better approach is to usually let the caller create the object, pass it to your method (you only need a procedure then) which modifies it.
I am curious to know why you would want to return a "slightly modified version" of your object. It sounds counter-intuitive to me...

As others had said, there's nothing wrong with that but there may be better ways.
Variant 1: Change this into class method and give it a meaningful name.
class function TMyObject.CreateSpecialized: TMyObject;
begin
Result := TMyObject.Create;
//initialize Result
end;
anObj := TMyObject.CreateSpecialized;
Variant 2: Use a constructor. You can have multiple constructors in a class.
constructor TMyObject.CreateSpecialized;
begin
Create; // make sure everything is initialized correctly
// now do custom initialization
end;
anObj := TMyObject.CreateSpecialized;
Usage is same in both examples but in second case your intentions are clearer to a random reader.
If you want to take one object and create another one based on first object's fields, use a constructor with parameter.
constructor TMyObject.CreateSpecialized(obj: TMyObject);
begin
Create;
intField := obj.IntField * 2;
end;
anObj := TMyObject.CreateSpecialized(otherObj);

If you have a new class derived from TMyObject like TMyOtherObject = class(TMyObject), the TrimEnds function will still return a TMyObject instead of a TMyOtherObject as one might expect.
You can fix this by using this scheme:
TMyObjectClass = class of TMyObject;
function TMyObject.TrimEnds: TMyObject;
begin
Result:= TMyObjectClass(ClassType).Create;
Result.DoStuff;
end;

there is absolutely nothing wrong with what you wrote. you are simply returning an object and this is perfectly valid, it could be any other object of any other type.

Related

What is the correct code to override Assign method?

I'm confused by the Assign code in many other threads, for example, this one:
See the Assign method implementation given in an answer.
procedure TDispPitch.Assign(Source: TPersistent);
var
LSource: TDispPitch;
begin
if Source is TDispPitch then
begin
LSource := TDispPitch(Source);
iLineSize := LSource.LineSize;
iLineColor := LSource.LineColor;
bDisplayAccent := LSource.DisplayAccent;
bVisible := LSource.Visible;
Changed;
end else
inherited;
end;
But if the call to inherited is in the Else part, how are the base class properties assigned for an object that satisfies the if condition? Compare this to the VCL's own code for TStringList.
procedure TStringList.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TStringList then
begin
FCaseSensitive := TStringList(Source).FCaseSensitive;
...
end;
end;
This correctly calls Inherited first so that base class properties are assigned first.
So is the first code block right? I find such a code at many places on stack overflow. I can't understand how the base class properties are assigned in that code. Can someone explain? On the other hand, if the code is wrong, why hasn't someone pointed it out in all those threads?
Finally I got around to solving this. Actually, when I went to implement a Sort on the TCollection, it didn't work and then finally I was able to see what was wrong and how it should work. I had to look at the sources in my old Delphi XE4 version to confirm my finding.
Here are the important points to consider when writing the overridden Assign code:
The Assign in TPersistent does nothing. In fact, it catches the error if a derived class didn't implement the Assign override at the time it is needed. It shows a message that says something like "Can not assign."
So if you have derived a class straight from TPersistent, you need the code like the first example at the top. The call to Inherited doesn't really achieve anything because the real work of assigning is done by the upper If block. My collection item had the code in the second example even though it was deriving from TCollectionItem which is a TPersistent class with no Assign logic of its own. Hence, the call to Inherited which occurred first was immediately causing an exception "Can not assign" as soon as the sort tried to exchange items.
Taking this further, if your class is derived from an intermediate class based on TPersistent, you need to know if it has its own Assign logic. If yes, you must use the second example at the top so that the inherited Assign logic gets a chance to do its magic first. In my case, I had a class derived from TOwnedCollection/TCollection with its own Assign logic that copies the Items from one collection to another. So I had to use the second example at the top.
Let's try to summarize what we learned from the point of view of using TCollectionItem and TCollection/TOwnedCollection:
Assign of the class that has the ancestor TOwnedCollection/TCollection: Use the second example code block at the top so that the collection can do its assigns before yours.
Assign of the class that has the ancestor TCollectionItem/TPersistent: Use the first example code block at the top because both of these do not have their own Assigns. However, if you are deriving from another class based on these and it has its own Assign logic, you must use the second example so that its Assign executes first by the call to Inherited.

Creating and destroying objects in Delphi

I am new to Delphi and am trying to better understand object creation / freeing as I am used to the luxury of .NET's GC. I have two questions specifically:
Let's assume I am setting a TDataSource as below. In .NET I wouldn't explicitly destroy the object as I am with adoQuery.Free. But I am assuming that with Delphi I need to free these objects. However, by destroying the adoQuery I am also setting the dataset to null. In this way adoQuery is meant to be a locally scoped variable to the function only with the datsource being retuned from the function. Therefore, how can I best handle this?
dataSrc := TDataSource.Create(nil);
dataSrc.DataSet := adoQuery;
dataSrc.Enabled := true;
{ adoQuery.Free; }
cnt := DataSrc.DataSet.RecordCount;
I've been reading several suggestions when returning a variable from a function that the best thing to do is create the variable within the caller and pass it to the subroutine. Therefore, the signature to a function would look like:
AdoConnectionManager.GetResult(query : String; dataSrc: TDataSource) : TDataSource;
Result := dataSrc;
This is unattractive to me. I'd prefer to have a new variable created within the subroutine and then returned back to the caller. However, this is something again I never really had to worry about with .NET GC and here I have to explicitly destroy the variable, right?
Thanks!
You've asked two questions. One concerns these database classes, and I'm going to ignore that question since I don't know anything about those classes. Instead I will answer the other questions. Do note that this sort of answer is why the site policy is for questions to be asked one at a time.
Regarding a function that returns a new object, that is certainly viable. However, it is sometimes more flexible to let the caller supply the object. That allows them to re-use instance, or supply objects that are derived from a base class. A classic example would be a function that populated a TStrings instance.
In this scenario you'd probably use a procedure rather than a function. It might look like this:
procedure PopulateList(List: TMyList);
If you want to have a function that returns a newly minted instance that would be done like so:
function CreateAndPopulateList: TMyList;
begin
Result := TMyList.Create;
try
// code to populate Result goes here, and may raise exceptions
except
Result.Free; // in case of exceptions, we must destroy the instance to avoid leaks
raise;
end;
end;
Note the naming. I use create to imply to the caller that a new instance is created. The calling code would look like this:
List := CreateAndPopulateList;
try
// do stuff with list
finally
List.Free;
end;
And this pattern is the standard object creation pattern. So you use CreateAndPopulateList just as you could a constructor.
It should also be mentioned here, that Delphi also provides Reference-Counting (but different to .NET).
The very short explanation to Reference-Counting in Delphi:
In difference to other Languagues, Reference-Counting in Delphi is only available by using Interfaces. Further, there is no Garbage-Collector: a reference-counted Object gets instantly destroyed when its Referencecount reaches 0.
So as an Delphi Developer, there are the following "global" Rules for destroying Instances:
- you do destroy an Object manually, whenever it's declared as a ClassType (e.g. var m: TMyClass)
- you never destroy an Object manually, whenever it's declared as a InterfaceType (e.g. var m: IMyClass)
With Delphi when you create an object, you should decide how it would be freed. There are several ways:
You can free it manually
It can be freed together with it's owner
It can be freed as a part of TObjectList or similar container
It can be freed because it's interfaced and reference counter for it became zero
And so on...
About first question: you should understand that with Delphi object variable is a pointer to object. When leaving function, you can lost locally scoped (pointer) variable, but you don't harm object itself. For example, you can do something like this:
function GetDataSource: TDataSource;
var Query: TADOQuery;
begin
Result := TDataSource.Create(nil);
Query := TADOQuery.Create(Result);
Query.SQL.Text := ' ... ';
Result.DataSet := Query;
end;
It'll give you datasource you want with background query. When you free this datasource, query also would be freed.
About second question: intrafunction creation of return object is a normal practise, part of good design. Yes, you should decide who will free this object and how. You can use many strategies, there are no silver bullet here. Just for example, you can decide to add parameter 'datasource's owner' to function above and controls it's lifetime this way.

Is there a generics equivalent of a sorted TStringList for storing objects?

I want to store objects sorted by a name in a list. Duplicates should be ignored and when added the object should automatically be freed. In pre-generics times I would have used a TStringList like this:
List := TStringList.Create;
List.Sorted := true;
List.Duplicates := dupIgnore;
List.AddObject('bla', TSomeObject.Create({parameters go here});
List.AddObject('blub', TSomeObject.Create({parameters go here});
List.AddObject('bla', TSomeObject.Create({parameters go here});
(Inserting 'bla' twice creates a memory leak, I know, that's one problem I would like to solve here.)
Since Delphi nowadays comes with multiple generic containers, I think there should be one that makes the above cleaner.
The first one I came up with was TDictionary:
type
TSomeObjectContainer = TDictionary<string, TSomeObject>
// etc.
Unfortunately TSomeObjectContainer does not allow me to just ignore duplicates, so I have to implement that check myself.
Am I missing something?
If I see what you are looking for, using TDictionary<>.AddOrSetValue() can help with the duplicates. Also the TObjectDictionary class seems to give you a helping hand with memory management.
An example (resulting in no leaks in my tests):
Dict := TObjectDictionary<string, TObject>.Create([doOwnsValues]);
try
Dict.AddOrSetValue('c', TObject.Create);
Dict.AddOrSetValue('c', TObject.Create);
Dict.AddOrSetValue('d', TObject.Create);
finally
Dict.Free;
end;
You could do something like this:
TSomeObjectContainer = TDictionary<string, TSomeObject>;
procedure AddAndFreeDuplicates(aKey: string; aValue: TSomeObject);
end;
procedure TSomeObjectContainer.AddAndFreeDuplicates(aKey: string; aValue: TSomeObject);
begin
if Self.ContainsKey(aKey) then
aValue.Free
else
Self.Add(aKey, aValue);
end;
Then to use it you would do something like this:
var
MyCont: TSomeObjectContainer;
begin
MyCont := TSomeObjectContainer.Create;
MyCont.AddAndFreeDuplicates('bla', TSomeObject.Create({parameters go here}));
MyCont.AddAndFreeDuplicates('blub', TSomeObject.Create({parameters go here}));
MyCont.AddAndFreeDuplicates('bla', TSomeObject.Create({parameters go here}));
...
MyCont.Free;
end;
It depends on how often you add and how many items you have in the list as to whether you should create a list and keep it sorted or not. Ultimately you are going to have to check the duplicates at the point of adding. The question is just how you do that check.
There isn't a generic TStringList, unfortunately. Probably your best bet for a case like this is to build on your original idea: Create a class that inherits from TDictionary<string, TSomeObject> and put in your own Add method that checks for duplicates and handles them appropriately, or calls inherited if there isn't one.
You are not missing anything. The current set of Generic containers does not have the functionality you are looking for. There is no Generic container that does everything TStringList does, so you are going to have to implement some functionality yourself. If you use TDictionary, you will have to implement duplicate checks yourself. If you use TList<T> or TObjectList<T>, you will have to implement your own sorting and duplicate checks.

Why is using procedures to create objects preferred over functions?

This is similar to this question. I asked "Why?" to the most popular response but I don't know that anyone would ever look at it again. At least not in any timely manner.
Anyway, my question is about best practices for delegating responsibility for creation of objects to functions or procedures, without causing memory leaks. It seems that this:
procedure FillObject(MyObject: TMyObject; SomeParam: Integer);
begin
//Database operations to fill object
end;
procedure CallUsingProcedure();
var
MyObject: TMyObject;
begin
MyObject = TMyObject.Create();
try
FillObject(MyObject, 1);
//use object
finally
MyObject.Free();
end;
end;
is preferred over this:
function CreateMyObject(DBID: Integer): TMyObject;
begin
Result := TMyObject.Create();
try
//Database operations to fill object
except on E: Exception do
begin
Result.Free();
raise;
end;
end;
end;
procedure CallUsingFunction();
var
MyObject: TMyObject;
begin
MyObject = CreateMyObject(1);
try
//use object
finally
MyObject.Free();
end;
end;
Why?
I'm relatively new to Delphi, having previously worked most with Java and PHP, as well as C++, though to a lesser extent. Intuitively, I lean toward the function method because:
It encapsulates the object creation code in the function, rather than create the object separately whenever I want to use the procedure.
I dislike methods that alter their parameters. It's often left undocumented and can make tracing bugs more difficult.
Vague, but admittedly it just "smells" bad to me.
I'm not saying I'm right. I just want to understand why the community chooses this method and if there is good reason for me to change.
Edit:
References to #E-Rock in comments are to me(Eric G). I changed my display name.
One problem is what Ken White wrote: you hand the user of the function an object he or she must free.
Another advantage of procedures is that you can pass several objects of a hierarchy, while a function that creates such an object always generates the same. E.g.
procedure PopulateStrings(Strings: TStrings);
To that procedure, you can pass any kind of TStrings, be it the Lines of a TMemo, the Items of a TListBox or TComboBox or a simple standalone TStringList. If you have a function:
function CreateStrings: TStrings;
You always get the same kind of object back (which object exactly is not known, as TStrings is abstract, so you probably get a TStringList), and must Assign() the contents to the TStrings you want to modify. The procedure is to be preferred, IMO.
Additionally, if you are the author of the function, you can't control whether the object you create is freed, or when. If you write a procedure, that problem is taken off your hands, since the user provides the object, and its lifetime is none of your concern. And you don't have to know the exact type of the object, it must just be of the class or a descendant of the parameter. IOW, it is also much better for the author of the function.
It is IMO seldom a good idea to return an object from a function, for all the reasons given. A procedure that only modifies the object has no dependency on the object and creates no dependency for the user.
FWIW, Another problem is if you do that from a DLL. The object returned uses the memory manager of the DLL, and also the VMT to which it points is in the DLL. That means that code that uses as or is in the user code does not work properly (since is and as use the VMT pointer to check for class identity). If the user must pass an object of his, to a procedure, that problem does not arise.
Update
As others commented, passing an object to a DLL is not a good idea either. Non-virtual functions will call the functions inside the DLL and use its memory manager, which can cause troubles too. And is and as will not work properly inside the DLL either. So simply don't pass objects into or out of a DLL. That goes with the maxime that DLLs should only use POD type parameters (or compound types -- arrays, records -- that only contain POD types) or COM interfaces. The COM interfaces should also only use the same kind of parameters.
Creating the object instance and passing it into another procedure makes it clear which code is responsible for freeing the instance.
In the first case (using a procedure to fill it):
MyObj := TMyObject.Create;
try
// Do whatever with MyObj
finally
MyObj.Free;
end;
This is clear that this block of code is responsible for freeing MyObj when it's finished being used.
MyObj := CreateMyObject(DBID);
What code is supposed to free it? When can you safely free it? Who is responsible for exception handling? How do you know (as a user of someone else's code)?
As a general rule, you should create, use, and free object instances where they're needed. This makes your code easier to maintain, and definitely makes it easier for someone who comes along later and has to try and figure it out. :)
I use a combination of both idioms. Pass the object as an optional parameter and if not passed, create the object. And in either case return the object as the function result.
This technique has (1) the flexibility of the creation of the object inside of the called function, and (2) the caller control of the caller passing the object as a parameter. Control in two meanings: control in the real type of the object being used, and control about the moment when to free the object.
This simple piece of code exemplifies this idiom.
function MakeList(aList:TStrings = nil):TStrings;
var s:TStrings;
begin
s:=aList;
if s=nil then
s:=TSTringList.Create;
s.Add('Adam');
s.Add('Eva');
result:=s;
end;
And here are three different ways to use it
simplest usage, for quick and dirty code
var sl1,sl2,sl3:TStrings;
sl1:=MakeList;
when programmer wants to make more explicit ownership and/or use a custom type
sl2:=MakeList(TMyStringsList.create);
when the object is previously created
sl3:=TMyStringList.Create;
....
MakeList(sl3);

Correct way to duplicate Delphi object

What are pros and cons of duplication an object instance with constructor or instance function?
Example A:
type
TMyObject = class
strict private
FField: integer;
public
constructor Create(srcObj: TMyObject); overload;
//alternatively:
//constructor CreateFrom(srcObj: TMyObject);
property Field: integer read FField;
end;
constructor TMyObject.Create(srcObj: TMyObject);
begin
inherited Create;
FField := srcObj.Field;
end;
Example B:
type
TMyObject = class
strict private
FField: integer;
public
function Clone: TMyObject;
property Field: integer read FField;
end;
function TMyObject.Clone: TMyObject;
begin
Result := TMyObject.Create;
Result.FField := FField;
end;
One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.
Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)
EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.
newObj := TMyObject.Create(oldObj)
vs.
newObj := oldObj.Clone;
EDIT2 or "Why I want single-line operation"
I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.
I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.
IOW, I prefer to write
Send(TMyObject.Create(obj));
or
Send(obj.Clone);
to
newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class
The Delphi way (TPersistent) separates creation and cloning:
dest := TSomeClass.Create;
dest.Assign(source);
and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.
edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)
type
TBaseSomeObject = class;
TBaseObjectClass = class of TBaseSomeObject;
TBaseSomeObject = class(TPersistent)
function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
end;
...
function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
begin
if Assigned(t) then
Result := t.Create
else
Result := TBaseObjectClass(Self.ClassType).Create;
Result.Assign(Self);
end;
SendObject(obj.Clone); // full clone.
SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object
For the rest, just implement your assign() operators, and you can mix multiple ways.
edit2
I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.
I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)
The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
The clone way is short although you need to provide a call for each class.
The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.
And if you implement Assign using streams, you have only one place to worry about which fields need to be available.
I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.
I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.

Resources