Pergunta

Desde a minha pergunta de talvez não fosse totalmente claro e eu não obter a resposta Eu queria, eu vou tentar formulá-la de uma forma mais geral:

Existe uma maneira de implementar um comportamento especial com base no tipo real de um tipo genérico instanciado ou usando instruções condicionais explict ou usando algum tipo de especialização? 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;
Foi útil?

Solução

Você pode cair de volta para RTTI, usando TypeInfo(T) = TypeInfo(string). Para testar para ver se algo é uma classe, você poderia usar algo como PTypeInfo(TypeInfo(T))^.Kind = tkClass.

O tipo PTypeInfo e membro tkClass enumeração são definidas na unidade TypInfo.

Outras dicas

Se alguém estiver interessado como eu fiz implementar o meu "tamanho pior caso com tratamento especial para cordas"

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 mim, isso faz o trabalho na mão. Graças a todos os contribuintes!

em C #, você pode fazer um typeof(T) que lhe permitiria fazer algo como

(T = String)

ou

(T is class)

Eu não vi sua outra pergunta (você não fez link para ele), mas o que você realmente procurando? Em geral, fazendo condicional algo do tipo ou um typecode via ifs como você está fazendo ou um interruptor é geralmente melhor transformado em ter um em algum lugar da interface ou função abstrata que fica personalizado pelo contexto.

TypeInfo (T) é o caminho certo. Além disso, você pode usar todo o material da unidade TypInfo como registro TTypeData para determinar algumas propriedades específicas de um tipo que você usar em vez de genérico. Quando você determinar o tipo de corrente utilizado em vez de T, você pode usar truque ponteiro para obter um valor de uma variável.

Aqui está um código de exemplo que aceita qualquer tipo de enumeração como genérico. Note-se que ele irá trabalhar para enumerações habituais somente (sem valores fixos, como

= TEnumWontWork (primeira = 1, segundo, terceiro)

) ea enumeração não deve ser declarado como tipo local dentro de um procedimento. Nestes casos compilador não gera TypeInfo para os enums.

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;

Amostra de uso:

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

Como um currículo, eu usei três truques aqui:

1) Obtendo TypeInfo para T com adereços gerais de T

2) Obtendo TypeData para T com adereços detalhados de T

3) Usando magia ponteiro para obter o valor de parâmetros de dados como do tipo T.

Espero que isso ajuda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top