Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi : Handling the fact that Strings are not Objects

I am trying to write a function that takes any TList and returns a String representation of all the elements of the TList.

I tried a function like so

function ListToString(list:TList<TObject>):String;

This works fine, except you can't pass a TList<String> to it.

E2010 Incompatible types: 'TList<System.TObject>' and 'TList<System.string>'

In Delphi, a String is not an Object. To solve this, I've written a second function:

function StringListToString(list:TList<string>):String;

Is this the only solution? Are there other ways to treat a String as more 'object-like'?

In a similar vein, I also wanted to write an 'equals' function to compare two TLists. Again I run into the same problem

function AreListsEqual(list1:TList<TObject>; list2:TList<TObject>):boolean;

Is there any way to write this function (perhaps using generics?) so it can also handle a TList<String>? Are there any other tricks or 'best practises' I should know about when trying to create code that handles both Strings and Objects? Or do I just create two versions of every function? Can generics help?

I am from a Java background but now work in Delphi. It seems they are lately adding a lot of things to Delphi from the Java world (or perhaps the C# world, which copied them from Java). Like adding equals() and hashcode() to TObject, and creating a generic Collections framework etc. I'm wondering if these additions are very practical if you can't use Strings with them.

[edit: Someone mentioned TStringList. I've used that up till now, but I'm asking about TList. I'm trying to work out if using TList for everything (including Strings) is a cleaner way to go.]

like image 239
awmross Avatar asked May 26 '10 02:05

awmross


2 Answers

Your problem isn't that string and TObject are incompatible types, (though they are,) it's that TList<x> and TList<y> are incompatible types, even if x and y themselves are not. The reasons why are complicated, but basically, it goes like this.

Imagine your function accepted a TList<TObject>, and you passed in a TList<TMyObject> and it worked. But then in your function you added a TIncompatibleObject to the list. Since the function signature only knows it's working with a list of TObjects, then that works, and suddenly you've violated an invariant, and when you try to enumerate over that list and use the TMyObject instances inside, something's likely to explode.

If the Delphi team added support for covariance and contravariance on generic types then you'd be able to do something like this safely, but unfortunately they haven't gotten around to it yet. Hopefully we'll see it soon.

But to get back to your original question, if you want to compare a list of strings, there's no need to use generics; Delphi has a specific list-of-strings class called TStringList, found in the Classes unit, which you can use. It has a lot of built-in functionality for string handling, including three ways to concatenate all the strings into a single string: the Text, CommaText and DelimitedText properties.

like image 81
Mason Wheeler Avatar answered Nov 14 '22 01:11

Mason Wheeler


Although it is far from optimal, you can create string wrapper class, possibly containing some additional useful functions which operate on strings. Here is example class, which should be possibly enhanced to make the memory management easier, for example by using these methods.

I am only suggesting a solution to your problem, I don't agree that consistency for the sake of consistency will make the code better. If you need it, Delphi object pascal might not be the language of choice.

like image 1
too Avatar answered Nov 14 '22 00:11

too