views:

273

answers:

4

I was poking around in .NET Reflector, and noticed that for reference types like "String" for example, there is an explicit overload of the "==" operator:

typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public)

returns: System.Reflection.MethodInfo for the "==" operator.

Due to its implementation, you can't do things like:

if("hi" == 3)  // compiler error, plus code would throw an exception even if it ran)

However, the same thing works for value types:

if((int)1 == (float)1.0)  // correctly returns true
if((int)1 == (float)1.2)  // correctly returns false

I'm trying to figure out exactly how .NET internally handles the type conversion process, so I was looking for the implementation of op_Equality() in .NET Reflector, but "int" doesn't have one.

typeof(int).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public)

returns null.

So, where is the default implementation for the "==" operator for value types? I'd like to be able to call it via reflection:

public bool AreEqual(object x, object y)
{
    if(x.GetType().IsValueType && y.GetType().IsValueType)
        return x == y; // Incorrect, this calls the "object" equality override
    else
        ...
}


Edit #1:

I tried this, but it didn't work:

(int)1 == (float)1;                          // returns true
System.ValueType.Equals( (int)1, (float)1 ); // returns false


Edit #2:

Also tried this, but no love:

object x = (int)1;
object y = (float)1.0;

bool b1 = (x == y);                 // b1 = false
bool b2 = ((ValueType)x).Equals(y); // b2 = false

I beleive this .Equals operator on ValueType does not work due to this type check (ripped from .NET Reflector):

ValueType.Equals(object obj)
{
    ...

    RuntimeType type = (RuntimeType) base.GetType();
    RuntimeType type2 = (RuntimeType) obj.GetType();
    if (type2 != type)
    {
        return false;
    }

    ...
+1  A: 

I'm guessing you're looking for ValueType.Equals(object obj) which is inherited to your structs. It uses reflection to compare all fields.

Simon Svensson
This is the a-typical case. Much more commonly it will do a bit by bit comparison
JaredPar
See my edit#2 above in my original post.
rally25rs
+1  A: 

the evaluation of (int)1 == (float)1.0 doesn't rely on any special == operator, just the conversion rules.

The compiler will turn this into (float)((int)1) == (float)1.0

Edit: The rules are specified on MSDN.

Henk Holterman
Thats getting on the right track. Is there a way to tell what the proper cast should be? For example how does it know to cast the result to float instead of int? there has to be an order of precedence somewhere...
rally25rs
This is correct. If you code C# (int)1 == (float)1.0; The compiler will generate the IL equivalent of (float)1.0 == (float)1.0; It upcasts the int at compile time, so at runtime the types are equal.
rally25rs
+2  A: 

What you're looking for is ValueType.Equals

In many cases this just does a bit by bit comparison of the values. In certain cases though it will use reflection to verify the fields.

EDIT

You're confusing how C# compares value types and how .Net compares value types. ValueType.Equals is a function in .Net used to compare value type objects which have the same type. C# will emit code that eventually calls that function. But it does not call it with an "int" and a "float". Instead it first converts both of them to a type which does not loose precision for either value (double) and then compares the resulting double values. This is why you see a difference in behavior.

JaredPar
I tried that, but it did not work as expected. ValueType.Equals((int)1, (float)1); returns false, the opposite of: (int)1 == (float)1;
rally25rs
@rally25rs, the code you wrote shoudln't compile AFAICT. ValueType.Equals is an instance method and only takes 1 parameter
JaredPar
I just noticed there is a ValueType.Equals(object, object) that is inherited from Object, and a ValueType.Equals(object) that is on ValueType. I was calling the wrong one. oops! :)
rally25rs
That still didn't work for me, see edit #2 above.
rally25rs
+1  A: 

My answer to another question provides the Rotor implementation. The actual code is implemented in the CLR as native code.

Specifically:

// Compare the contents (size - vtable - sink block index).
BOOL ret = memcmp(
    (void *) (pThisRef+1), 
    (void *) (pCompareRef+1), 
    pThisRef->GetMethodTable()->GetBaseSize() - sizeof(Object) - sizeof(int)) == 0;
Mehrdad Afshari