如何使用泛型处理集



我正在尝试使用Generics将一组Bytes转换为枚举集。但是代码不会编译。TValue.FromOrdinal(TypeInfo(T(,Ord(B((.AsType实际上正确地返回了枚举值,但我不能将此值包含在枚举集中。

interface 
type TByteSet = set of Byte;
type TMyNewEnum = (meZero, meOne, meTwo);
type TMyNewEnumSet = set of TMyNewEnum;
type
TEnum<T> = class(TObject)
public
class function ToString(const aEnumValue: T): string; reintroduce;
class function FromString(const aEnumString: string; const aDefault: T): T;
class procedure FromByteSet(const Value: TByteSet; out EnumSet: TMyNewEnumSet);
end
implementation
Var
MyByteSet: TMyByteSet;
MyEnumSet: TMyNewEnumSet;
...  
class procedure TEnum<T>.FromByteSet(const Value: TByteSet; out EnumSet: TMyNewEnumSet);
var
B: Byte;
begin
Assert(PTypeInfo(TypeInfo(T)).Kind = tkEnumeration, 'Type parameter must be an Enumeration');
for B in Value do
begin
EnumSet := EnumSet + TValue.FromOrdinal(TypeInfo(T), Ord(B)).AsType<T>; //This line does not compile
end;
end;
...

//intended Usage
MyByteSet := [0, 2];
TEnum<TMyNewEnum>.FromByteSet(MyByteSet, MyEnumSet); 
//I would like MyEnumSet to contain [meZero, meTwo]
end.

有什么想法吗?

您所尝试的是不可能的。为了实现这一点,您需要能够将泛型类型参数约束为可以在其上形成集合的类型。但该语言不支持这种通用约束。

事实上,您现有的代码已经包含了根本问题的信号。您有:

type
TEnum<T> = class(TObject)
public
class procedure FromByteSet(const Value: TByteSet; out EnumSet: TMyNewEnumSet);
end;

这里房间里的大象是FromByteSet没有引用T,因此不是通用的。

为了使函数通用,你需要这样的东西:

type
TEnum<T: record> = class(TObject)
private
type SetOfT = set of T;
public
class procedure FromByteSet(const Value: TByteSet; out EnumSet: SetOfT);
end;

这不会编译。编译器使用以下对象来反对类型声明:

[dcc32错误]:需要E2001序数类型

这是因为编译器无法确定T是序数类型。为了做到这一点,因为T是一个泛型类型参数,您需要在那里施加一个泛型约束,即T是一个序数类型。但该语言不支持这种限制。

你可以很简单地实现你想要的,但不是你试图实现的方式(其他人已经指出了这一点(

如果您逐步执行以下程序,您将在调试器中看到MyEnumSet最终得到所需的值。

program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;

type TByteSet = set of Byte;
type TMyNewEnum = (meZero, meOne, meTwo);
type TMyNewEnumSet = set of TMyNewEnum;
type
TEnum<T> = class(TObject)
public
class procedure FromByteSet(const Value: TByteSet; out EnumSet: T);
end;
Var
MyByteSet: TByteSet;
MyEnumSet: TMyNewEnumSet;

procedure Test( const Parm1 : TByteSet; out Parm2 : TMyNewEnumSet );
var
iResult : TMyNewEnumSet absolute Parm1;
begin
Parm2 := iResult;
end;

{ TEnum<T> }
class procedure TEnum<T>.FromByteSet(const Value: TByteSet; out EnumSet : T );
var
iResult : T absolute Value;
begin
EnumSet := iResult;
end;
begin
MyByteSet := [0,2];
TEnum<TMyNewEnumSet>.FromByteSet( MyByteSet, MyEnumSet);
end.

当然,您需要添加可以使用RTTI的错误检查(边界等(。

最新更新