Reputation: 111
I am looking at a generic list which can be written to an Array of string for any object based on a particular field. The base List will have a number of descendants. The Code for the declaration is shown below and compiles fine:
unit TestList;
interface
uses
System.Classes, System.Rtti, System.Generics.Collections, system.SysUtils;
type
TMakeString<T> = reference to function(const input: T): String;
TMyList<T>=class(TList<T>)
protected
function GenericAsString(const Value): string;
Function CastToString(const Value: T): string; virtual;
public
Function MakeArray:TArray<string>;
end;
TMyObject=class(TObject)
private
fname: string;
public
property Name:string read fname write FName;
end;
TMyObjectList<T:TMyObject>=class(TMyList<T>)
protected
Function CastToString(const Value: T): string; override;
end;
implementation
{ TMyList<T> }
function TMyList<T>.CastToString(const Value: T): string;
begin
result:='';
if TypeInfo(T) = TypeInfo(string) then
Result := GenericAsString(Value);
end;
function TMyList<T>.GenericAsString(const Value): string;
begin
Result := String(Value);
end;
function TMyList<T>.MakeArray: TArray<string>;
var
i:integer;
begin
setlength(Result,0);
for i := 0 to count-1 do
Result[i]:=CastToString(Items[i]);
end;
{ TMyObjectList<T> }
function TMyObjectList<T>.CastToString(const Value: T): string;
begin
result:=TMyObject(T).Name;
end;
end.
When I implement this code using the following:
var
MyList:TMyList<string>;
AList1:Tarray<string>;
begin
MyList:=TMyList<string>.create;
MyList.Add('Name1');
MyList.Add('Name1');
Alist1:=MyList.MakeArray;
end;
I keep getting an AV during the MakeArray call when it calls the CastToString function and tries to return a result. The same happens when I invoke the TMYObjectList at the same point.
I am not sure if my use of generics here is effective or appropriate to this situation, and if not is there a better way to do this? I was toying with using an anonymous method approach as well but wasn't sure how to go about this.
Upvotes: 2
Views: 3875
Reputation: 612794
Your first problem lies here:
function TMyList<T>.MakeArray: TArray<string>;
var
i:integer;
begin
setlength(Result,0); // <--- oops
for i := 0 to count-1 do
Result[i]:=CastToString(Items[i]);
end;
That should be
SetLength(Result, Count);
Then consider this function:
function TMyList<T>.GenericAsString(const Value): string;
begin
Result := String(Value);
end;
You cannot just cast an arbitrary variable to a string and hope that it will work. It so happens that in your code you only call this function when Value
is a string, but that's not a good approach. You might be better removing GenericAsString
and writing CastToString
as:
function TMyList<T>.CastToString(const Value: T): string;
begin
Result := TValue.From<T>(Value).ToString;
end;
However, even that has dubious utility in my view.
This code is also wrong:
function TMyObjectList<T>.CastToString(const Value: T): string;
begin
result := TMyObject(T).Name;
end;
Remember that T
is a type. You are casting a type to an instance. That always fails.
You would write this like so:
function TMyObjectList<T>.CastToString(const Value: T): string;
begin
result := Value.Name;
end;
It's very hard to advise you on how to proceed. I don't think that it makes sense for the list class to implement the capability of obtaining a string representation of one of its members. But exactly where and how that functionality should be implemented very much depends on your overall requirements. Which we simply cannot see.
Upvotes: 8