为什么我们必须在 C# 中同时定义 == 和 !=?

发布于 2024-11-27 10:43:15 字数 1474 浏览 2 评论 0原文

C# 编译器要求每当自定义类型定义运算符 == 时,它还必须定义 != (请参阅 此处)。

为什么?

我很好奇为什么设计者认为有必要,以及为什么编译器不能在只有另一个运算符存在时默认为其中一个运算符提供合理的实现。例如,Lua 允许您仅定义相等运算符,而您可以免费获得另一个。 C# 可以通过要求您定义 == 或同时定义 == 和 != 来完成相同的操作,然后自动将缺少的 != 运算符编译为 !(left == right)

我知道存在一些奇怪的极端情况,其中某些实体可能既不相等也不不平等(例如 IEEE-754 NaN),但这些似乎是例外,而不是规则。因此,这并不能解释为什么 C# 编译器设计者将例外作为规则。

我见过一些做工很差的案例,其中定义了相等运算符,然后不等运算符是复制粘贴,每个比较都颠倒过来,每个 &&&&切换到 || (你明白了......基本上!(a==b)通过德摩根规则扩展)。这是一种糟糕的做法,编译器可以通过设计消除这种做法,就像 Lua 的情况一样。

笔记: 对于运算符 < 也同样如此。 > <=>=。我无法想象您需要以不自然的方式定义这些的情况。 Lua 让你只定义 <和 <= 并定义 >= 和 >自然地通过前者的否定。为什么 C# 不做同样的事情(至少“默认情况下”)?

编辑

显然,有充分的理由允许程序员按照自己的喜好实现平等和不平等的检查。一些答案指出了这样的情况可能会很好。

然而,我的问题的核心是,为什么在 C# 中强制要求这一点,而通常这在逻辑上不是必需的?

它也与 .NET 接口的设计选择形成鲜明对比,例如 Object.EqualsIEquatable.Equals IEqualityComparer.Equals,其中缺乏NotEquals 对应项表明框架将 !Equals() 对象视为不相等,仅此而已。此外,像 Dictionary 这样的类和像 .Contains() 这样的方法完全依赖于上述接口,并且即使定义了运算符也不直接使用它们。事实上,当 ReSharper 生成相等成员时,它会根据 Equals() 定义 ==!=,即使如此,仅当用户完全选择生成运算符。框架不需要相等运算符来理解对象相等。

基本上,.NET 框架并不关心这些运算符,它只关心一些 Equals 方法。要求用户同时定义 == 和 != 运算符的决定纯粹与语言设计相关,而不是 .NET 涉及的对象语义。

The C# compiler requires that whenever a custom type defines operator ==, it must also define != (see here).

Why?

I'm curious to know why the designers thought it necessary and why can't the compiler default to a reasonable implementation for either of the operators when only the other is present. For example, Lua lets you define only the equality operator and you get the other for free. C# could do the same by asking you to define either == or both == and != and then automatically compile the missing != operator as !(left == right).

I understand that there are weird corner cases where some entities may neither be equal nor unequal, (like IEEE-754 NaN's), but those seem like the exception, not the rule. So this doesn't explain why the C# compiler designers made the exception the rule.

I've seen cases of poor workmanship where the equality operator is defined, then the inequality operator is a copy-paste with each and every comparison reversed and every && switched to a || (you get the point... basically !(a==b) expanded through De Morgan's rules). That's poor practice that the compiler could eliminate by design, as is the case with Lua.

Note:
The same holds for operators < > <= >=. I can't imagine cases where you'll need to define these in unnatural ways. Lua lets you define only < and <= and defines >= and > naturally through the formers' negation. Why doesn't C# do the same (at least 'by default')?

EDIT

Apparently there are valid reasons to allow the programmer to implement checks for equality and inequality however they like. Some of the answers point to cases where that may be nice.

The kernel of my question, however, is why this is forcibly required in C# when usually it's not logically necessary?

It is also in striking contrast to design choices for .NET interfaces like Object.Equals, IEquatable.Equals IEqualityComparer.Equals where the lack of a NotEquals counterpart shows that the framework considers !Equals() objects as unequal and that's that. Furthermore, classes like Dictionary and methods like .Contains() depend exclusively on the aforementioned interfaces and do not use the operators directly even if they are defined. In fact, when ReSharper generates equality members, it defines both == and != in terms of Equals() and even then only if the user chooses to generate operators at all. The equality operators aren't needed by the framework to understand object equality.

Basically, the .NET framework doesn't care about these operators, it only cares about a few Equals methods. The decision to require both == and != operators to be defined in tandem by the user is related purely to the language design and not object semantics as far as .NET is concerned.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

淡紫姑娘! 2024-12-04 10:43:15

我不能代表语言设计者说话,但据我所知,这似乎是有意的、正确的设计决策。

查看这个基本的 F# 代码,您可以将其编译到工作库中。这是 F# 的合法代码,并且仅重载相等运算符,而不重载不等式:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

这正是它看起来的样子。它仅在 == 上创建一个相等比较器,并检查类的内部值是否相等。

虽然您无法在 C# 中创建这样的类,但您可以使用为 .NET 编译的类。很明显它将使用我们的重载运算符来表示 == 那么,运行时使用什么来表示 != 呢?

C# EMCA 标准有一大堆规则(第 14.9 节),解释了如何确定在评估相等性时使用哪个运算符。简而言之,如果要比较的类型属于同一类型并且存在重载的相等运算符,则它将使用该重载而不是标准引用从 Object 继承的相等运算符。因此,毫不奇怪,如果仅存在其中一个运算符,它将使用所有对象都有的默认引用相等运算符,因此不会有重载。1

知道这一点确实如此,真正的问题是:为什么要这样设计,为什么编译器不能自己弄清楚?很多人说这不是一个设计决定,但我喜欢认为这是这样考虑的,特别是考虑到所有对象都有一个默认的相等运算符这一事实。

那么,为什么编译器不自动创建 != 运算符呢?除非微软的人证实这一点,否则我无法确定,但这是我根据事实推理可以确定的。


为了防止意外行为

也许我想对 == 进行值比较以测试相等性。但是,当涉及到 != 时,我根本不关心值是否相等,除非引用相等,因为对于我的程序来说,将它们视为相等,我只关心引用是否匹配。毕竟,这实际上被概述为 C# 的默认行为(如果两个运算符都没有重载,就像某些用另一种语言编写的 .net 库的情况一样)。如果编译器自动添加代码,我就不能再依赖编译器输出应该兼容的代码。编译器不应编写会改变您行为的隐藏代码,尤其是当您编写的代码符合 C# 和 CLI 标准时。

就它强迫你超载它,而不是进入默认行为而言,我只能坚定地说它在标准中 (EMCA-334 17.9.2)2。标准没有具体说明原因。我相信这是因为 C# 借用了 C++ 的很多行为。有关这方面的更多信息,请参阅下文。


当您覆盖 !=== 时,您不必返回 bool。

这是另一个可能的原因。在 C# 中,此函数:

public static int operator ==(MyClass a, MyClass b) { return 0; }

与此函数一样有效:

public static bool operator ==(MyClass a, MyClass b) { return true; }

如果您返回 bool 以外的值,则编译器无法自动推断出相反的类型。此外,在您的运算符确实返回bool的情况下,创建生成只存在于一种特定情况下的代码,或者正如我上面所说的,隐藏的代码对他们来说是没有意义的CLR 的默认行为。


C# 大量借鉴了 C++3

当 C# 被引入时,MSDN 杂志上有一篇文章写道,谈论 C#:

许多开发人员希望有一种像 Visual Basic 一样易于编写、阅读和维护的语言,但仍提供 C++ 的强大功能和灵活性。

是的,C# 的设计目标是提供与 C++ 几乎相同的功能,只牺牲一点点来实现严格的类型安全和垃圾收集等便利性。 C# 强烈模仿 C++。

您可能不会惊讶地发现,在 C++ 中,相等运算符不必返回 bool,如 此示例程序

现在,C++ 并不直接要求您重载补余运算符。如果您编译了示例程序中的代码,您将看到它运行时没有错误。但是,如果您尝试添加行:

cout << (a != b);

您将得到

编译器错误 C2678 (MSVC):二进制“!=”:找不到采用“Test”类型左侧操作数的运算符(或者没有可接受的转换)。

因此,虽然 C++ 本身不要求您成对重载,但它不会让您使用尚未在自定义类上重载的相等运算符。它在 .NET 中有效,因为所有对象都有一个默认对象; C++ 没有。


1.附带说明一下,如果您想重载其中一个运算符,C# 标准仍然要求您重载这对运算符。这是标准的一部分,而不仅仅是编译器。但是,当您访问以不具有相同要求的其他语言编写的 .net 库时,有关确定调用哪个运算符的相同规则也适用。

2. EMCA-334 (pdf) (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf)

3.还有 Java,但这并不是重点

I can't speak for the language designers, but from what I can reason on, it seems like it was intentional, proper design decision.

Looking at this basic F# code, you can compile this into a working library. This is legal code for F#, and only overloads the equality operator, not the inequality:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

This does exactly what it looks like. It creates an equality comparer on == only, and checks to see if the internal values of the class are equal.

While you can't create a class like this in C#, you can use one that was compiled for .NET. It's obvious it will use our overloaded operator for == So, what does the runtime use for !=?

The C# EMCA standard has a whole bunch of rules (section 14.9) explaining how to determine which operator to use when evaluating equality. To put it overly-simplified and thus not perfectly accurate, if the types that are being compared are of the same type and there is an overloaded equality operator present, it will use that overload and not the standard reference equality operator inherited from Object. It is no surprise, then, that if only one of the operators is present, it will use the default reference equality operator, that all objects have, there is not an overload for it.1

Knowing that this is the case, the real question is: Why was this designed in this way and why doesn't the compiler figure it out on its own? A lot people are saying this wasn't a design decision, but I like to think it was thought out this way, especially regarding the fact all objects have a default equality operator.

So, why doesn't the compiler automagically create the != operator? I can't know for sure unless someone from Microsoft confirms this, but this is what I can determine from reasoning on the facts.


To prevent unexpected behavior

Perhaps I want to do a value comparison on == to test equality. However, when it came to != I didn't care at all if the values were equal unless the reference was equal, because for my program to consider them equal, I only care if the references match. After all, this is actually outlined as default behavior of the C# (if both operators were not overloaded, as would be in case of some .net libraries written in another language). If the compiler was adding in code automatically, I could no longer rely on the compiler to output code that should is compliant. The compiler should not write hidden code that changes the behavior of yours, especially when the code you've written is within standards of both C# and the CLI.

In terms of it forcing you to overload it, instead of going to the default behavior, I can only firmly say that it is in the standard (EMCA-334 17.9.2)2. The standard does not specify why. I believe this is due to the fact that C# borrows much behavior from C++. See below for more on this.


When you override != and ==, you do not have to return bool.

This is another likely reason. In C#, this function:

public static int operator ==(MyClass a, MyClass b) { return 0; }

is as valid as this one:

public static bool operator ==(MyClass a, MyClass b) { return true; }

If you're returning something other than bool, the compiler cannot automatically infer an opposite type. Furthermore, in the case where your operator does return bool, it just doesn't make sense for them create generate code that would only exist in that one specific case or, as I said above, code that hides the default behavior of the CLR.


C# borrows much from C++3

When C# was introduced, there was an article in MSDN magazine that wrote, talking about C#:

Many developers wish there was a language that was easy to write, read, and maintain like Visual Basic, but that still provided the power and flexibility of C++.

Yes the design goal for C# was to give nearly the same amount of power as C++, sacrificing only a little for conveniences like rigid type-safety and garbage-collection. C# was strongly modeled after C++.

You may not be surprised to learn that in C++, the equality operators do not have to return bool, as shown in this example program

Now, C++ does not directly require you to overload the complementary operator. If your compiled the code in the example program, you will see it runs with no errors. However, if you tried adding the line:

cout << (a != b);

you will get

compiler error C2678 (MSVC) : binary '!=' : no operator found which takes a left-hand operand of type 'Test' (or there is no acceptable conversion)`.

So, while C++ itself doesn't require you to overload in pairs, it will not let you use an equality operator that you haven't overloaded on a custom class. It's valid in .NET, because all objects have a default one; C++ does not.


1. As a side note, the C# standard still requires you to overload the pair of operators if you want to overload either one. This is a part of the standard and not simply the compiler. However, the same rules regarding the determination of which operator to call apply when you're accessing a .net library written in another language that doesn't have the same requirements.

2. EMCA-334 (pdf) (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf)

3. And Java, but that's really not the point here

薄荷→糖丶微凉 2024-12-04 10:43:15

可能是因为有人需要实现三值逻辑(即null)。在这种情况下(例如 ANSI 标准 SQL),不能简单地根据输入来否定运算符。

您可能会遇到这样的情况:

var a = SomeObject();

并且 a == true 返回 false 并且 a == false 也返回 false

Probably for if someone needs to implement three-valued logic (i.e. null). In cases like that - ANSI standard SQL, for instance - the operators can't simply be negated depending on the input.

You could have a case where:

var a = SomeObject();

And a == true returns false and a == false also returns false.

傾城如夢未必闌珊 2024-12-04 10:43:15

除了 C# 在许多领域遵循 C++ 之外,我能想到的最好的解释是,在某些情况下,您可能希望采用稍微不同的方法来证明“不相等”与证明“相等” ”。

显然,例如,通过字符串比较,您可以测试是否相等,并在看到不匹配的字符时从循环中返回。然而,对于更复杂的问题,它可能不会那么干净。我想到了 bloom 过滤器;快速判断元素是否不在集合中非常容易,但很难判断元素是否在集合中。虽然可以应用相同的 return 技术,但代码可能不那么漂亮。

Other than that C# defers to C++ in many areas, the best explanation I can think of is that in some cases you might want to take a slightly different approach to proving "not equality" than to proving "equality".

Obviously with string comparison, for example, you can just test for equality and return out of the loop when you see nonmatching characters. However, it might not be so clean with more complicated problems. The bloom filter comes to mind; it's very easy to quickly tell if the element is not in the set, but difficult to tell if the element is in the set. While the same return technique could apply, the code might not be as pretty.

哥,最终变帅啦 2024-12-04 10:43:15

如果您查看 .net 源代码中 == 和 != 重载的实现,您会发现它们通常不会将 != 实现为 !(left == right)。他们用否定逻辑完全实现它(如 ==)。例如,DateTime 实现 == as

return d1.InternalTicks == d2.InternalTicks;

和 != as

return d1.InternalTicks != d2.InternalTicks;

如果您(或编译器,如果它隐式执行了它)要实现 != as

return !(d1==d2);

那么您就对 == 和 != 在您的事物中的内部实现做出了假设类正在引用。避免这种假设可能是他们决定背后的哲学。

If you look at implementations of overloads of == and != in the .net source, they often don't implement != as !(left == right). They implement it fully (like ==) with negated logic. For example, DateTime implements == as

return d1.InternalTicks == d2.InternalTicks;

and != as

return d1.InternalTicks != d2.InternalTicks;

If you (or the compiler if it did it implicitly) were to implement != as

return !(d1==d2);

then you are making an assumption about the internal implementation of == and != in the things your class is referencing. Avoiding that assumption may be the philosophy behind their decision.

画离情绘悲伤 2024-12-04 10:43:15

要回答您的编辑,关于为什么如果您覆盖其中一个就必须覆盖两者,这一切都在继承中。

如果您重写 ==,很可能提供某种语义或结构相等(例如,如果 DateTimes 的 InternalTicks 属性相等,即使它们可能是不同的实例,则 DateTimes 是相等的),那么您将更改运算符的默认行为对象,它是所有 .NET 对象的父对象。在 C# 中,== 运算符是一种方法,其基本实现 Object.operator(==) 执行引用比较。 Object.operator(!=) 是另一种不同的方法,它也执行引用比较。

在几乎任何其他方法重写的情况下,假设重写一种方法也会导致反义方法的行为改变是不合逻辑的。如果您使用 Increment() 和 Decrement() 方法创建了一个类,并在子类中覆盖了 Increment(),您是否期望 Decrement() 也会被与您覆盖的行为相反的行为覆盖?编译器无法足够智能,无法在所有可能的情况下为运算符的任何实现生成反函数。

然而,尽管操作符的实现方式与方法非常相似,但它们在概念上是成对工作的。 == 和 !=, <和>,以及<=和>=。在这种情况下,从消费者的角度来看,认为 != 的工作方式与 == 有任何不同是不合逻辑的。因此,编译器不能在所有情况下都假设 a!=b == !(a==b),但通常期望 == 和 != 应该以类似的方式操作,因此编译器强制你成对实施,但你实际上最终这样做了。如果对于您的类,a!=b == !(a==b),则只需使用 !(==) 实现 != 运算符,但如果该规则并不适用于您的对象的所有情况(例如,如果与特定值(相等或不相等)进行比较是无效的),那么你必须比 IDE 更聪明。

真正应该问的问题是为什么<和>和 <= 和 >= 是必须同时实现的比较运算符对,当用数字表示时 !(a < b) == a >= b 和 !(a > b) == a < = b.如果您重写其中一个,则应该要求您实现所有四个,并且可能还应该要求您重写 == (和 !=),因为 (a <= b) == (a == b) 如果 a 是语义上等于 b.

To answer your edit, regarding why you are forced to override both if you override one, it's all in the inheritance.

If you override ==, most likely to provide some sort of semantic or structural equality (for instance, DateTimes are equal if their InternalTicks properties are equal even through they may be different instances), then you are changing the default behavior of the operator from Object, which is the parent of all .NET objects. The == operator is, in C#, a method, whose base implementation Object.operator(==) performs a referential comparison. Object.operator(!=) is another, different method, which also performs a referential comparison.

In almost any other case of method overriding, it would be illogical to presume that overriding one method would also result in a behavioral change to an antonymic method. If you created a class with Increment() and Decrement() methods, and overrode Increment() in a child class, would you expect Decrement() to also be overridden with the opposite of your overridden behavior? The compiler can't be made smart enough to generate an inverse function for any implementation of an operator in all possible cases.

However, operators, though implemented very similarly to methods, conceptually work in pairs; == and !=, < and >, and <= and >=. It would be illogical in this case from the standpoint of a consumer to think that != worked any differently than ==. So, the compiler can't be made to assume that a!=b == !(a==b) in all cases, but it's generally expected that == and != should operate in a similar fashion, so the compiler forces you to implement in pairs, however you actually end up doing that. If, for your class, a!=b == !(a==b), then simply implement the != operator using !(==), but if that rule does not hold in all cases for your object (for instance, if comparison with a particular value, equal or unequal, is not valid), then you have to be smarter than the IDE.

The REAL question that should be asked is why < and > and <= and >= are pairs for comparative operators that must be implemented concurrently, when in numeric terms !(a < b) == a >= b and !(a > b) == a <= b. You should be required to implement all four if you override one, and you should probably be required to override == (and !=) as well, because (a <= b) == (a == b) if a is semantically equal to b.

顾挽 2024-12-04 10:43:15

如果您为自定义类型重载 ==,而不是 !=,那么它将由 object != object 的 != 运算符处理,因为所有内容都派生自 object,这与 CustomType != CustomType 有很大不同。

此外,语言创建者可能希望这种方式能够为编码人员提供最大的灵活性,并且这样他们就不会对您打算做什么做出假设。

If you overload == for your custom type, and not != then it will be handled by the != operator for object != object since everything is derived from object, and this would be much different than CustomType != CustomType.

Also the language creators probably wanted it this way to allow the most most flexibility for coders, and also so that they are not making assumptions about what you intend to do.

鹊巢 2024-12-04 10:43:15

这是我首先想到的:

  • 如果测试不等式比测试相等快得多怎么办?
  • 如果在某些情况下您想返回 false 两者 < code>== 和 != (即,如果由于某种原因无法比较它们)

This is what comes to my mind first:

  • What if testing inequality is much faster than testing equality?
  • What if in some cases you want to return false both for == and != (i.e. if they can't be compared for some reason)
时光是把杀猪刀 2024-12-04 10:43:15

您问题中的关键词是“为什么”和“必须”。

结果:

这样回答是因为他们设计的就是这样,这是真的……但没有回答问题的“为什么”部分。

回答有时独立地覆盖这两个问题可能会有所帮助,这是正确的……但没有回答问题的“必须”部分。

我认为简单的答案是,没有有任何令人信服的理由说明为什么 C#要求您覆盖两者。

该语言应该只允许您重写 ==,并为您提供 != 的默认实现,即 ! 。如果您碰巧也想覆盖 != ,那就这么做吧。

这不是一个好的决定。人类设计语言,人类并不完美,C# 也不完美。耸肩和 QED

The key words in your question are "why" and "must".

As a result:

Answering it's this way because they designed it to be so, is true ... but not answering the "why" part of your question.

Answering that it might sometimes be helpful to override both of these independently, is true ... but not answering the "must" part of your question.

I think the simple answer is that there isn't any convincing reason why C# requires you to override both.

The language should allow you to override only ==, and provide you a default implementation of != that is ! that. If you happen to want to override != as well, have at it.

It wasn't a good decision. Humans design languages, humans aren't perfect, C# isn't perfect. Shrug and Q.E.D.

素衣风尘叹 2024-12-04 10:43:15

好吧,这可能只是一个设计选择,但正如您所说,x!= y 不必与 !(x == y) 相同。通过不添加默认实现,您可以确定您不会忘记实现特定的实现。如果它确实像你说的那么微不足道,你可以使用另一个来实现一个。我不明白这是“糟糕的做法”。

C# 和 Lua 之间可能还存在一些其他差异......

Well, it's probably just a design choice, but as you say, x!= y doesn't have to be the same as !(x == y). By not adding a default implementation, you are certain that you cannot forget to implement a specific implementation. And if it's indeed as trivial as you say, you can just implement one using the other. I don't see how this is 'poor practise'.

There may be some other differences between C# and Lua too...

江挽川 2024-12-04 10:43:15

只是为了补充这里的优秀答案:

考虑当您尝试进入 != 运算符并最终得到 = 时,调试器中会发生什么= 运算符代替!谈论令人困惑!

CLR 允许您自由地省略一个或其他运算符是有道理的 - 因为它必须适用于多种语言。但是,有很多 C# 未公开 CLR 功能的示例(例如,ref 返回和局部变量),并且有很多实现 CLR 本身没有的功能的示例(例如:using,<代码>锁定,<代码>foreach等)。

Just to add to the excellent answers here:

Consider what would happen in the debugger, when you try to step into a != operator and end up in an == operator instead! Talk about confusing!

It makes sense that CLR would allow you the freedom to leave out one or other of the operators - as it must work with many languages. But there are plenty of examples of C# not exposing CLR features (ref returns and locals, for example), and plenty of examples of implementing features not in the CLR itself (eg: using, lock, foreach, etc).

神魇的王 2024-12-04 10:43:15

编程语言是极其复杂的逻辑语句的语法重新排列。考虑到这一点,您可以定义平等的情况而不定义不平等的情况吗?答案是否定的。要使对象 a 等于对象 b,则对象 a 不等于 b 的逆也必须为真。证明这一点的另一种方法是

if a == b then !(a != b)

这为语言提供了确定对象相等性的明确能力。例如,比较 NULL != NULL 可能会破坏未实现非相等语句的相等系统的定义。

现在,关于 != 只是可替换定义的想法,如

if !(a==b) then a!=b

我无法反驳这一点。然而,这很可能是 C# 语言规范组的决定,迫使程序员一起显式定义对象的相等性和不相等性

Programming languages are syntactical rearrangements of exceptionally complex logical statement. With that in mind, can you define a case of equality without defining a case of non-equality? The answer is no. For an object a to be equal to object b, then the inverse of object a does not equal b must also be true. Another way to show this is

if a == b then !(a != b)

this provides the definite ability for the language to determine the equality of objects. For instance, the comparison NULL != NULL can throw a wrench into the definition of a equality system that does not implement a non-equality statement.

Now, in regards to the idea of != simply being replaceable definition as in

if !(a==b) then a!=b

I can't argue with that. However, it was most likely a decision by the C# language specification group that the programmer be forced to explicitly define the equality and and non-equality of an object together

带上头具痛哭 2024-12-04 10:43:15

简而言之,强制一致性。

'==' 和 '!=' 始终是真正的对立面,无论您如何定义它们,通过它们对“等于”和“不等于”的口头定义来定义它们。仅定义其中之一,您就会面临相等运算符不一致的情况,其中对于两个给定值,“==”和“!=”可能都为 true 或都为 false。您必须定义两者,因为当您选择定义一个时,您还必须适当地定义另一个,以便明确您对“平等”的定义是什么。编译器的另一个解决方案是只允许您覆盖“==”或“!=”,并让另一个本质上否定另一个。显然,C# 编译器的情况并非如此,我确信这有充分的理由,这可能严格归因于简单性的选择。

您应该问的问题是“为什么我需要覆盖运算符?”这是一个强有力的决定,需要强有力的推理。对于对象,'==' 和 '!=' 通过引用进行比较。如果您要覆盖它们以不通过引用进行比较,则会造成一般运算符不一致,而这对于任何其他仔细阅读该代码的开发人员来说并不明显。如果您试图问“这两个实例的状态是否相等?”,那么您应该实现 IEquatible,定义 Equals() 并利用该方法调用。

最后,出于同样的原因,IEquatable() 没有定义 NotEquals():可能会导致相等运算符不一致。 NotEquals() 应该始终返回 !Equals()。通过向实现 Equals() 的类开放 NotEquals() 的定义,您将再次强制解决确定相等性时的一致性问题。

编辑:这只是我的推理。

In short, forced consistency.

'==' and '!=' are always true opposites, no matter how you define them, defined as such by their verbal definition of "equals" and "not equals." By only defining one of them, you open yourself up to an equality operator inconsistency where both '==' and '!=' can both be true or both be false for two given values. You must define both since when you elect to define one, you must also define the other appropriately so that it is blatantly clear what your definition of "equality" is. The other solution for the compiler is to only allow you to override '==' OR '!=' and leave the other as inherently negating the other. Obviously, that isn't the case with the C# compiler and I'm sure there's a valid reason for that that may be attributable strictly as a choice of simplicity.

The question you should be asking is "why do I need to override the operators?" That is a strong decision to make which requires strong reasoning. For objects, '==' and '!=' compare by reference. If you are to override them to NOT compare by reference, you are creating a general operator inconsistency that is not apparent to any other developer who would peruse that code. If you are attempting to ask the question "is the state of these two instances equivalent?," then you should implement IEquatible, define Equals() and utilize that method call.

Lastly, IEquatable() does not define NotEquals() for the same reasoning: potential to open up equality operator inconsistencies. NotEquals() should ALWAYS return !Equals(). By opening up the definition of of NotEquals() to the class implementing Equals(), you are once again forcing the issue of consistency in determining equality.

Edit: This is simply my reasoning.

可能只是他们没有想到没有时间做的事情。

当我重载==时我总是使用你的方法。然后我就在另一处使用它。

你是对的,只需少量工作,编译器就可以免费将其提供给我们。

Probably just something they didn't think of of didn't have time to do.

I always use your method when I overload ==. Then I just use it in the other one.

You're right, with a small amount of work, the compiler could give this to us for free.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文