Идея функции, сгенерированной компилятором, оправдана.
Думая об эффектах, я думаю, что команда разработчиков языка сделала это правильно. Сгенерированные компилятором методы, известные из C ++, трудны для понимания новичками. Давайте посмотрим, что произойдет в C # с автоматически сгенерированной struct.Equals:
Как и сейчас, концепция .Equals () проста:
- Каждая структура наследует Equals из ValueType.
- При переопределении применяется пользовательский метод Equals.
Если бы компилятор всегда создавал метод Equals, мы могли бы иметь:
- Каждая структура наследует Equals от Object. (ValueType больше не будет реализовывать свою собственную версию)
- Object.Equals теперь всегда (!) Переопределяется либо сгенерированным компилятором методом Equals, либо пользовательской реализацией
Теперь в нашей структуре есть автоматически сгенерированный метод переопределения, который программа чтения кода не видит! Итак, как вы узнаете, что базовый метод Object.Equals не применяется к вашей структуре? Изучая все случаи автоматически сгенерированных методов компилятора. И это как раз одна из трудностей изучения C ++.
Было бы хорошим решением оставить эффективную структуру Equals для пользователя и сохранить простые концепции, требующие стандартного метода Equals по умолчанию.
При этом критические структуры производительности должны переопределять Equals. Код ниже показывает
3606 против 53 Миллисекунд измерено в .Net 4.5.1
Это увеличение производительности, безусловно, связано с отказом от виртуальных равных, но в любом случае, поэтому, если бы виртуальные Object.Equals назывались, усиление было бы намного ниже. Однако в критических случаях производительности не будет вызываться Object.Equals, поэтому здесь будет применяться усиление.
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);
}
}
см. Также http://blog.martindoms.com/2011/01/03/c-tip-override-equals-on-value-types-for-better-performance/