用泛型實現對枚舉的通用處理

寫代碼的時候遇到一個問題,想寫一個通用方法來實現對枚舉的類型的操做,如獲取枚舉的項的列表,獲取一個枚舉值的索引等等,函數

原本覺得很簡單,寫一個函數:spa

function GetEnumNames(枚舉類): TArray<string>code

結果發現這個參數怎麼搞也搞不對,不知道傳一個什麼樣的參數能夠支持全部枚舉類型,由於函數內會用TypeInfo。blog

後來想到用泛型來傳入枚舉類來處理,果真成功了。索引

  /// <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;

 

經過此次嘗試,加深了對泛型的理解。string

相關文章
相關標籤/搜索