如何使用重载运算符将空集分配给记录

How to assign an empty set to a record using overloaded operators

我正在使用一条记录来封装两个不同的集合。
我已经放入运算符以允许将任一集合分配给记录。这样做会清除另一组。
但是我不能分配一个空集。

参见下面的示例代码:

Program test;

{$Apptype console}
type
  TSomeThing = (a,b,c);
  TOtherThing = (x,y,z);
  TSomeThings = set of TSomething;
  TOtherThings = set of TOtherThing;

  TSomeRecord = record
  strict private
    Fa: TSomeThings;
    Fb: TOtherThings;
  public
    class operator Implicit(a: TSomeThings): TSomeRecord;
    class operator Implicit(a: TOtherThings): TSomeRecord;
  end;

implementation

class operator TSomeRecord.Implicit(a: TSomeThings): TSomeRecord;
begin
  Result.Fa:= a;
  Result.Fb:= [];
end;

class operator TSomeRecord.Implicit(a: TOtherThings): TSomeRecord;
begin
  Result.Fa:= [];
  Result.Fb:= a;
end;

var
  SomeRec: TSomeRecord;

begin
  SomeRec:= [];
end.

[dcc64 Error] InstructionList.pas(512): E2010 Incompatible types: 'TSomeRecord' and 'Set'

如何才能将空集分配给我的记录?
我可以滥用隐式运算符来允许 SomeRec:= nil;,但这看起来很丑陋。

编译器无法判断您指的是 TSomeThing 的空集还是 TOtherThing 的空集。您可以声明类型化常量以允许编译器解析重载:

const
  EmptySomeThings: TSomeThings = [];
  EmptyOtherThings: TOtherThings = [];

然后以下赋值将按您预期的方式编译和解析:

SomeRec:= EmptySomeThings;
SomeRec:= EmptyOtherThings;

当然,您知道其中任何一个都具有相同的效果,因为 Implicit 运算符的实现设置了一个字段,并清除了另一个字段。但是编译器无法知道这一点。

如果您想清除记录中的两个成员,您可以随时使用:

SomeRec:= Default(TSomeRecord);

我个人可能会将其包装在静态 class 方法中,如下所示:

class function Default: TSomeRecord; static;
....
class function TSomeRecord.Default: TSomeRecord;
begin
  Result := Default(TSomeRecord);
end;

那你可以这样写:

SomeRec:= TSomeRecord.Default;

在理想情况下,您可以在类型中声明常量,但语言设计者没有想到这一点,遗憾的是这是不可能的。

更新

Rudy 在评论中正确指出,可以通过记录助手将常量添加到记录类型。这对我来说是个新闻,因为我错误地认为助手只能添加方法。这就是我喜欢 Stack Overflow 的地方。即使您认为自己非常了解某事,也总是有获得更多知识的余地。谢谢鲁迪。

所以你可以这样写:

type
  TSomeRecordHelper = record helper for TSomeRecord
  public
    const
      Default: TSomeRecord = ();
  end;