質問

昨日からの私の質問はおそらく完全に明確ではなかったので、答えは得られませんでした欲しかったので、もっと一般的な方法で定式化しようとします:

明示的な条件付きステートメントを使用するか、何らかの特殊化を使用して、インスタンス化されたジェネリック型の実際の型に基づいて特別な動作を実装する方法はありますか?擬似コード:

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;
役に立ちましたか?

解決

TypeInfo(T)= TypeInfo(string)を使用して、RTTIにフォールバックできます。何かがクラスかどうかをテストするには、 PTypeInfo(TypeInfo(T))^。Kind = tkClass のようなものを使用できます。

PTypeInfo タイプおよび tkClass 列挙メンバーは、 TypInfo ユニットで定義されます。

他のヒント

「文字列の特別な扱いで最悪のサイズ」をどのように実装したか誰かが興味を持っている場合

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;

私にとって、これは手元にあります。すべての貢献者に感謝します!

C#では、 typeof(T)を実行できます。これにより、次のようなことができます

(T = String)

または

(T is class)

他の質問を見たことはありません(リンクしていません)が、本当に探しているものは何ですか? 一般に、あなたがしているようなifやスイッチを介してタイプやタイプコードを条件とする何かを行うことは、一般的には、コンテキストによってカスタマイズされたインターフェースまたは抽象関数をどこかに持つことに最も良く変換されます。

TypeInfo(T)は正しい方法です。さらに、TTypeDataレコードのようなTypInfoユニットのすべてのものを使用して、ジェネリックではなく使用するタイプの特定のプロパティを決定できます。 Tの代わりに使用されている現在の型を決定するとき、ポインタートリックを使用して変数の値を取得できます。

列挙型を汎用として受け入れるサンプルコードを次に示します。通常の列挙に対してのみ機能することに注意してください(

TEnumWontWork =(最初の= 1、2番目、3番目)

)および列挙型は、プロシージャ内でローカル型として宣言しないでください。これらの場合、コンパイラーは列挙型のTypeInfoを生成しません。

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;

使用例:

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

履歴書として、ここで3つのトリックを使用しました:

1)Tの一般的な小道具でTのTypeInfoを取得する

2)Tの詳細な小道具でTのTypeDataを取得する

3)ポインターマジックを使用して、T型として指定されたパラメーターの値を取得します。

このヘルプをお待ちしています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top