views:

256

answers:

4

What happens if "== operator is not defined"?

Example:

class a
{
    int variable = 0;
}
class b
{
    void proc()
    {
        a ref1 = new a();
        a ref2 = new a();
        bool cmp1 = ref1 == ref2;//?
        bool cmp2 = ref1 == ref1;//?
    }
}

Does it differ when working with structs?

How about marshaled (System.Runtime.Remoting.*) objects (singletons)?

+1  A: 

It will likely compare pointers 'a' and 'b', whether they point to the same object in memory.

If you need to compare fields of those objects, you'll have to define the comparer function.

You'll need to inherit from IComparable interface and define the CompareTo method.

Look here: IComparable Interface

Developer Art
and what happens to marshaled objects?
Behrooz
No, you don't have to implement IComparable for equality checks. The steps you should take are outlined here: http://msdn.microsoft.com/en-us/library/dd183755.aspxYou need to implement a `IComparer<T>` or `IComparable<T>` if you want to define a total order of the elements, i.e. you want to sort/order them.
andras
+1  A: 

From MSDN:

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.

Justin Ethier
And user defined value types?
Aistina
The default implementation of `==` will return nothing for user defined value types, since there is no default implementation of the operators "==" and "!=" for user defined value types. It won't compile. -1
andras
@andras is right. This answer is incorrect.
Eric Lippert
+1  A: 

When == is not overridden, I believe it compares references, checking if they are the same object.

Example:

MyClass a = new MyClass(1);
MyClass b = new MyClass(1);
MyClass c = a;

if (a == b) // false
    ...
if (a == c) // true
    ...

So therefore, in your code above, cmp1 would be false, but cmp2 would be true

For user defined value types, however, it compares the actual value of the type.

Charlie Somerville
No, it will not compare the actual value. `==` and `!=` are not defined for user defined value types. You might be deceived into thinking that these operators will work like `Equals()`, which does a field-by-field comparison with reflection on user defined structs. This, however, is not the case.
andras
+4  A: 

For user defined value types, your code won't compile.

Specifically, it would fail compilation with the following error: "Operator '==' cannot be applied to operands of type 'a' and 'a' ".

"The == and != operators cannot operate on a struct unless the struct explicitly overloads them."

You have to overload both of them. You most probably do not want to utilize the default Equals() in your method, since "...for structs, the default implementation of Object.Equals(Object) (which is the overridden version in System.ValueType) performs a value equality check by using reflection to compare the values of every field in the type. When an implementer overrides the virtual Equals method in a stuct, the purpose is to provide a more efficient means of performing the value equality check and optionally to base the comparison on some subset of the struct's field or properties."

For user defined reference types (simplified case, as in the OP's example):

"The == and != operators can be used with classes even if the class does not overload them. However, the default behavior is to perform a reference equality check. In a class, if you overload the Equals method, you should overload the == and != operators, but it is not required."

If you do not overload the operators, there will most probably only be a reference equality test.

"Simplified case", because operator overload resolution might select another implementation instead of the default.

//Minimal example, for demonstration only.
//No Equals(), GetHaschode() overload, no IEquatable<T>, null checks, etc..
class Program
{
    static void Main()
    {

        MyMoreDerived a = new MyMoreDerived() { fbase = 1, fderived = 3 };
        MyMoreDerived b = new MyMoreDerived() { fbase = 2, fderived = 3 };

        //Even though MyMoreDerived does not overload the operators, this
        //will succeed - the definition in MyDerived will be used.
        if (a == b)
        {
            //Reached, because the operator in MyDerived is used.
            Console.WriteLine("MyDerived operator used: a == b");
        }

        a.fderived = 2;
        b.fbase = 1;
        //a => {1, 2} 
        //b => {1, 3}
        //Since 2 != 3, the operator in MyDerived would return false.
        //However only the operator in MyBase will be used.
        if ((MyBase)a == (MyBase)b)
        {
            //Reached, because the operator in MyBase is used.
            Console.WriteLine("MyBase operator used: a == b");
        }

        b.fderived = 2;
        //a => {1, 2} 
        //b => {1, 2}
        //Now both operator definitions would compare equal,
        //however they are not used.
        if ((object)a != (object)b)
        {
            //Reached, because the default implementation is used
            //and the references are not equal.
            Console.WriteLine("Default operator used: a != b");
        }

    }

    class MyBase
    {
        public int fbase;

        public static bool operator ==(MyBase x, MyBase y)
        {
            return x.fbase == y.fbase;
        }

        public static bool operator !=(MyBase x, MyBase y)
        {
            return x.fbase != y.fbase;
        }

    }

    class MyDerived : MyBase
    {
        public int fderived;

        public static bool operator ==(MyDerived x, MyDerived y)
        {
            return x.fderived == y.fderived;
        }

        public static bool operator !=(MyDerived x, MyDerived y)
        {
            return x.fderived != y.fderived;
        }

    }

    class MyMoreDerived : MyDerived
    {
    }

}

Singletons are most meaningful in the context of reference types and their purpose is to return one specific instance. I cannot imagine a reasonable case where the reference is the same but the object is not "equal" to itself.

Even with remoting it is best practice to separate operation contracts from data contracts. The former will typically be implemented by MarshalByRefObjects on the server side - implementing operations defined by interfaces - while the latter with data/message classes that are marshaled by value and might be shared by the client and server. It might not be a big problem if you overload the operators in data classes. These, however should not reference/make calls to remote objects, I believe.

Even if you provide a custom client proxy that overloads the operators, imho it is a really bad practice and a debugging nightmare to hide remoting calls behind == and != operators. (If I understand your intentions, that I am not sure of.)

andras