Domanda

Dato che la mia domanda da ieri forse non era completamente chiara e non ho ricevuto la risposta Volevo, cercherò di formularlo in un modo più generale:

Esiste un modo per implementare un comportamento speciale basato sul tipo effettivo di un tipo generico istanziato utilizzando istruzioni condizionali esplicite o utilizzando un qualche tipo di specializzazione? Pseudocodice:

TGenericType <T> = class
  function Func : Integer;
end;
...
function TGenericType <T>.Func : Integer;
begin
  if (T = String) then Exit (0);
  if (T is class) then Exit (1);
end;
...
function TGenericType <T : class>.Func : Integer;
begin
Result := 1;
end;
function TGenericType <String>.Func : Integer;
begin
Result := 0;
end;
È stato utile?

Soluzione

Puoi tornare a RTTI, usando TypeInfo (T) = TypeInfo (stringa) . Per verificare se qualcosa è una classe, puoi usare qualcosa come PTypeInfo (TypeInfo (T)) ^. Kind = tkClass .

Il tipo di PTypeInfo e il tkClass sono definiti nell'unità TypInfo .

Altri suggerimenti

Se qualcuno è interessato a come ho implementato la mia dimensione "nel peggiore dei casi con un trattamento speciale per le stringhe"

class function RTTIUtils.GetDeepSize <T> (Variable : T) : Integer;
var
  StringLength          : Integer;
  Ptr                   : PInteger;
begin
if (TypeInfo (T) = TypeInfo (String)) then
  begin
  Ptr := @Variable;
  Ptr := PInteger (Ptr^);
  Dec (Ptr);
  StringLength := Ptr^;
  Result := StringLength * SizeOf (Char) + 12;
  end
else
  Result := 0;
end;

Per me, questo fa il lavoro a portata di mano. Grazie a tutti i collaboratori!

in C #, puoi fare un typeof (T) che ti permetterebbe di fare qualcosa del genere

(T = String)

o

(T is class)

Non ho mai visto l'altra tua domanda (non l'hai collegata ad essa), ma cosa stai cercando davvero ? In generale, fare qualcosa in base al tipo o al codice tipo tramite ifs come si sta facendo o un interruttore è generalmente meglio trasformato in avere un'interfaccia o una funzione astratta da qualche parte che viene personalizzata dal contesto.

TypeInfo (T) è la strada giusta. Inoltre puoi usare tutto ciò che è presente nell'unità TypInfo come il record TTypeData per determinare alcune proprietà specifiche di un tipo che usi invece di quelle generiche. Quando si determina il tipo corrente utilizzato anziché T, è possibile utilizzare il trucco del puntatore per ottenere il valore di una variabile.

Ecco un codice di esempio che accetta qualsiasi tipo di enumerazione come generico. Nota che funzionerà solo per le normali enumerazioni (senza valori fissi come

TEnumWontWork = (primo = 1, secondo, terzo)

) e l'enum non deve essere dichiarato come tipo locale all'interno di una procedura. In questi casi il compilatore non genera TypeInfo per gli enum.

type
  // Sample generic class that accepts any enumeration type as T
  TEnumArr<T> = class
  strict private
    fArr: array of Byte;
    fIdxType: TOrdType;
    function IdxToInt(idx: T): Int64;
    procedure Put(idx: T; Val: Byte);
    function Get(idx: T): Byte;
  public
    constructor Create;
    property Items[Index: T]: Byte read Get write Put; default;
  end;

constructor TEnumArr<T>.Create;
var
  pti: PTypeInfo;
  ptd: PTypeData;
begin
  pti := TypeInfo(T);
  if pti = nil then
    Error('no type info');
  // Perform run-time type check
  if pti^.Kind <> tkEnumeration then
    Error('not an enum');
  // Reach for TTypeData record that goes right after TTypeInfo record
  // Note that SizeOf(pti.Name) won't work here
  ptd := PTypeData(PByte(pti) + SizeOf(pti.Kind) + (Length(pti.Name)+1)*SizeOf(AnsiChar));
  // Init internal array with the max value of enumeration
  SetLength(fArr, ptd.MaxValue);
  // Save ordinal type of the enum
  fIdxType := ptd.OrdType;
end;

// Converts index given as enumeration item to integer.
// We can't just typecast here like Int64(idx) because of compiler restrictions so
//  use pointer tricks. We also check for the ordinal type of idx as it may vary
//  depending on compiler options and number of items in enumeration.
function TEnumArr<T>.IdxToInt(idx: T): Int64;
var
  p: Pointer;
begin
  p := @idx;

  case fIdxType of
    otSByte: Result := PShortInt(p)^;
    otUByte: Result := PByte(p)^;
    otSWord: Result := PSmallInt(p)^;
    otUWord: Result := PWord(p)^;
    otSLong: Result := PLongInt(p)^;
    otULong: Result := PLongWord(p)^;
  end;
end;

function TEnumArr<T>.Get(idx: T): Byte;
begin
  Result := fArr[IdxToInt(idx)];
end;

procedure TEnumArr<T>.Put(idx: T; Val: Byte);
begin
  fArr[IdxToInt(idx)] := Val;
end;

Esempio di utilizzo:

type
  TEnum  = (enOne, enTwo, enThree);
var
  tst: TEnumArr<TEnum>;
begin
  tst := TEnumArr<TEnum>.Create;
  tst[enTwo] := $FF;
  Log(tst[enTwo]);

Come riassunto, ho usato tre trucchi qui:

1) Ottenere TypeInfo per T con oggetti di scena generali di T

2) Ottenere TypeData per T con oggetti di scena dettagliati di T

3) Utilizzo del puntatore magico per ottenere il valore dei parametri forniti di tipo T.

Spero che questo aiuto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top