如何使用通用枚举类型调用GetEnumName? [英] How can I call GetEnumName with a generic enumerated type?
问题描述
我创建了一个小型演示类来说明问题:
键入
TEnumSettings&TK;记录> = class
private
键:TKey;
public
构造函数Create(aKey:TKey);
函数ToString:string;覆盖
结束
使用
TypInfo;
{TEnumSettings< TKey> }
构造函数TEnumSettings< TKey> .Create(aKey:TKey);
begin
如果PTypeInfo(System.TypeInfo(TKey)).Gind<> tkEnumeration then
Exception.Create(string(PTypeInfo(System.TypeInfo(TKey))。Name)+'不是枚举');
密钥:= aKey;
结束
函数TEnumSettings< TKey> .ToString:string;
begin
结果:= GetEnumName(System.TypeInfo(TKey),Integer(Key))< ==这里我得到一个编译错误:无效的类型cast
end;
我使用的是Delphi XE。那么这可以做到吗如果是这样呢?
个人来说,我会通过调用 Move
。我有以下类型:
type
TEnumeration< T:record> = class
strict private
class function TypeInfo:PTypeInfo;一致;静态的;
类函数TypeData:PTypeData;一致;静态的;
public
类函数IsEnumeration:Boolean;静态的;
类函数ToOrdinal(枚举:T):整数;一致;静态的;
类函数FromOrdinal(Value:Integer):T;一致;静态的;
类函数MinValue:Integer;一致;静态的;
类函数MaxValue:Integer;一致;静态的;
类函数InRange(Value:Integer):Boolean;一致;静态的;
类函数EnsureRange(Value:Integer):Integer;一致;静态的;
结束
{TEnumeration< T> }
类函数TEnumeration< T> .TypeInfo:PTypeInfo;
begin
结果:= System.TypeInfo(T);
结束
类函数TEnumeration< T> .TypeData:PTypeData;
begin
结果:= TypInfo.GetTypeData(TypeInfo);
结束
类函数TEnumeration< T> .IsEnumeration:Boolean;
begin
结果:= TypeInfo.Kind = tkEnumeration;
结束
类函数TEnumeration< T> .ToOrdinal(枚举:T):整数;
begin
Assert(IsEnumeration);
Assert(SizeOf(Enum)< = SizeOf(Result));
结果:= 0; //当SizeOf(Enum)< SizeOf(Result)
Move(枚举,结果,SizeOf(枚举));
Assert(InRange(Result));
结束
类函数TEnumeration< T> .FromOrdinal(Value:Integer):T;
begin
Assert(IsEnumeration);
Assert(InRange(Value));
Assert(SizeOf(Result)< = SizeOf(Value));
Move(Value,Result,SizeOf(Result));
结束
类函数TEnumeration< T> .MinValue:Integer;
begin
Assert(IsEnumeration);
结果:= TypeData.MinValue;
结束
class function TEnumeration< T> .MaxValue:Integer;
begin
Assert(IsEnumeration);
结果:= TypeData.MaxValue;
结束
类函数TEnumeration< T> .InRange(Value:Integer):Boolean;
var
ptd:PTypeData;
begin
Assert(IsEnumeration);
ptd:= TypeData;
结果:= Math.InRange(Value,ptd.MinValue,ptd.MaxValue);
结束
类函数TEnumeration< T> .EnsureRange(Value:Integer):整数;
var
ptd:PTypeData;
begin
Assert(IsEnumeration);
ptd:= TypeData;
结果:= Math.EnsureRange(Value,ptd.MinValue,ptd.MaxValue);
结束
ToOrdinal
我确定你可以适应你的班级。
如果你不喜欢使用 Move
这样,那么你可以使用 TValue
。
TValue.From< TKey>(Key).AsOrdinal
而@TLama指出你可以避免调用 GetEnumName
根据使用
TValue.From< TKey> ).ToString
面对它,使用 TValue
似乎更符合泛型和RTTI的精神。调用 Move
依赖于枚举类型的具体实现细节。但是,通过调试器来查看执行 TValue.From< TKey>(Key).AsOrdinal
之间的代码是非常有趣的。这一点就足以令我犹豫,推荐使用 TValue
。
另一种实现此目的的方法是使用 TRttiEnumerationType
:
TRttiEnumerationType.GetName< TKey>(Key)
执行此操作比使用 TValue.ToString
更有效,只是调用 GetEnumName
。
I have a Generic class wich uses an Enum Generic Type. My problem how do I use GetEnumName on an instance of that type ?
I've created a small demo class to illustrate the problem :
type
TEnumSettings<TKey: record > = class
private
Key: TKey;
public
constructor Create(aKey: TKey);
function ToString: string; override;
end;
uses
TypInfo;
{ TEnumSettings<TKey> }
constructor TEnumSettings<TKey>.Create(aKey: TKey);
begin
if PTypeInfo(System.TypeInfo(TKey)).Kind <> tkEnumeration then
Exception.Create(string(PTypeInfo(System.TypeInfo(TKey)).Name) + ' is not an Enumeration');
Key := aKey;
end;
function TEnumSettings<TKey>.ToString: string;
begin
Result := GetEnumName(System.TypeInfo(TKey), Integer(Key)) <== HERE I get a compile error: Invalid type cast
end;
I'm using Delphi XE. So can this be done? And if so how?
Personally, I would do this with a call to Move
. I have the following type:
type
TEnumeration<T: record> = class
strict private
class function TypeInfo: PTypeInfo; inline; static;
class function TypeData: PTypeData; inline; static;
public
class function IsEnumeration: Boolean; static;
class function ToOrdinal(Enum: T): Integer; inline; static;
class function FromOrdinal(Value: Integer): T; inline; static;
class function MinValue: Integer; inline; static;
class function MaxValue: Integer; inline; static;
class function InRange(Value: Integer): Boolean; inline; static;
class function EnsureRange(Value: Integer): Integer; inline; static;
end;
{ TEnumeration<T> }
class function TEnumeration<T>.TypeInfo: PTypeInfo;
begin
Result := System.TypeInfo(T);
end;
class function TEnumeration<T>.TypeData: PTypeData;
begin
Result := TypInfo.GetTypeData(TypeInfo);
end;
class function TEnumeration<T>.IsEnumeration: Boolean;
begin
Result := TypeInfo.Kind=tkEnumeration;
end;
class function TEnumeration<T>.ToOrdinal(Enum: T): Integer;
begin
Assert(IsEnumeration);
Assert(SizeOf(Enum)<=SizeOf(Result));
Result := 0; // needed when SizeOf(Enum) < SizeOf(Result)
Move(Enum, Result, SizeOf(Enum));
Assert(InRange(Result));
end;
class function TEnumeration<T>.FromOrdinal(Value: Integer): T;
begin
Assert(IsEnumeration);
Assert(InRange(Value));
Assert(SizeOf(Result)<=SizeOf(Value));
Move(Value, Result, SizeOf(Result));
end;
class function TEnumeration<T>.MinValue: Integer;
begin
Assert(IsEnumeration);
Result := TypeData.MinValue;
end;
class function TEnumeration<T>.MaxValue: Integer;
begin
Assert(IsEnumeration);
Result := TypeData.MaxValue;
end;
class function TEnumeration<T>.InRange(Value: Integer): Boolean;
var
ptd: PTypeData;
begin
Assert(IsEnumeration);
ptd := TypeData;
Result := Math.InRange(Value, ptd.MinValue, ptd.MaxValue);
end;
class function TEnumeration<T>.EnsureRange(Value: Integer): Integer;
var
ptd: PTypeData;
begin
Assert(IsEnumeration);
ptd := TypeData;
Result := Math.EnsureRange(Value, ptd.MinValue, ptd.MaxValue);
end;
The ToOrdinal
method does what you need, and I'm sure you'll be able to adapt it to your class.
If you don't like using Move
in this way, then you can use TValue
.
TValue.From<TKey>(Key).AsOrdinal
And @TLama points out that you can avoid calling GetEnumName
at all by using
TValue.From<TKey>(Key).ToString
On the face of it, using TValue
seems to be more in keeping with the ethos of generics and RTTI. A call to Move
relies on the specific implementation details of enumerated types. However, it's quite interesting to step through the debugger and observe quite how much code is involved in executing TValue.From<TKey>(Key).AsOrdinal
. That alone is enough to make me hesitate to recommend using TValue
.
Yet another way to achieve this is to use TRttiEnumerationType
:
TRttiEnumerationType.GetName<TKey>(Key)
The implementation of this is much more efficient than using TValue.ToString
, being little more than a call to GetEnumName
.
这篇关于如何使用通用枚举类型调用GetEnumName?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!