Переопределение метода Equals в структурах
Я искал переопределяющие рекомендации для структур, но все, что я могу найти, это для классов.
сначала я подумал, что мне не нужно будет проверять, был ли переданный объект null, поскольку структуры являются типами значений и не могут быть null. Но теперь, когда я думаю об этом, как равный подпись
public bool Equals(object obj)
Кажется, нет ничего, что мешает пользователю моей структуры пытаться сравнить его с произвольным ссылочным типом.
мой второй пункт касается кастинг я (думаю, что я) должен сделать, прежде чем сравнивать мои личные поля в моей структуре. Как я должен привести объект к типу моей структуры? C# ' s as
ключевое слово подходит только для ссылочных типов.
6 ответов:
struct MyStruct { public override bool Equals(object obj) { if (!(obj is MyStruct)) return false; MyStruct mys = (MyStruct) obj; // compare elements here } }
Я полагаю, если вы используете .NET 4.5, можно использовать реализацию по умолчанию, как указано в документация:
когда вы определяете свой собственный тип, Этот тип наследует функциональность, определенную методом Equals его базового типа.
ValueType.Равно: равенство значений; либо прямое байтовое сравнение, либо сравнение по полю с использованием отражения.
в случае, если кто-то задается вопросом о производительности бокса структуры в объекте с нулевым значением (чтобы избежать двойной проверки типа от
is
и актерский состав), там и незначительную нагрузку.tl; dr используйте
is
& приведение в этом сценарии.struct Foo : IEquatable<Foo> { public int a, b; public Foo(int a, int b) { this.a = a; this.b = b; } public override bool Equals(object obj) { #if BOXING var obj_ = obj as Foo?; return obj_ != null && Equals(obj_.Value); #elif DOUBLECHECK return obj is Foo && Equals((Foo)obj); #elif MAGIC ? #endif } public bool Equals(Foo other) { return a == other.a && b == other.b; } } class Program { static void Main(string[] args) { RunBenchmark(new Foo(42, 43), new Foo(42, 43)); RunBenchmark(new Foo(42, 43), new Foo(43, 44)); } static void RunBenchmark(object x, object y) { var sw = Stopwatch.StartNew(); for (var i = 0; i < 100000000; i++) x.Equals(y); sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); } }
результаты:
BOXING EQ 8012 7973 7981 8000 NEQ 7929 7715 7906 7888 DOUBLECHECK EQ 3654 3650 3638 3605 NEQ 3310 3301 3319 3297
предупреждение: этот тест может быть ошибочным во многих отношениях, хотя я действительно проверил, что сам тестовый код не был оптимизирован в нечетном мода.
глядя на IL, метод двойной проверки компилирует немного чище.
бокс IL:
.method public hidebysig virtual instance bool Equals ( object obj ) cil managed { // Method begins at RVA 0x2060 // Code size 37 (0x25) .maxstack 2 .locals init ( [0] valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo> obj_ ) IL_0000: ldarg.1 IL_0001: isinst valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo> IL_0006: unbox.any valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo> IL_000b: stloc.0 IL_000c: ldloca.s obj_ IL_000e: call instance bool valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo>::get_HasValue() IL_0013: brfalse.s IL_0023 IL_0015: ldarg.0 IL_0016: ldloca.s obj_ IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo>::get_Value() IL_001d: call instance bool StructIEqualsImpl.Foo::Equals(valuetype StructIEqualsImpl.Foo) IL_0022: ret IL_0023: ldc.i4.0 IL_0024: ret } // end of method Foo::Equals
дважды проверьте IL:
.method public hidebysig virtual instance bool Equals ( object obj ) cil managed { // Method begins at RVA 0x2060 // Code size 23 (0x17) .maxstack 8 IL_0000: ldarg.1 IL_0001: isinst StructIEqualsImpl.Foo IL_0006: brfalse.s IL_0015 IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: unbox.any StructIEqualsImpl.Foo IL_000f: call instance bool StructIEqualsImpl.Foo::Equals(valuetype StructIEqualsImpl.Foo) IL_0014: ret IL_0015: ldc.i4.0 IL_0016: ret } // end of method Foo::Equals
реквизит для Романа Райнера за обнаружение ошибки, которая действительно не заставляла меня хорошо выглядеть.
использовать
is
оператор:public bool Equals(object obj) { if (obj is MyStruct) { var o = (MyStruct)obj; ... } }
спасибо Новости в C# 7.0 есть более простой способ сделать то же самое, что и принятый ответ:
struct MyStruct { public override bool Equals(object obj) { if (!(obj is MyStruct mys)) // type pattern here return false; return this.field1 == mys.field1 && this.field2 == mys.field2 // mys is already known here without explicit casting } }
или мой любимый - то же самое, что выражение bodied function:
struct MyStruct { public override bool Equals(object obj) => obj is MyStruct mys && mys.field1 == this.field1 && mys.field2 == this.field2; }