cheechaway
cheechaway

Reputation: 111

Delphi Generic List to string

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

Answers (1)

David Heffernan
David Heffernan

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

Related Questions