TypeInfo (T) là đúng cách. Hơn nữa, bạn có thể sử dụng tất cả các công cụ từ đơn vị TypInfo như bản ghi TTypeData để xác định một số thuộc tính cụ thể của một loại mà bạn sử dụng thay vì chung chung. Khi bạn xác định loại hiện tại được sử dụng thay cho T, bạn có thể sử dụng mẹo con trỏ để nhận giá trị của một biến.
Dưới đây là mã mẫu chấp nhận bất kỳ loại liệt kê nào là chung chung. Lưu ý rằng nó sẽ làm việc cho enumerations thông thường chỉ (không có giá trị cố định như
TEnumWontWork = (đầu tiên = 1, thứ hai, thứ ba)
) và enum phải không được khai báo là kiểu địa phương bên trong một thủ tục. Trong những trường hợp này trình biên dịch tạo ra không có TypeInfo cho enums.
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;
Mẫu sử dụng:
type
TEnum = (enOne, enTwo, enThree);
var
tst: TEnumArr<TEnum>;
begin
tst := TEnumArr<TEnum>.Create;
tst[enTwo] := $FF;
Log(tst[enTwo]);
Là một sơ yếu lý lịch, tôi đã sử dụng ba thủ thuật ở đây:
1) Bắt TypeInfo cho T với đạo cụ chung của T
2) Bắt TypeData cho T với các đạo cụ chi tiết của T
3) Sử dụng phép thuật con trỏ để có được giá trị của parame ters được cho là loại T.
Hy vọng trợ giúp này.
+1 Cảm ơn rất nhiều! Điều đó thực hiện chính xác những gì tôi đang tìm kiếm! Ngay cả khi bạn không phải là người hâm mộ ý tưởng ban đầu của tôi;) – jpfollenius