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


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

本來以為很簡單,寫一個函數:

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;

 

通過這次嘗試,加深了對泛型的理解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM