TypeInfo (T) es el camino correcto. Además, puede usar todo el contenido de la unidad TypInfo como el registro TTypeData para determinar algunas propiedades específicas de un tipo que utiliza en lugar de genérico. Cuando determina el tipo actual utilizado en lugar de T, puede usar el truco del puntero para obtener el valor de una variable.
Aquí hay un código de muestra que acepta cualquier tipo de enumeración como genérico. Tenga en cuenta que va a trabajar para enumeraciones habituales solamente (sin valores fijos como
TEnumWontWork = (primera = 1, segundo, tercero)
) y la enumeración no debe ser declarado como tipo de locales 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;
Ejemplo de uso:
type
TEnum = (enOne, enTwo, enThree);
var
tst: TEnumArr<TEnum>;
begin
tst := TEnumArr<TEnum>.Create;
tst[enTwo] := $FF;
Log(tst[enTwo]);
Como resumen, he usado tres trucos aquí:
1) Cómo TypeInfo para T con los apoyos generales de T
2) Como llegar TypeData para T con detalles de T
3) Uso de magia de puntero para obtener el valor de parame ters dados a partir del tipo T.
Espero que esta ayuda.
+1 ¡Muchas gracias! ¡Eso hace exactamente lo que estaba buscando! Incluso si no eres fanático de mi idea original;) – jpfollenius