结构和类中的等式运算符重载

时间:2012-02-08 01:29:16

标签: c# .net

如果我为一个类重载operator ==,我必须在比较字段之前执行一些检查:

  • 如果两个参数都为null,或者两个参数都是同一个实例,则返回true

    示例:if (System.Object.ReferenceEquals(arg1, arg2)) return true;

  • 如果一个为空,但不是两个,则返回false

    示例:if (((object)arg1 == null) || ((object)arg2 == null)) return false;

实际上,如果我有一个结构并且我想要重载operator ==,那么这些检查不是必需的,而是它们没用,原因如下:struct是值类型< / em>,因此它不能为null,例如DateTime date = null;无效,因为DateTime(即结构)不是引用类型,因此您无法比较两个{{1} },其中一个设置为DateTime

我使用null创建了一个简单的结构Point2D,然后我将operator ==的实例与Point2D进行比较:

null
  1. 显然Point2D point = new Point2D(0,0); Console.WriteLine((point == null)); 未调用,但比较返回operator ==。叫哪种方法?

  2. documentation表示不建议在非不可变类型中重载此运算符。为什么呢?

2 个答案:

答案 0 :(得分:12)

Chris Shain的回答是正确,但没有解释为什么这是合法的

当覆盖相等运算符,并且两个操作数都是非可空值类型,并且返回类型是 bool 时,我们会免费为您提供解除了运营商。也就是说,如果你有

public static bool operator ==(S s1, S s2) { ... }

然后无需额外费用即可获得

public static bool operator ==(S? s1, S? s2) { ... }

正在调用的运算符。当然编译器知道结果总是假的,因为其中一个操作数是null而另一个操作数永远不是。

曾经有一个警告,说明你的代码总是返回false,但是我们不小心将它们禁用了几个版本,并且从未实际将其重新打开。我明天在Roslyn编译器中处理这个代码,所以我会看到我能做些什么来恢复它的形状。

答案 1 :(得分:5)

因为似乎编译器优化了这一点。我试过这段代码:

System.Drawing.Point point = new System.Drawing.Point(0,0);
Console.WriteLine((point == null));

它产生了以下IL:

IL_0000:  ldloca.s    00 
IL_0002:  ldc.i4.0    
IL_0003:  ldc.i4.0    
IL_0004:  call        System.Drawing.Point..ctor
IL_0009:  ldc.i4.0    
IL_000A:  call        System.Console.WriteLine

这最终归结为“创建一个Point,然后将false写入命令行”

这也解释了为什么它不会给您的运营商打电话。结构永远不能为null,并且在编译器可以保证您总是会因此而得到错误的情况下,它根本不会发出代码来调用运算符。

此代码也会发生同样的事情,即使String是一个类并重载==运算符:

System.Drawing.Point point = new System.Drawing.Point(0,0);
Console.WriteLine("foo" == null);

至于不变性...... C#中的==运算符通常被解释为表示“引用相等”,例如这两个变量指向同一个类的实例。如果你正在重载它,那么你通常会说这个类的两个实例,而不是同一个实例,当它们的数据相同时,它们应该表现得好像它们是同一个实例。典型的例子是Strings。 "A" == GiveMeAnA()即使GiveMeAnA返回的实际字符串引用可能与文字"A"所代表的字符串引用不同。

如果你在不可变的类上重载了==运算符,那么在评估==之后的类的变异可能会导致许多微妙的错误。