如何使用通用枚举类型调用GetEnumName? [英] How can I call GetEnumName with a generic enumerated type?

查看:158
本文介绍了如何使用通用枚举类型调用GetEnumName?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个泛型类,它使用枚举通用类型。我的问题如何在该类型的实例上使用GetEnumName?



我创建了一个小型演示类来说明问题:

 键入
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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆