13

I thought this method was valid but I was wrong:

static void Equals<T>(T x, T y)
{
    return x == y;    //operator == can't be applied to type T
}

After reading the specifiation (§7.2.4 in v3.0 and §7.3.4 in v4.0):

7.2.4 Binary operator overload resolution

An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §7.2.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.

  • If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (§7.7 through §7.11).

  • The overload resolution rules of §7.4.3 are applied to the set of candidate operators to select the best operator with respect to the argument list (x, y), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a compile-time error occurs.

In step 2 I think this predefined implementation should be applied:

bool operator ==(object x, object y);
bool operator !=(object x, object y);

since everything in C# derives from Object. How can a compile-time error occurs in step 3? I don't think it's possible that "overload resolution fails to select" in this case.

EDIT The question came to my mind when I was implementing something like this:

class EnumComparer<TEnum> : IEqualityComparer<TEnum>
{
    public bool Equals(TEnum x, TEnum y)
    {
        return x == y;
    }
    public int GetHashCode(TEnum obj)
    {
        return (int)obj;
    }
}

I'm afraid I need to build a expression and invoke it dynamicly in Equals method.

Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
  • possible duplicate of [c# compare two generic values](http://stackoverflow.com/questions/488250/c-compare-two-generic-values) – Kirk Woll Apr 27 '11 at 17:29
  • FWIW, `(object)x == (object)y` is valid -- but consider `(object)1 == (object)1` as a reason for not desiring this. Just need to help `T` along to a non-generic. –  Apr 27 '11 at 17:33
  • 1
    The discussion is a bit abstract, but Eric Lippert has pointed out that it is a general misconception that everything *derives* from `Object`. http://blogs.msdn.com/b/ericlippert/archive/2009/08/06/not-everything-derives-from-object.aspx A relevant point he mentions is that type parameters do not derive from anything. – Dr. Wily's Apprentice Apr 27 '11 at 17:35
  • 1
    @pst: I think `(dynamic)x == (dynamic)y` is what you'd want here. Otherwise it just does a reference equality. – Gabe Apr 27 '11 at 17:36
  • Right; type parameters are compile-time types that are always *convertible* to object, but it's a stretch to say that they have any sort of "inheritance" relationship. – Eric Lippert Apr 27 '11 at 17:37
  • 2
    I'm somewhat confused; why is the built-in implementation of equality on enums not sufficient? That is, why not just use the default equality comparer? – Eric Lippert Apr 27 '11 at 18:43
  • @Eric the built in equality compared on enums (at least up to 3.5) is horrid in performance terms in that it allocates each time. We've had to adults our own generic one in C++/CLI to do reinterpret casts to get decent performance when using generics which are enums. – ShuggyCoUk Apr 29 '11 at 13:24
  • it appears that the BCL designers 'fixed' this in 4.0 which is great, but only for the int cases, which leaves us in the same situation, but at least C++/CLI lets you do pretty anything you want if you're happy with the risk. – ShuggyCoUk Apr 29 '11 at 16:41
  • @ShuggyCoUk: You are correct. That's why I asked another question: http://stackoverflow.com/questions/5829441/equalitycomparert-default-isnt-clever-enough – Cheng Chen Apr 30 '11 at 02:02

4 Answers4

21

Good for you for reading the spec, but you stopped reading too soon. Had you read further you would have gotten to this bit:


The predefined reference type equality operators require one of the following:

  • Both operands are a value of a type known to be a reference-type or the literal null. Furthermore, an explicit reference conversion exists from the type of either operand to the type of the other operand.

  • One operand is a value of type T where T is a type-parameter and the other operand is the literal null. Furthermore T does not have the value type constraint.

Unless one of these conditions are true, a binding-time error occurs. (*)


The error isn't from overload resolution; the error is that overload resolution would have chosen the predefined reference type equality operator, and you don't have reference types.

Consider your code. What stops T from being a value type with no equality operator defined on it? Nothing. Suppose we fell back to the object version; both operands would box to different locations and therefore be reference-unequal, even if they had the same content. Since that is slow, confusing and wrong, it is illegal to even try.

Why are you trying to do this thing in the first place? If your method worked, which it doesn't, then your method would be worse than simply using == in the first place. What is the value you intend to add to the world with this method?


(*) I've reported the grammatical error in this sentence to the spec maintainers.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
9

That would possibly work if it knew that where T : class, doing a reference comparison. Operators generally have very little support with generics, but there are workarounds. MiscUtil offers indirect support for operators on generics, otherwise EqualityComparer<T>.Default.Equals(x,y) is a good choice.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks! I just want to implement a comparer for enums. See my edit. – Cheng Chen Apr 27 '11 at 18:32
  • 4
    @Danny - try `EqualityComparer.Default.Equals(x,y)` then – Marc Gravell Apr 27 '11 at 18:41
  • LOL!I was in the wrong direction! Thanks very much for the suggestion! – Cheng Chen Apr 28 '11 at 03:02
  • Hi Marc, the implementation of EqualityComparer.Default.Equals(x,y) looks like `x == y` or `x.Equals(y)`?(the source code didn't tell me).The latter will cause a boxing right? – Cheng Chen Apr 28 '11 at 04:02
  • @Danny no boxing if it implements `IEquatable` - but I've just checked, and annoying this isn't the case for the enum. Grrr! I could write one that works via meta-programming if you really want (some ILGenerator action) – Marc Gravell Apr 28 '11 at 05:27
  • @Marc: A related question of mine: http://stackoverflow.com/questions/5829441/equalitycomparert-default-isnt-clever-enough – Cheng Chen Apr 29 '11 at 07:51
  • @Marc, dropping to C++/CLI lets you do a nice switch in the static constructor based on the backing type and then use reinterpret casts even on generic type variables. c# won't let you directly. We found that easier than codegen... – ShuggyCoUk May 01 '11 at 17:33
1

I like using EqualityComparer<T>.Default for this.

It is based on the overridden Equals method, but uses IEquatable<T> when available, avoiding boxing on value types implementing it.

EqualityComparer<T>.Default.Equals(x, y)
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
-1

use .Equals() method and be sure that the T implement IComparable

Maged Samaan
  • 1,742
  • 2
  • 19
  • 39