寫代碼的時候遇到一個問題,想寫一個通用方法來實現對枚舉的類型的操作,如獲取枚舉的項的列表,獲取一個枚舉值的索引等等,
本來以為很簡單,寫一個函數:
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;
通過這次嘗試,加深了對泛型的理解。
其實錯誤消息已經很明確的告訴你了,A type used as a constraint must be an interface, a non-sealed class or a type parameter.
必須是接口、未封閉的類或者類型
所以枚舉是不行的,你說說你到底想要實現什麼功能,我幫你想想辦法
public static IEnumerable<OT> GetByte<OT, T>() where T : OT { var t = typeof(T); T[] value = Enum.GetValues(t) as T[]; List<OT> aa = value.Select(j => (OT)j).ToList(); return aa; }