【问题标题】:How to assign an empty set to a record using overloaded operators如何使用重载运算符将空集分配给记录
【发布时间】:2026-01-21 22:10:02
【问题描述】:

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

参见以下示例代码:

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 错误] InstructionList.pas(512): E2010 不兼容的类型:'TSomeRecord' 和 'Set'

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

【问题讨论】:

  • 编译器无法识别[] 是什么。是 TSomeThings 还是 TOtherThings ?甚至我们也不能说。只有你知道你要在那里分配什么。

标签: delphi set operator-overloading


【解决方案1】:

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

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

然后按照您的预期编译和解析以下分配:

SomeRec:= EmptySomeThings;
SomeRec:= EmptyOtherThings;

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

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

SomeRec:= Default(TSomeRecord);

我个人可能会将它封装在这样的静态类方法中:

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;

【讨论】:

  • default() 被允许写信给严格的私人成员,这感觉很奇怪。我相信这种感觉会过去的。我会将您的最后一行缩短为:SomeRec.Clear 使用内联方法。
  • @Johan Default 没有写入任何内容 - 它正在创建一个默认的、已初始化的类型实例。编译后的应用程序需要能够做到这一点,例如,在构造过程中分配和初始化类字段。如果您担心复制该记录的私有成员的记录分配......好吧,记录是值类型。如果不复制记录的成员,分配还能如何工作?
  • @Johan 所以,假设你创建了一个TList<TSomeRecord>。然后你可能会想写:List[i].Clear。好吧,那不会有任何用处。它所做的只是清除临时的,并保持列表的内容不变。
  • @DavidHeffernan 不道歉。有些日子你得到熊......有些日子熊得到你;)
  • 我不是 100% 确定并且现在没有时间检查,但是从某个 Delphi 版本开始的 IIRC,您可以使用记录助手在类型之后潜入记录常量已定义。