Question

Depuis ma question de hier n'était peut-être pas tout à fait claire et je n'ai pas eu la réponse. Je voulais, je vais essayer de le formuler de manière plus générale:

Existe-t-il un moyen d'implémenter un comportement spécial basé sur le type réel d'un type générique instancié, en utilisant des instructions conditionnelles explicites ou en utilisant une sorte de spécialisation? Pseudocode:

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;
Était-ce utile?

La solution

Vous pouvez revenir à RTTI en utilisant TypeInfo (T) = TypeInfo (chaîne) . Pour vérifier si quelque chose est une classe, vous pouvez utiliser quelque chose comme PTypeInfo (TypeInfo (T)) ^. Kind = tkClass .

Le type PTypeInfo et le membre d'énumération tkClass sont définis dans l'unité TypInfo .

Autres conseils

Si quelqu'un s'intéresse à la façon dont j'ai implémenté ma "taille minimale" avec un traitement spécial pour les chaînes "

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;

Pour moi, cela fait le travail à portée de main. Merci à tous les contributeurs!

en C #, vous pouvez faire un typeof (T) qui vous permettrait de faire quelque chose comme

(T = String)

ou

(T is class)

Je n’ai pas vu votre autre question (vous n’avez pas créé de lien), mais que cherchez-vous vraiment ? En général, il est préférable de transformer une chose conditionnée par un type ou un code de code via ifs ou un commutateur en une interface ou une fonction abstraite personnalisable en fonction du contexte.

TypeInfo (T) est la bonne façon. De plus, vous pouvez utiliser tous les éléments de l'unité TypInfo tels que l'enregistrement TTypeData pour déterminer certaines propriétés spécifiques d'un type que vous utilisez plutôt que générique. Lorsque vous déterminez le type actuel utilisé au lieu de T, vous pouvez utiliser l'astuce du pointeur pour obtenir la valeur d'une variable.

Voici un exemple de code qui accepte tout type d’énumération en tant que générique. Notez que cela fonctionnera uniquement pour les énumérations habituelles (sans valeurs fixes telles que

TEnumWontWork = (premier = 1, deuxième, troisième)

) et l'énumération ne doit pas être déclarée comme type local dans une procédure. Dans ces cas, le compilateur ne génère pas TypeInfo pour les énumérations.

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;

Exemple d'utilisation:

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

En résumé, j’ai utilisé trois astuces ici:

1) Obtention de TypeInfo pour T avec les accessoires généraux de T

2) Obtention de TypeData for T avec les accessoires détaillés de T

3) Utilisation de la magie de pointeur pour obtenir la valeur de paramètres donnée en tant que type T.

J'espère que cela vous aidera.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top