【问题标题】:Conditional behaviour based on concrete type for generic class基于泛型类的具体类型的条件行为
【发布时间】:2010-10-22 19:10:56
【问题描述】:

由于我来自yesterday 的问题可能并不完全清楚,并且我没有得到我想要的答案,所以我将尝试以更一般的方式来表述它:

有没有一种方法可以根据实例化泛型类型的实际类型使用显式条件语句或使用某种特化来实现特殊行为?伪代码:

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;

【问题讨论】:

    标签: delphi generics types delphi-2009


    【解决方案1】:

    您可以使用TypeInfo(T) = TypeInfo(string) 回退到 RTTI。要测试某个东西是否是一个类,您可以使用 PTypeInfo(TypeInfo(T))^.Kind = tkClass 之类的东西。

    PTypeInfo 类型和tkClass 枚举成员在TypInfo 单元中定义。

    【讨论】:

    • +1 非常感谢!这正是我想要的!即使你不喜欢我最初的想法;)
    【解决方案2】:

    如果有人对我是如何实现“对字符串进行特殊处理的最坏情况大小”感兴趣的

    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;
    

    对我来说,这可以完成手头的工作。感谢所有贡献者!

    【讨论】:

      【解决方案3】:

      TypeInfo(T) 是正确的方法。此外,您可以使用 TypInfo 单元中的所有内容(例如 TTypeData 记录)来确定您使用的类型的某些特定属性,而不是泛型。当您确定当前使用的类型而不是 T 时,您可以使用指针技巧来获取变量的值。

      这是一个示例代码,它接受任何枚举类型作为泛型。请注意,它仅适用于通常的枚举(没有固定值,如

      TEnumWontWork = (first = 1, second, third)

      ) 并且枚举不能在过程中声明为本地类型。在这些情况下,编译器不会为枚举生成 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]);
      

      作为简历,我在这里使用了三个技巧:

      1) 使用 T 的通用属性获取 T 的 TypeInfo

      2) 使用 T 的详细 props 获取 T 的 TypeData

      3) 使用指针魔术来获取类型为 T 的参数的值。

      希望对您有所帮助。

      【讨论】:

        【解决方案4】:

        在 C# 中,您可以执行 typeof(T),这将允许您执行类似的操作

        (T = String)
        

        (T is class)
        

        我还没有看到您的其他问题(您没有链接到它),但是您真正在寻找什么?一般来说,通过 ifs 或 switch 以类型或类型代码为条件的操作通常最好转换为在某处具有由上下文自定义的接口或抽象函数。

        【讨论】:

        • 我添加了另一个问题的链接。基本上我想要对字节大小进行粗略估计。我想处理不同于其他类型的字符串。
        • 谁对此投了反对票,请随时评论您的理由。我认为这是一个合理的答案
        • 啊,老爷子的因果报应系统有时会给人惊喜,谢谢!有趣的是,'是我支持你对自己的回答(但最初没有支持这个问题:P)
        猜你喜欢
        • 2017-11-01
        • 1970-01-01
        • 2018-09-20
        • 2020-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-11
        相关资源
        最近更新 更多