Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast a generic type into an actual type in Delphi

Consider the following code

procedure TMyClass.SetParam<T>(Name: string; Value: T);
begin
  if (TypeInfo(T) = TypeInfo(string)) then
  begin
    FHashTable.AddString(Name, (Value as string));
  end
  else if (TypeInfo(T) = TypeInfo(Integer)) then
  begin
    FHashTable.AddInteger(Name, (Value as Integer));
  end
......

I want to have a generic procedure that gets a generic value of type T and according to the actual type of T insert the value to an hashtable.

The compiler won't let me do this cast, also won't let me do something like Integer(Value).

Can someone please explain how should i implement the above?

like image 702
itay312 Avatar asked Dec 23 '22 22:12

itay312


2 Answers

Try something like this:

procedure TMyClass.SetParam<T>(Name: string; Value: T);
begin
  if (TypeInfo(T) = TypeInfo(string)) then
  begin
    FHashTable.AddString(Name, PString(@Value)^);
  end
  else if (TypeInfo(T) = TypeInfo(Integer)) then
  begin
    FHashTable.AddInteger(Name, PInteger(@Value)^);
  end
......

Or this:

uses
  System.Rtti;

procedure TMyClass.SetParam<T>(Name: string; Value: T);
var
  LValue: TValue;
begin
  LValue := TValue.From<T>(Value);
  if (TypeInfo(T) = TypeInfo(string)) then
  begin
    FHashTable.AddString(Name, LValue.AsString);
  end
  else if (TypeInfo(T) = TypeInfo(Integer)) then
  begin
    FHashTable.AddInteger(Name, LValue.AsInteger);
  end
......
like image 54
Remy Lebeau Avatar answered Jan 17 '23 18:01

Remy Lebeau


Although you can do this type of thing easily with classes, it is not so easy with other types, like integers, strings and enums. Although they work with generics to an extent, they are not great. On the other hand, in this case you don't need to.

Because generics are so useful there is a great temptation to rush into generics when they are not really needed (I know I have fallen into that trap more than once). All you need here is overloaded functions as is shown below.

unit UnitTest1;

interface

type
  THashTable = class
    procedure AddString( const pName : string; pValue : string ); virtual; abstract;  // dummy for illustration only
    procedure AddInt( const pName : string; const pInt : integer ); virtual; abstract;  // dummy for illustration only
  end;

  TMyClass = class
  private
    FHashTable : THashTable;
  public
    procedure TestString;
    procedure TestInt;

    procedure SetParam( const pName : string; const pValue : string ); overload;
    procedure SetParam( const pName : string; const pValue : integer ); overload;

  end;

implementation

{ TMyClass }

procedure TMyClass.SetParam(const pName, pValue: string);
begin
  FHashTable.AddString( pName, pValue );
end;

procedure TMyClass.SetParam(const pName: string; const pValue: integer);
begin
  FHashTable.AddInt( pName, pValue );
end;

procedure TMyClass.TestInt;
begin
  SetParam( 'Int', 4 );
end;

procedure TMyClass.TestString;
begin
  SetParam( 'Int', 'Fred' );
end;

end.

I have created a dummy class THashTable for the purposes of illustration only and I have not created FHashTable. This is simply to illustrate the principles. I know the code will not run as it stands, but it will compile.

like image 37
Dsm Avatar answered Jan 17 '23 18:01

Dsm