【问题标题】:Delphi 'in' operator overload on a set集合上的 Delphi 'in' 运算符重载
【发布时间】:2011-11-08 03:33:04
【问题描述】:

在 Delphi XE2 中,我试图重载记录上的 in 运算符,以允许我检查记录表示的值是否是集合的一部分。我的代码如下所示:

type
  MyEnum = (value1, value2, value3);
  MySet = set of MyEnum;
  MyRecord = record
    Value: MyEnum;
    class operator In(const A: MyRecord; B: MySet): Boolean;
  end;

class operator MyRecord.In(const A: MyRecord; B: MySet): Boolean;
begin
  Result := A.Value in B;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  S: MySet;
begin
  R.Value := value1;
  S := [value1, value2];
  Button1.Caption := BoolToStr(R in S);
end;

代码编译失败。对于语句R in S,编译器会说:不兼容的类型MyRecordMyEnum

如何在MyRecord 上重载In 运算符,以便R in S 在上述代码中计算为True

【问题讨论】:

  • 我不认为你想要达到的目标是可能的......你应该有更好的运气写一个额外的字符 ".Value" => BoolToStr(R.Value in S );并完成它
  • 我的问题中的代码只是一个简化的示例。在我的实际应用中,记录类型与集合类型没有一一对应的关系。我最终使用的解决方法是在记录中添加 function InSet(S: MySet): Boolean 并使用它而不是 in 运算符。
  • 或许将成员函数改为足够好 - BoolToStr(R._in(S));

标签: delphi operator-overloading delphi-xe2


【解决方案1】:

要使 in 运算符起作用,正确的操作数必须是记录类型,因为它是集合运算符而不是二元运算符。在您的情况下,它是左操作数。

所以以下将起作用:

type
  MyRecord = record
    Value: MyEnum;
    class operator In(const A: MyRecord; const B: MySet): Boolean;
  end;

  MyRecord2 = record
    Value: MySet;
    class operator In(const A: MyRecord; const B: MyRecord2): Boolean;
    class operator In(const A: MyEnum; const B: MyRecord2): Boolean;
  end;

class operator MyRecord.In(const A: MyRecord; const B: MySet): Boolean;
begin
  Result := A.Value in B;
end;

class operator MyRecord2.In(const A: MyRecord; const B: MyRecord2): Boolean;
begin
  Result := A.Value in B.Value;
end;

class operator MyRecord2.In(const A: MyEnum; const B: MyRecord2): Boolean;
begin
  Result := A in B.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: MyRecord;
  R2: MyRecord2;
begin
  R.Value := value1;
  R2.Value := [value1, value2];

  if R in R2 then;
  if value1 in R2 then;
end;

【讨论】:

    【解决方案2】:

    嗯,您几乎可以这样做,但您可能不想这样做。 AFAIK,类运算符仅适用于它们定义的类(或记录),因此代码中的 R 和 S 都必须是 TMyRecord。通过不明智地使用隐式转换,我们得到以下结果:

    unit Unit2;
    interface
    type
      MyEnum = (value1, value2, value3);
      MySet = set of MyEnum;
      MyRecord = record
        Value: MyEnum;
        ValueSet: MySet;
        class operator Implicit(A: MyEnum): MyRecord;
        class operator Implicit(A: MySet): MyRecord;
        class operator In (Left,Right:MyRecord): Boolean;
      end;
    
    implementation
    
    class operator MyRecord.Implicit(A: MyEnum): MyRecord;
    begin
      Result.Value := A;
    end;
    
    class operator MyRecord.Implicit(A: MySet): MyRecord;
    begin
      Result.ValueSet := A;
    end;
    
    class operator MyRecord.In(Left, Right: MyRecord): Boolean;
    begin
      Result:= left.Value in Right.ValueSet;
    end;
    end.
    

    以下内容现在可以编译,甚至可以工作:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      R: MyRecord;
      S: MyRecord;
    begin
      R.Value := value1;
      S := [value1,value2,value3];
      Button1.Caption := BoolToStr(R In S,true);
    end;
    

    我相信我们都会同意,它比“BoolToStr(R.Value in S)”要优雅得多。 但是下面的代码也会编译,但是会给出错误的结果:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      R: MyRecord;
      S: MyRecord;
    begin
      R.Value := value1;
      S := [value1,value2,value3];
      Button1.Caption := BoolToStr(S In R,true);
    end;
    

    因此,正如 Dorin 所评论的那样,最好只使用沉闷、古板的“BoolToStr(R.Value in S)”。当然,除非您按每行代码获得报酬。以及修复错误的奖励。

    【讨论】:

    • 这个特殊的问题只是一个我以前从未使用过的找出类运算符的练习。我正在处理的实际代码中的记录类型要复杂得多。它定义的运算符允许使用该记录的代码更简单。几百行简单的操作符函数让几千行复杂的代码更易读。
    • 对不起,我的 cmets 有点搞笑。我承认我对新的操作符加载记录非常感兴趣,并且正在广泛使用它。而且我同意它在许多情况下使代码更加清晰。我只是在取笑我自己的解决方案,因为在这种特殊情况下它是矫枉过正的;-)
    • "所以代码中的 R 和 S 都必须是 TMyRecord" 这是不正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    相关资源
    最近更新 更多