Are Equals and == equal?

Hello!

In C# all types can be split into reference and value types. This implies that there is a need for two different types of equality comparison mechanisms. Value and referential equality exist to allow us compare value and reference types. By default, value types use the value equality, and reference types rely on referential equality.

Before I proceed in comparing these equality types, let me try getting your attention with a simple example. Which of the two methods below would you rather have, if you were worried about run-time stability?

public static bool Equals1(double? a, double? b)
     {
        if (a == b)
            return true;
         else
            return false;
      }

public static bool Equals2(object a, object b)
     {
        if (a.Equals(b))
            return true;
        else
            return false;
     }

The acceptable answer is another question – it depends on what you are trying to achieve and the context of the task at hand. However, Equals2 is a small but a potentially dangerous function. If object a is null, we get a Null Reference Exception, as in the following example. The == comparison, on the other hand, will never fail so badly.

The reason for the behavior difference lies in the main difference between how == and object.Equals are implemented internally. The first is a static operator (or a function, if you like), and the second is a virtual method (remember you can override Equals?). Since dLeft is null, it cannot be de-referenced to call an Equals method on. The == operator, being a static function, resolves the type of dLeft at compilation. In our example, it is a boxed double (yes, nullable value types come at a cost). But even though we boxed our doubles, we get value type comparison in Equals1.

So, do we always have to check for null before calling Equals on them? Not always, as we can utilize the static helper object.Equals(object,object) instead. This method accepts two arguments and lets us avoid having to check for equality on a potentially null reference. Here is a much less dangerous Equals2 for you:

public static bool Equals2(object a, object b)
       {
            if (object.Equals(a,b))
                return true;
            else
                return false;
       }

To continue comparing ‘==’ with Equals, note that the latter is reflexive, meaning that two NaNs are equal according to Equals, and are different according to ‘==’. Other than execution speed, with value types ‘==’ and Equals behave the same. With reference types, the default behavior of Equals is value-type equality and with ‘==’ operator it is referential equality. This can be demonstrated with a simple example:

double left = 10;
double right = left;
double middle = 10;

if (right.Equals(middle))
    Console.WriteLine("Same doubles");
else
    Console.WriteLine("Diff doubles");

if (right==middle)
    Console.WriteLine("Same doubles");
else
    Console.WriteLine("Diff doubles");

object oLeft = 10;
object oRight = oLeft;
object oMiddle = 10;

if (oRight.Equals(oMiddle))
    Console.WriteLine("Same objects");
else
    Console.WriteLine("Diff objects");

if (oRight == oMiddle)
    Console.WriteLine("Same objects");
else
Console.WriteLine("Diff objects");

The above will print that two variables are the same in the first 3 cases. In the last case, by default, ‘==’ for reference types performs referential equality comparison. Finally, if you are into numerical comparisons, I would recommend sticking with ‘==’ since it is faster than calling Equals or even CompareTo. For comparing monetary amounts, the difference between two doubles can be compared to some predefined numerical threshold (that is, still using ==).

Happy programming!

Generic Delegates

Greetings to my blog readers!

I have previously covered delegates and events, but I’ve decided to go over the concept of delegates in C# once more. In addition, this time I will mention generic delegates.

So, let’s refresh – what is a delegate? A delegate is a type. It is a type that is designed to be compatible with methods. You may have come across someone comparing C# delegates with C++ function pointers. It is a fair functional comparison. That is, only a certain part of how C++ pointers behave and what they are used for is comparable with the C# delegates. As far as the internal representation – there is really almost nothing in common. The reason I bring up the function pointer is because I too think of delegates as being “like function pointers”. This makes it easier to understand their purpose and syntax. Let me reinforce this functional comparison with an example. Take the below declaration of a delegate:

public delegate T AnyMethod<T>(T input_param);

This declares a delegate that “can represent” any method that takes one parameter and returns one parameter. In other words, AnyMethod delegate is compatible with any method with a signature like that of this delegate. Since AnyMethod is generic, this gives us flexibility in what methods we can assign. Take a look at the below example code which was written to simply support this explanation. Given some appropriate type, AnyMethod can be assigned to a method that works with any parameter type.

using System;

namespace GenericDelegate
{
 
    public delegate T AnyMethod<T>(T input_param);
      

    class EntryLevel
    {
        static void Main(string[] args)
        {
            int i = 10;
            string s = "any string";
            AnyMethod<int> im = IntMethod;          // this is the same as AnyMethod<int> im = new AnyMethod<int>(IntMethod); 
            AnyMethod<string> sm = StringMethod;    // this is the same as AnyMethod<string> sm = new AnyMethod<string>(StringMethod);
            
            im(i);
            sm(s);
        }

        static int IntMethod(int int_param)
        {
            Console.WriteLine("Inside IntMethod with "+int_param.ToString());
            return 0;
        }

        static string StringMethod(string string_param)
        {
            Console.WriteLine("Inside StringMethod with " + string_param);
            return "void";
        }
    }
}

AnyMethod delegate can work with generic methods as well. I can easily replace my two static methods with a single generic one.

class EntryLevel
    {
        static void Main(string[] args)
        {
            int i = 10;
            string s = "any string";
            AnyMethod<int> im = TMethod;          // same as AnyMethod<int> im = new AnyMethod<int>(TMethod);  
            AnyMethod<string> sm = TMethod;       // same AnyMethod<string> sm = new AnyMethod<string>(TMethod); 
            
            im(i);
            sm(s);
        }

         static T TMethod<T> (T t_param)
         {
             Console.WriteLine("Inside TMethod with " + t_param.ToString());
             return t_param;
         }
    }

Remember I wrote that a delegate is a type. Note, I did not write that it is simply a method. Because it is not. Under the hood, for the delegate declaration on line 6, the compiler will be busy creating a real class (albeit a sealed one) that extends System.MultiCastDelegate class, has a constructor and three methods! I prefer not to bother with these details because they tend to confuse me. Instead, I will stick with the comfortable “types compatible with methods” definition. In fact, I like this definition so much that I will remove that delegate declaration entirely…And replace it with Func:

using System;

namespace GenericDelegate
{
    class EntryLevel
    {
        static void Main(string[] args)
        {
            int i = 10;
            string s = "any string";
            Func<int, int> im = TMethod;             // Func<int,int> im = new Func<int,int>(TMethod);   
            Func<string, string> sm = TMethod;       //  Func<string,string> sm = new Func<string,string>(TMethod); 
            
            im(i);
            sm(s);
        }

         static T TMethod<T> (T t_param)
         {
             Console.WriteLine("Inside TMethod with " + t_param.ToString());
             return t_param;
         }
    }
}

Much better! If you have never come across Func before, take a look at the reference source. You will see that Func is just another delegate (with an upper bound on its input parameters), but its definition has been hidden away. Action and Func were introduced in .Net 3.5. I believe they were added to support lambda expressions. However, both are very handy in covering up the unnecessary complexity and reinforcing my earlier analogy.