如何使用泛型处理集

时间:2018-11-12 16:13:06

标签: delphi generics

我正在尝试使用泛型将一组字节转换为一个枚举集。但是代码无法编译。 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.

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

无法尝试。为了使之成为可能,您将需要将通用类型参数约束为可以在其上形成集合的类型。但是该语言不支持这种通用约束。

实际上,您现有的代码已经包含根本问题的明显迹象。你有:

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 Error]: E2001 Ordinal type required

这是因为编译器无法确定T是序数类型。为此,由于T是泛型类型参数,因此您需要在其中强加一个通用约束,即T是序数类型。但是语言不支持这种约束。

答案 1 :(得分:-1)

您可以很轻松地实现所需的目标,但不能达到尝试的目的(其他人已经指出了这一点)

如果逐步执行以下程序,您将在调试器中看到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的错误检查(范围等)。