繁体   English   中英

基于泛型类的具体类型的条件行为

[英]Conditional behaviour based on concrete type for generic class

由于我昨天提出的问题可能并不完全清楚,而且我没有得到我想要的答案,我将尝试以更一般的方式制定它:

有没有办法基于实例化的泛型类型的实际类型实现特殊行为,使用explict条件语句或使用某种特化? 伪代码:

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;

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

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

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

TEnumWontWork =(第一= 1,第二,第三)

)并且枚举不得在过程中声明为本地类型。 在这些情况下,编译器不会为枚举生成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的详细道具获取T的TypeData

3)使用指针魔法获取T类型给出的参数值。

希望这有帮助。

在C#中,你可以做一个typeof(T) ,它可以让你做类似的事情

(T = String)

要么

(T is class)

我还没有看到你的另一个问题(你没有链接到它),但你真的在寻找什么? 一般来说,通过类似于你正在做的事情或者切换来做类型或类型代码的条件通常最好转换为在某个地方通过上下文定制的接口或抽象函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM