Frage

Da meine Frage von gestern vielleicht nicht ganz klar war, und ich habe nicht die Antwort ich wollte, ich werde versuchen, es in eine allgemeinere Art und Weise zu formulieren:

Gibt es eine Möglichkeit besonderes Verhalten zu implementieren, basierend auf dem aktuellen Typ eines instanziiert generischen Typs entweder mit explict bedingten Anweisungen oder irgendeine Art von Spezialisierung mit? Pseudo-Code:

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;
War es hilfreich?

Lösung

Sie können mit TypeInfo(T) = TypeInfo(string) zurück zu RTTI, fallen. Um zu testen, um zu sehen, ob etwas eine Klasse ist, Sie so etwas wie PTypeInfo(TypeInfo(T))^.Kind = tkClass nutzen könnten.

Der PTypeInfo Typ und tkClass Enumerationsmember sind in der TypInfo Einheit definiert.

Andere Tipps

Wenn jemand interessiert, wie ich habe meine „Worst-Case-Größe mit einer besonderen Behandlung für Strings“ implementieren

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;

Für mich hat dies den Job bei der Hand. Dank an alle Mitwirkenden!

in C #, können Sie eine typeof(T) tun, was Sie so etwas wie

zu tun erlauben würde,
(T = String)

oder

(T is class)

Ich habe nicht Ihre andere Frage (Sie didnt Link, um es) gesehen, aber was sind Sie wirklich suchen? In der Regel etwas davon abhängig Art tun oder einen Typcode über ifs wie Sie tun, oder ein Schalter ist in der Regel am besten umgewandelt in einer Schnittstelle oder abstrakte Funktion irgendwo, die durch Kontext angepasst wird.

Typeinfo (T) ist der richtige Weg. Außerdem können Sie das ganze Zeug von Typinfo Einheit wie TTypeData Aufzeichnung verwenden Sie einige spezifische Eigenschaften eines Typs anstelle von Generika verwenden, um zu bestimmen. Wenn Sie die den aktuellen Typ anstelle von T verwendet bestimmen, können Sie Zeiger Trick verwenden, um einen Wert einer Variablen zu erhalten.

Hier ist ein Beispielcode, der jeden Aufzählungstyp als generisches akzeptiert. Beachten Sie, dass es nur für normale Aufzählungen arbeiten (ohne feste Werte wie

TEnumWontWork = (1 = erste, zweite, dritte)

) und die Enum darf nicht als lokaler Typ innerhalb einer Prozedur deklariert werden. In diesen Fällen Compiler erzeugt keine Typeinfo für die Aufzählungen.

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;

Beispiel der Nutzung:

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

Als Resümee, habe ich drei Tricks hier:

1) Erste Typeinfo für T mit allgemeinen Requisiten von T

2) Erste Typedata für T mit detaillierten Requisiten von T

3) Unter Verwendung Zeiger Magie den Wert der Parameter des Typs T gegeben zu bekommen.

Hoffe diese Hilfe.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top