寫代碼的時候遇到一個問題,想寫一個通用方法來實現對枚舉的類型的操作,如獲取枚舉的項的列表,獲取一個枚舉值的索引等等,
本來以為很簡單,寫一個函數:
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; }