[英]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.