359

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.

Stefan Dragnev
  • 14,143
  • 6
  • 48
  • 52
  • 28
    +1 for an excellent question. There doesn't seem to be any reason at all... of course the compiler could assume that a != b can be translated into !(a == b) unless it's told otherwise. I'm curious to know why they decided to do this, too. – Patrick87 Aug 02 '11 at 18:43
  • If it's as simply as `!(left == right)`, it's no problem for you. If the negation is much more complicated, the compiler has no chance to optimize - but you. Hence it's consequent to force you to handle both cases by yourself. – Tim Schmelter Aug 02 '11 at 20:28
  • 3
    The #1 catchall golden rule of designing software, APIs, and GUIs is: **Always make the common case the default!** It looks like someone mistakenly did not do that. – BlueRaja - Danny Pflughoeft Aug 02 '11 at 20:45
  • @BlueRaja: Personally i'm prefering a compiler that does not think too much by itself, otherwise i'm at risk to forget to think by myself when the compiler makes mistakes. – Tim Schmelter Aug 02 '11 at 20:54
  • I think only the people who designed C# can answer this properly, anything else is pretty much just guessing. In fact, I wouldn't even be sure there's a satisfying answer. – luiscubal Aug 02 '11 at 21:04
  • @BlueRaja: the golden rule of designing software, APIs, and GUIs is not necessarily the golden rule of compiler design. Compiler's priority should be to avoid runtime errors, in case of doubt at the cost of programmer's convenience. – Tim Schmelter Aug 02 '11 at 22:55
  • Is it really common sense that != should be the opposite of whatever == is implemented as right now? To me it seems just as much common sense to assume that the default implementation of != should be the opposite of the default implementation of ==, unless overriden. Maybe common sense in this case really isn't common sense at all? – Andreas Ågren Aug 03 '11 at 07:31
  • Maybe it was just caution. If you start with 'operators must come in pairs', you have the ability to switch to 'infer omitted operator partners'. You can't go in the other direction. – Craig Gidney Aug 03 '11 at 23:22

13 Answers13

167

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

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Christopher Currens
  • 29,917
  • 5
  • 57
  • 77
  • 81
    "**you do not have to return bool**" .. I think that's our answer right there; well done. If !(o1 == o2) isn't even well-defined, then you can't expect the standard to rely on it. – Rag Aug 02 '11 at 22:08
  • 13
    Ironically in a topic about logical operators you have used a double-negative in the sentence, "C# does not allow you to not override only one of either" which actually logically means "C# does allow you to override only one of either". – Dan Diplo Aug 03 '11 at 08:21
  • 17
    @Dan Diplo, It's okay, It's not like I'm not prohibited to not to do that or not. – Christopher Currens Aug 03 '11 at 15:51
  • @Dan, I think your first quoted sentence logically means "C# *requires* [not just allows] you to override only one of either". – LarsH Aug 03 '11 at 16:51
  • 8
    If true, I suspect #2 is correct; but now the question becomes, why allow `==` to return anything but a `bool`? If you return an `int`, you can no longer use it as a conditional statement eg. `if(a == b)` will no longer compile. What's the point? – BlueRaja - Danny Pflughoeft Aug 03 '11 at 16:54
  • 3
    You could return an object that has an implicit conversion to bool (operator true/false) but I still fail to see where that would be useful. – Stefan Dragnev Aug 03 '11 at 19:49
  • 2
    @Danny, you can return something other than bool if you want to implement some non-boolean logic. For example, in some fuzzy logic scenarios, you don't know with certainty whether a value is equal to the other, so you can return the *probability* of a value being equal to the other (a double value between 0 and 1). – carlosfigueira Aug 03 '11 at 21:03
  • 2
    @carlosfigueira, but wouldn't you use a method with a descriptive name for that purpose, instead of an operator that may sow confusion? – Stefan Dragnev Aug 04 '11 at 09:27
  • 2
    Ah, so you mean that one poor choice led to another. The first poor choice is having predicates that don't return a boolean. Which leads to the poor choice that we're required override both `==` and `!=`. – Greg Hendershott Aug 04 '11 at 12:24
  • @carlos Would you actually write fuzzy logic code using `==` and `!=`? If you did, would you really inflict that code on someone else? :) Instead I think it would be clearer (and also more C# style vs. C++) to provide a named member like `Truthiness()`. – Greg Hendershott Aug 04 '11 at 12:34
  • @Greg, Stefan, I wouldn't write that myself (I dislike operator overloading in general, even for classes where it's normally accepted, like Complex numbers), but I don't see a reason why people shouldn't be able to express their logic in a "natural" way (i.e., using operators), *if their whole system is based on it* (so the confusion of the operators would be diminished). – carlosfigueira Aug 04 '11 at 14:21
  • 1
    @Greg H - Doubtful it was a poor choice. The decision is based on the same one in C++. Not only can the `operator==` return whatever you want, you are forced to overload the `operator!=` if you try to use it and haven't already done so. We're talking about languages that are supposed to give you as much freedom as possible here. – Christopher Currens Aug 04 '11 at 16:34
  • 1
    @Christopher - Fair enough. Maybe I misunderstand the spirit of C# in contrast to C++, and why someone would want to use one or the other. – Greg Hendershott Aug 04 '11 at 21:09
  • 2
    @Greg H, as an example use case, having `==` or `!=` return something other than boolean allows for infectious error results. E.g. in IEE754, `0.0 / 0.0` is a special `NaN` value which infects all the arithmetic operators; `NaN + anyValue` is `NaN` and similarly for `-`, `*`, and `/`. But that error bit often gets lost unintentionally when values are compared. With this kind of overloading you can do the same kind of infectious error value for logical operators. – Mike Samuel Aug 16 '11 at 02:16
55

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.

Yuck
  • 49,664
  • 13
  • 105
  • 135
  • 1
    I'd say in that case, since `a` isn't a boolean you should be comparing it with a three-state enum, not a real `true` or `false`. – Rag Aug 02 '11 at 18:50
  • 1
    @Brian Gordon: Probably right. My example is maybe a little contrived, but the reasoning behind it still holds I believe. There are cases where you'd not want `!=` to be defined automatically (and immutably) as the logical opposite of `==`. – Yuck Aug 02 '11 at 18:52
  • 1
    @Yuck: But in that case, why doesn't the user define what they mean by "!="? At most, a reasonable response in this case would be to emit a warning if the compilation is pedantic. – Patrick87 Aug 02 '11 at 18:58
  • 19
    By the way, I can't believe nobody's referenced the [FileNotFound](http://thedailywtf.com/Articles/What_Is_Truth_0x3f_.aspx) joke.. – Rag Aug 02 '11 at 19:26
  • 29
    If `a == true` is `false` then I would always expect `a != true` to be `true`, despite `a == false` being `false` – Fede Aug 02 '11 at 20:11
  • @Brian Gordon This isn't the DailyWTF :-) – LarsTech Aug 02 '11 at 20:31
  • if `a == true` is `false` then `a` *is not* `true`. We're not talking about references here (`===`), just binary states. – zzzzBov Aug 02 '11 at 20:52
  • 22
    @Fede hit the nail on the head. **This really has nothing to do with the question** - you're explaining why someone might want to override `==`, not why they should be forced to override both. – BlueRaja - Danny Pflughoeft Aug 02 '11 at 21:10
  • I cannot think of any practical case in which a `!=` and `==` do not give opposite results. – user703016 Aug 02 '11 at 22:09
  • @BlueRaja - Danny Pflughoeft: The OP is asking why the compiler can't default to a "reasonable implementation". My answer is that it isn't always obvious what a "reasonable implementation" might be. – Yuck Aug 02 '11 at 22:10
  • 6
    @Yuck - Yes, there is a good default implementation: it's the common case. Even in your example, the default implementation of `!=` would be correct. In the *extremely* uncommon case where we'd want `x == y` and `x != y` to both be false *(I can't think of a single example that makes sense)*, they could still override the default implementation and provide their own. *Always make the common case the default!* – BlueRaja - Danny Pflughoeft Aug 02 '11 at 22:14
25

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.

Rag
  • 6,405
  • 2
  • 31
  • 38
  • 6
    Great example; I think you're onto the reason why. A simple `!` locks you into a single definition for equality, where an informed coder might be able to optimize **both** `==` and `!=`. – Yuck Aug 02 '11 at 19:05
  • 15
    So this explains why they should be given the *option* to override both, not why they should be *forced* to. – BlueRaja - Danny Pflughoeft Aug 02 '11 at 21:08
  • 1
    In that vein, they don't have to *optimize* both, they just have to *provide a clear definition* of both. – Jesper Aug 04 '11 at 10:46
23

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.

18

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.

KeithS
  • 70,210
  • 21
  • 112
  • 164
15

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.

Chris Mullins
  • 6,677
  • 2
  • 31
  • 40
  • 3
    The flexibility reason can also be applied to a great many features that they decided to leave out, for example http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx .Thus it doesn't explain their choice for the implementation of equality operators. – Stefan Dragnev Aug 03 '11 at 07:04
11

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)
Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
7

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.

Greg Hendershott
  • 16,100
  • 6
  • 36
  • 53
6

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...

GolezTrol
  • 114,394
  • 18
  • 182
  • 210
  • 1
    It's excellent practice implementing one in terms of the other. I've just seen it done otherwise - which is error prone. – Stefan Dragnev Aug 02 '11 at 18:45
  • 3
    That is the problem of the programmer, not C#'s problem. Programmers that fail to implement this right, will fail in other areas too. – GolezTrol Aug 02 '11 at 18:48
  • @GolezTrol: Could you explain that Lua reference? I am not at all familiar with Lua, but you reference seems to hint at something. – zw324 Aug 02 '11 at 18:50
  • @Ziyao Wei: OP points out that Lua does this differently from C# (Lua apparently does add default counterparts for the mentioned operators). Just trying to trivialise such a comparison. If there are no differences at all then at least one of them has not right to exist. Must say I don't know Lua either. – GolezTrol Aug 02 '11 at 18:53
5

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).

Andrew Russell
  • 26,924
  • 7
  • 58
  • 104
  • 1
    This actually seems to me like the worst _relevant_ impact auto-generated operators would have. Don't think this was their reasoning, though. There's other examples where code runs that you didn't explicitly call, like static member initialization of a class you're not working with locally but loading without thinking about it. People easily get used to that after learning about the whys. I don't think people would be too confused why `==` is called when they check for `!=` without defining it. I suppose they'll understand, at the latest after reading the docs about that. – LWChris Aug 21 '20 at 16:24
3

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

Josh
  • 448
  • 2
  • 9
  • `Null` would only be a problem because `null` is a valid input to `==`, which it shouldn't be, though obviously, it's massively convenient. Personally, I just think it allows for vast amounts of vague, sloppy programming. – nicodemus13 May 02 '14 at 21:57
2

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.

Emoire
  • 29
  • 1
  • This is illogical. If the reason were consistency then the opposite would apply: you would only be able to implement `operator ==` and the compiler would infer `operator !=`. After all, this is what it does for `operator +` and `operator +=`. – Konrad Rudolph Aug 05 '11 at 15:14
  • 2
    foo += bar; is a compound assignment, shorthand for foo = foo + bar;. It is not an operator. – blenderfreaky Oct 24 '19 at 06:57
  • 1
    If indeed they _are_ ‘always true opposites’, then that would be a reason _to_ automatically define `a != b` as `!(a == b)`… (which is not what C# does). – andrewf Dec 17 '19 at 15:18
-3

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.

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
Rhyous
  • 6,510
  • 2
  • 44
  • 50