写代码的时候遇到一个问题,想写一个通用方法来实现对枚举的类型的操作,如获取枚举的项的列表,获取一个枚举值的索引等等,
本来以为很简单,写一个函数:
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;
通过这次尝试,加深了对泛型的理解。