The idea of a compiler generated function is justified.
Thinking about the effects however I think the language design team did it right. Compilergenerated methods known from C++ are hard to understand for beginners. Lets see what would happen in C# with autogenerated struct.Equals:
As it is now, the concept of .Equals() is simple:
- Every struct inherits Equals from ValueType.
- If overriden, the custom Equals method applies.
If the compiler would always create the Equals method, we could have:
- Every struct inherits Equals from Object. (ValueType would no longer implement its own version)
- Object.Equals is now always(!) overriden, either by the compiler generated Equals method or by the users implementation
Now our struct has an autogenerated override method that the code reader does not see! So how do you know that the base method Object.Equals does not apply to your struct? By learning all the cases of automatically compiler generated methods. And this is exactly one of the burdens learning C++.
Would consider it good decision to leave efficient struct Equals to the user and keep the concepts simple, requiring a standard default Equals method.
That said, performance critical structs should override Equals. The code below shows
3606 vs 53 Milliseconds measured on .Net 4.5.1
This performance gain is certainly due to avoiding virtual Equals, but anyway, so if the virtual Object.Equals would be called the gain would be much lower. Performance critical cases will not call Object.Equals however, so the gain here would apply.
using System;
using System.Diagnostics;
struct A
{
public int X;
public int Y;
}
struct B : IEquatable<B>
{
public bool Equals(B other)
{
return this.X == other.X && this.Y == other.Y;
}
public override bool Equals(object obj)
{
return obj is B && Equals((B)obj);
}
public int X;
public int Y;
}
class Program
{
static void Main(string[] args)
{
var N = 100000000;
A a = new A();
a.X = 73;
a.Y = 42;
A aa = new A();
a.X = 173;
a.Y = 142;
var sw = Stopwatch.StartNew();
for (int i = 0; i < N; i++)
{
if (a.Equals(aa))
{
Console.WriteLine("never ever");
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
B b = new B();
b.X = 73;
b.Y = 42;
B bb = new B();
b.X = 173;
b.Y = 142;
sw = Stopwatch.StartNew();
for (int i = 0; i < N; i++)
{
if (b.Equals(bb))
{
Console.WriteLine("never ever");
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
see also http://blog.martindoms.com/2011/01/03/c-tip-override-equals-on-value-types-for-better-performance/