Question

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;
    }

    ...
Was it helpful?

Solution

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.

OTHER TIPS

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.

Where is the default implementation for == op_Equality for value types?

There is no default == for value types. Try writing your own value type

struct JustAnotherValueType
{
  public readonly int Field;
  public JustAnotherValueType(int f) { Field = f; }
}

Make two values x and y of your type. Try saying x == y with them. It will not compile. You can say

(object)x == (object)y

but that will perform boxing on x (let's call it box 1) and boxing on y (box 2) and then perform a reference comparison on the two boxes. Hence it will always return false. And therefore it's useless, and that's the reason why the overload ==(object x, object y) is not automatically selected when you use == between two JustAnotherValueType.

So what's going on with int and float? You can compare them with == in C#, and they don't have the op_Equality method?! The explanation is that these overloads are defined by the C# Language Specification. See the sections Integer comparison operators and Floating-point comparison operators.

So the fact is that while these operators do not exist as members of the .NET structs, the C# Language Specification defines them, and the C# compiler must produce some valid IL to mimic their behavior.

By the way, System.Object does not have an op_Equality either. It too is only defined by C#. See the section Reference type equality operators. Note that System.Object has a ReferenceEquals method that the compiler might choose to translate this overload of == into. Also note that this section of the spec gives limitations so that x == y will never perform boxing on x or y.

What about value types like DateTime, TimeSpan, and Guid that are not mentioned in the C# Specification, but for which it is legal to use ==? The answer is, these do have the op_Equality member.

Conclusion: The C# Language Specification defines a lot of == overloads that an implementation of C# is required to have, and some of these overloads involve int and float and object.

Remark: The virtual instance method Equals(object) is defined in object and inherited by all value types. For structs that don't override this method themselves, the override in System.ValueType is used. It compares by value. So with x and y like above, x.Equals(y) will work fine. Equals is a virtual instance method. On the other hand == is a static "method" for which overload resolution based on the compile-time types of the two operands is performed (no "virtual dispatch").

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

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;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top