编码笔记

导航

用泛型实现对枚举的通用处理

写代码的时候遇到一个问题,想写一个通用方法来实现对枚举的类型的操作,如获取枚举的项的列表,获取一个枚举值的索引等等,

本来以为很简单,写一个函数:

function GetEnumNames(枚举类): TArray<string>

结果发现这个参数怎么搞也搞不对,不知道传一个什么样的参数可以支持所有枚举类型,因为函数内会用TypeInfo。

后来想到用泛型来传入枚举类来处理,果然成功了。

  /// <summary> 针对枚举类型的一组功能函数 </summary>
  TEnumEX<T> = class
  public
    /// <summary> 把字符串转成枚举的值 </summary>
    class function StrToEnumType(const S: string): T; overload;
    /// <summary> 把字符串转成枚举的值 </summary>
    class function StrToEnumType(const S: string; Default: T): T; overload;
    /// <summary> 把枚举的值转成字符串 </summary>
    class function EnumToString(Value: T): string;
    /// <summary> 获取枚举类型的项列表 </summary>
    class function GetEnumNames : TArray<string>;
    /// <summary> 获取枚举值的序号 </summary>
    class function GetEnumOrd(const S: string) : Integer;
  end;

implementation

uses
  RTTI,SysConst,uLayoutConst;

{ TEnumConvert<T> }

class function TEnumEX<T>.EnumToString(Value: T): string;
var
  v: Integer;
begin
  case PTypeInfo(TypeInfo(T))^.Kind of
    tkEnumeration:
      case TypInfo.GetTypeData(TypeInfo(T))^.OrdType of
        otUByte, otSByte: v := PByte(@Value)^;
        otUWord, otSWord: v := PWord(@Value)^;
        otULong, otSLong: v := PInteger(@Value)^;
      end;
  else
    raise EInvalidCast.CreateRes(@SInvalidCast);
  end;
  Result := TypInfo.GetEnumName(TypeInfo(T), v);
end;

class function TEnumEX<T>.StrToEnumType(const S: string): T;
begin
  case PTypeInfo(TypeInfo(T))^.Kind of
    tkEnumeration:
      case TypInfo.GetTypeData(TypeInfo(T))^.OrdType of
        otUByte, otSByte: PByte(@Result)^ := GetEnumValue(TypeInfo(T), S);
        otUWord, otSWord: PWord(@Result)^ := GetEnumValue(TypeInfo(T), S);
        otULong, otSLong: PInteger(@Result)^ := GetEnumValue(TypeInfo(T), S);
      end;
  else
    raise EInvalidCast.CreateRes(@SInvalidCast);
  end;
end;

class function TEnumEX<T>.GetEnumNames: TArray<string>;
var
  p: PTypeData;
  i: Integer;
  s: String;
  pt: PTypeInfo;
begin
  pt := TypeInfo(T);
  p := GetTypeData(TypeInfo(T));
  SetLength(Result, p.MaxValue+1);
  for i := p.MinValue to p.MaxValue do
  begin
    S := GetEnumName(pt,i);
    Result[i] := S;
  end;
end;

class function TEnumEX<T>.GetEnumOrd(const S: string): Integer;
begin
  case PTypeInfo(TypeInfo(T))^.Kind of
    tkEnumeration:
        Result := GetEnumValue(TypeInfo(T), S);
  else
    raise EInvalidCast.CreateRes(@SInvalidCast);
  end;
end;

class function TEnumEX<T>.StrToEnumType(const S: string; Default: T): T;
begin
  if S <> '' then begin
    Result := StrToEnumType(S);
  end else begin
    Result := Default;
  end;
end;

 

调用很简单

var
  s : string;
  ss : TArray<string>;
begin
  inherited;
  ss := TEnumEX<TBIEditUIControl>.GetEnumNames;
  for s in ss do
  begin
    ShowMessage(s);
  end;
end;

 

通过这次尝试,加深了对泛型的理解。

posted on 2014-09-18 19:59  封三郎  阅读(5012)  评论(0编辑  收藏  举报