6

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

awmross
  • 3,789
  • 3
  • 38
  • 51
  • 5
    I wouldn't really put much effort into supporting `TList`; nobody uses that because [it's inferior to `TStringList`](http://stackoverflow.com/questions/279471/tstringlist-vs-tliststring). Focus your efforts elsewhere. – Rob Kennedy May 26 '10 at 06:04
  • How would a stringrepresentation of TList look? – Pieter B Apr 25 '13 at 09:26

3 Answers3

7

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.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • 1
    I forgot to add I'm talking about TList, 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 May 26 '10 at 03:40
  • So I can't pass for example a TList to my ListToString function. I see! Is there another way to write the function signature of ListToString so I *can* pass a `TList` ? – awmross May 26 '10 at 04:21
  • 2
    @awmross: Well, you could give the function itself a generic type. `function ListToString(list:TList):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 May 26 '10 at 04:32
  • 1
    A foolish consistency, if ever I saw one. awmross, you are seriously going about this all wrong. – Warren P Jun 14 '10 at 17:53
  • Agreed with Mason, TStringList is lightyears more flexible than any generic TList in regarding strings. And is much more likely you'd need to use ListToString(Memo1.Lines) than ListToString(Lista) where Lista is TList... – Fabricio Araujo May 24 '11 at 18:12
1

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.

Community
  • 1
  • 1
too
  • 3,009
  • 4
  • 37
  • 51
0

It's not cleaner. It's worse. It's a fundamentally BAD idea to use a TList<String> instead of TStringList.

It's not cleaner to say "I use generics everywhere". In fact, if you want to be consistent, use them 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 generics are safe to use heavily.

If you start using TList you won't be able to copy it cleanly to your Memo.Lines which is a TStringList, and you will have created a type incompatibility, for nothing, plus you will have lost the extra functionality in TStringList. And instead of using TStringList.Text you have to invent that for yourself. You also lose LoadFromFile and SaveToFile, and lots more. Arrays of strings are an ubiquitous thing in Delphi, and they are almost always a TStringList. TList<String> is lame.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • 1
    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 Jun 01 '10 at 01:51
  • 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 Jun 14 '10 at 17:54
  • Rob Kennedy seems to agree with me, that the OP is seeking a foolish consistency, and ignoring the ubiquitous and desirable idea of using TStringLIst, wherever appropriate. – Warren P Mar 25 '11 at 01:54
  • Awmross has the right idea; they want to write proper code. The language already has multiple string types; now there's a TStringList and a regular TList AND a generic TList. As is, TStringList contains a poor man's quasi-implementation of a dictionary/hashmap - not because that's a good idea, just because Delphi didn't have a dictionary/hashmap at the time. TStringList also became a way to split strings on a multi-character substring for a time before Delphi got a string helper function to do that. It's a Frankenstein's Monster of a class that implements multiple kludges to fix deficits. – alcalde Aug 05 '15 at 04:07