views:

255

answers:

2

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.]

+3  A: 

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 aTListand it worked. But then in your function you added aTIncompatibleObject` 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.

Mason Wheeler
I forgot to add I'm talking about TList<string>, not TStringList. Before D2010 I used TStringList, but I'm trying to move away from that to something with a more uniform API (i.e. TList). I will edit my question.
awmross
So I can't pass for example a TList<TStream> to my ListToString function. I see! Is there another way to write the function signature of ListToString so I *can* pass a `TList<TStream>` ?
awmross
@awmross: Well, you could give the function itself a generic type. `function ListToString<T>(list:TList<T>):String;` That would work. But then the compiler can't assume anything about the nature of T, so you can't treat it as an object. (Unless you put a class constraint or a specific-class constraint on T. But then you couldn't use strings.) Basically, generics are useful but they're not the final answer to everything.
Mason Wheeler
A foolish consistency, if ever I saw one. awmross, you are seriously going about this all wrong.
Warren P
A: 

It's a fundamentally BAD idea to use a TList<-> of type string. Use TStringList.

It's not cleaner to say "I use templates everywhere". In fact, if you want to be consistent, use templates NOWHERE. Avoid them, like most delphi developers avoid them, like the plague.

All "lists" of strings in the VCL are of type TStringList. Most collections of objects in most delphi apps use TObjectList, instead of templated types.

It is not cleaner and more consistent to be LESS consistent with the entire Delphi platform, and to pick on some odd thing, and standardize on it, when it will be you, and you alone, doing this oddball thing.

In fact, I'm still not sure that templates are safe to use.

Warren P
Interoperability with existing libraries is certainly something to be considered. The inconsistency with TStringList lies in the fact that if you want a List of something other than a String, you have to use something completely incompatible with TStringList (e.g a TObjectList).
awmross
You're doing it wrong. Delphi string types are fundamental types. It sounds like you want them to be objects. But they are not. Neither are integers a kind of object. Learn to live with that.
Warren P