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.]
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.
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.
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.
精彩评论