Pregunta

Debido a que mi pregunta de ayer quizás no fue del todo clara y no obtuve la respuesta Quería, intentaré formularlo de una manera más general:

¿Existe una manera de implementar un comportamiento especial basado en el tipo real de un tipo genérico instanciado, ya sea utilizando sentencias condicionales explícitas o usando algún tipo de especialización? Pseudocódigo:

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;
¿Fue útil?

Solución

Puede recurrir a RTTI utilizando TypeInfo (T) = TypeInfo (cadena) . Para probar si algo es una clase, puedes usar algo como PTypeInfo (TypeInfo (T)) ^. Kind = tkClass .

El tipo PTypeInfo y el miembro de enumeración tkClass se definen en la unidad TypInfo .

Otros consejos

Si a alguien le interesa cómo implementé mi " tamaño de peor caso con tratamiento especial para cadenas "

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;

Para mí, esto hace el trabajo en cuestión. ¡Gracias a todos los colaboradores!

en C #, puedes hacer un typeof (T) que te permita hacer algo como

(T = String)

o

(T is class)

No he visto tu otra pregunta (no te vinculaste), pero ¿qué estás realmente buscando? En general, hacer algo condicional en el tipo o un código de tipo a través de ifs como lo está haciendo o un interruptor generalmente se transforma mejor para tener una interfaz o función abstracta en algún lugar que se personaliza por contexto.

TypeInfo (T) es la forma correcta. Además, puede usar todas las cosas de la unidad TypInfo como el registro TTypeData para determinar algunas propiedades específicas de un tipo que usa en lugar de genérico. Cuando determine el tipo actual utilizado en lugar de T, puede usar el puntero del puntero para obtener el valor de una variable.

Aquí hay un código de ejemplo que acepta cualquier tipo de enumeración como genérico. Tenga en cuenta que solo funcionará para las enumeraciones habituales (sin valores fijos como

TEnumWontWork = (primero = 1, segundo, tercero)

) y la enumeración no debe declararse como tipo local dentro de un procedimiento. En estos casos, el compilador no genera TypeInfo para las enumeraciones.

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;

Muestra de uso:

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

Como currículum, utilicé tres trucos aquí:

1) Obtención de TypeInfo for T con accesorios generales de T

2) Obtención de TypeData para T con accesorios detallados de T

3) Usar la magia del puntero para obtener el valor de los parámetros dados a partir del tipo T.

Espero esta ayuda.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top