Overriding Equals method in Structs

前端 未结 6 532
-上瘾入骨i
-上瘾入骨i 2020-12-28 11:24

I\'ve looked for overriding guidelines for structs, but all I can find is for classes.

At first I thought I wouldn\'t have to check to see if the passed object was n

相关标签:
6条回答
  • 2020-12-28 11:58

    Thanks to pattern matching in C# 7.0 there is an easier way to accomplish the accepted answer:

    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
        }
    }
    

    You could also make it even shorter as an expression-bodied function:

    struct MyStruct 
    {
        public override bool Equals(object obj) => 
            obj is MyStruct mys
                && mys.field1 == this.field1
                && mys.field2 == this.field2;
    }
    
    0 讨论(0)
  • 2020-12-28 12:00
    struct MyStruct 
    {
       public override bool Equals(object obj) 
       {
           if (!(obj is MyStruct))
              return false;
    
           MyStruct mys = (MyStruct) obj;
           // compare elements here
    
       }
    
    }
    
    0 讨论(0)
  • 2020-12-28 12:01

    Use the is operator:

    public bool Equals(object obj)
    {
      if (obj is MyStruct)
      {
        var o = (MyStruct)obj;
        ...
      }
    }
    
    0 讨论(0)
  • 2020-12-28 12:15

    I suppose, if one's using .NET 4.5, one can use the default implementation as noted in the documentation:

    When you define your own type, that type inherits the functionality defined by the Equals method of its base type.

    ValueType.Equals: Value equality; either direct byte-by-byte comparison or field-by-field comparison using reflection.

    0 讨论(0)
  • 2020-12-28 12:16

    Adding to the existing answers.

    You can still have nullable values if you append a ? after the struct name (this works for every value object)

    int?
    

    Casting is done also by calling (MyStructName)variableName

    0 讨论(0)
  • 2020-12-28 12:20

    In case anyone's wondering about the performance hit of boxing the struct in a Nullable object (to avoid the double type check from is and the cast), there is a non-negligible overhead.

    tl;dr: Use is & cast in this scenario.

    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);
        }
    }
    

    Results:

    BOXING
    EQ  8012    7973    7981    8000
    NEQ 7929    7715    7906    7888
    
    DOUBLECHECK
    EQ  3654    3650    3638    3605
    NEQ 3310    3301    3319    3297
    

    Warning: This test might be flawed in many ways, though I did verify that the benchmark code itself wasn't optimized in an odd fashion.

    Looking at the IL, the double-check method compiles a little cleaner.

    Boxing 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
    

    Double-check 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
    

    Props to Roman Reiner for spotting a mistake that really wasn't making me look good.

    0 讨论(0)
提交回复
热议问题