2

In searching various terms of the "C# Language Specification, Version 5.0" there appears to be no explicit statement that the LHS and RHS of a comparison must be either of the same type, or be able to be explicitly convertible, or via a cast be explicitly convertible.

Words and phrases searched include conversion, cast, implicit, explicit, if statement, et cetera.

Intuitively, one does not compare apples to oranges; regardless, surprisingly (to me), AFAIK this is not explicitly stated in the C# Language Specification.

Veve
  • 6,643
  • 5
  • 39
  • 58
gerryLowry
  • 2,626
  • 5
  • 35
  • 44
  • It is possible to define custom comparison operators that do not use the same types for both arguments, so I doubt there is any explicit restriction. All the builtin comparisons are of the same type and builtin conversions are always available. – Mike Zboray Dec 03 '15 at 18:16
  • You may be asking about the behavior of the builtin equality operators which have the following restriction: "It is a binding-time error to use the predefined reference type equality operators to compare two references that are known to be different at binding-time. For example, if the binding-time types of the operands are two class types A and B, and if neither A nor B derives from the other, then it would be impossible for the two operands to reference the same object. Thus, the operation is considered a binding-time error." – Mike Zboray Dec 03 '15 at 18:22

3 Answers3

5

Does the C# Language Specification explicitly state that comparison must be of the same type?

No. Comparison operators -- < <= > >= == != -- need not have operands of the same type on both sides. Indeed, the operand need not have a type. foo >= null is perfectly legal, but null does not have a type.

Intuitively, one does not compare apples to oranges; regardless, surprisingly (to me), AFAIK this is not explicitly stated in the C# Language Specification.

Does intuitively one expect to be able to compare ints to shorts, shorts to decimals, and so on? Does one expect to be able to compare nullable ints to ints? If a manager is a kind of person, does it make sense to be able to ask "is this manager and this person the same?" I think it is very intuitively obvious that the operands of a comparison operator need not be the same type, either at compile time or at run time.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    Well one could argue that you *always* end up comparing same types. C# does not enforce this in anyway, but the way `IComparable` and *operators* are implemented throughout the framework is consistent with this behaviour; `IComparable.CompareTo(object obj)` throws an `ArgumentException` when the argument is not the callee's type and overload resolution simply fails with operators; `int i = 1; ulong u = 1; var c = i > u; //operator cannot be applied to types...`. The point being that both arguments need to be implcitly convertible to a common type that can perform the comparison. – InBetween Dec 04 '15 at 20:59
  • 2
    @InBetween: Though I take your point, you seem to be reasoning from the particular to the general. The fact that no built-in C# comparison operator has operands of different types is just a fact about the history of the language, not a restriction on the design space. If tomorrow the design team wanted to add an operator that numerically compared ulongs to longs it could do so very easily without having to find a type larger than both ulong and long. – Eric Lippert Dec 04 '15 at 23:01
  • I agree, hence why I made it clear that the language does not enforce this. I was simply stating that the OP's intuition is not that farfetched becase as the framework stands and works right now, he is, in a sense, right. – InBetween Dec 05 '15 at 00:26
  • @EricLippert given `short shortNumber = 12;` with `long valueNumber = 89;`, `if (shortNumber > valueNumber)...` in IL one sees BOTH literals processed with `conv.i8` ~~ i.e., the *same* types are compared via implicit conversion. `Warning CS0464` basically says that "*foo >= null* is not a valid comparison since the result is always false". Therefore, it seems fair to state that comparisons *ultimately* must be *of the same type* even though the c# Language Specification doe not appear to state that explicitly. – gerryLowry Dec 12 '15 at 08:01
  • @gerryLowry: IL is an implementation detail; facts about the IL language are completely irrelevant. If you want to know why comparing a short to a long converts both operands to long then you can just read the C# specification, which says that. I do not see how you deduce from that though that comparison operands must be of the same type. There are easy counterexamples to your claim; just make a user-defined comparison operator that compares apples to giraffes. – Eric Lippert Dec 12 '15 at 15:13
  • @EricLippert Explanation FWIW ~~ the c# language specification is a map for what well-formed IL (or the output of any c# compiler) is expected to deliver with a properly conforming implementation; i.e., the specification drives the IL, not the other way around. That stated, reverse engineering IL ought to be sufficient to correctly derive the specification, at least in part. Creationists can provide counterexamples to evolution; counterexamples do not necessarily qualify as a proof. `Comparer.Compare Method (T, T)` is expected to perform "a comparison of two objects of the same type". – gerryLowry Dec 13 '15 at 03:49
4

In the specification found here, I found on page 344 the quote

The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters.

The part to note is that "types" is pluralized. In C#, I view binary operators as being similar to methods that take two parameters and returns a value of a given type. The return type must be defined by the types of the two input parameters.

UPDATE:

I decided to add some more, since I find it interesting. In this Eric Lippert Blog Post, he says the following.

[...] it is legal and surprisingly common for a class to implement == and Equals inconsistently.

His entire post is basically how C# defines a whole lot of ways to compare two objects, and nowhere in the specification does it define that they should all behave consistently. Among other things, this means that you can define your own classes A and B such that (a as A) == (b as B) and (a as A) != (b as B) both evaluate to the same thing.

This just adds a little more punch behind my comment earlier that binary operators are really a lot like methods in this regard. The C# language doesn't give a specific meaning to them, even though we as people find this confusing.

Frank Bryce
  • 8,076
  • 4
  • 38
  • 56
3

Because it is a valid comparison, the language does not disallow it. In the example below there's no way nor need to convert an A to a B or vice-versa, but the comparison code still valid. Maybe unreasonable logic-wise, but valid compiler-wise.

class A {
    public int a = 5;

    public static bool operator==(A a, B b) {
        return a.a == b.b;
    }
    public static bool operator!=(A a, B b) {
        return !(a == b);
    }
}
class B {
    public int b = 5;
}
// ...
Console.WriteLine(new A() == new B()); // "true"
Mephy
  • 2,978
  • 3
  • 25
  • 31