如果我为一个类重载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
显然Point2D point = new Point2D(0,0);
Console.WriteLine((point == null));
未调用,但比较返回operator ==
。叫哪种方法?
documentation表示不建议在非不可变类型中重载此运算符。为什么呢?
答案 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"
所代表的字符串引用不同。
如果你在不可变的类上重载了==
运算符,那么在评估==
之后的类的变异可能会导致许多微妙的错误。