Expressions aren’t made equal

I got surprised by Expressions last week while trying to create a caching mechanism for them. To be precise, this piece of code

static void Main()
{
    Expression<Action<string>> first = s => Console.WriteLine(s);
    Expression<Action<string>> second = n => Console.WriteLine(n);
    Expression<Action<string>> third = n => Console.WriteLine(n);
    Check(first, second);
    Check(second, third);
 
}
 
private static void Check(Expression<Action<string>> a, Expression<Action<string>> b)
{
    Console.WriteLine("{0} (hc: {1})\nis {2}equal to \n{3} (hc: {4})\n-------------",
                      a, a.GetHashCode(),
                      (a.Equals(b) ? "" : "not "),
                      b, b.GetHashCode());
}

Produces this result

Expressions

which I find… well, not intuitive.

Expression (and classes deriving from it) do not override Equals and GetHashCode, so we get standard System.Object behavior. I really can see no reason why it works this way. Expressions denote piece of algorithm, so one might expect two semantically identical Expressions to be equal (and to have the same hashcode). I would actually expect first and second expressions to be equal. They aren’t equal syntactically (first’s parameter is called “s” and second’s parameter is called “n”, so they are different in this regard). Semantically thought they are identical, so why aren’t they equal?

The good news is, that you can create your own IComparer<Expression> and implement that behavior. The bad news is, you have to do it. I’d be thankful if someone pointed me to some explanation why it’s done this way.