覆盖结构中的等于方法

我一直在寻找结构的主要指导原则,但是我所能找到的只是关于类的。

起初我以为我不必检查传递的对象是否为null,因为struct是值类型并且不能为null。 但是现在我想到了,等于签名是

public bool Equals(object obj)

似乎没有什么可以阻止我的结构的用户尝试将其与任意引用类型进行比较。

我的第二点涉及在比较结构中的私有字段之前,我(我想)必须进行的转换。 我应该如何将对象转换为结构的类型? C#的as关键字似乎仅适用于引用类型。

devoured elysium asked 2019-11-18T22:11:15Z
6个解决方案
75 votes
struct MyStruct 
{
   public override bool Equals(object obj) 
   {
       if (!(obj is MyStruct))
          return false;

       MyStruct mys = (MyStruct) obj;
       // compare elements here

   }

}
James Curran answered 2019-11-18T22:11:29Z
13 votes

我想,如果使用的是.NET 4.5,则可以使用文档中所述的默认实现:

定义自己的类型时,该类型将继承其基本类型的Equals方法定义的功能。

ValueType.Equals:值相等; 直接字节比较或使用反射的逐字段比较。

Albus Dumbledore answered 2019-11-18T22:12:06Z
11 votes

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

您也可以将其作为表达式强健的函数使其更短:

struct MyStruct 
{
    public override bool Equals(object obj) => 
        obj is MyStruct mys
            && mys.field1 == this.field1
            && mys.field2 == this.field2;
}
ensisNoctis answered 2019-11-18T22:12:37Z
7 votes

如果有人想知道将结构装在Nullable对象中的性能影响(以避免is和强制类型转换的双重类型检查),那么开销是不可忽略的。

tl; dr:在这种情况下使用is&cast。

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

对罗曼·赖纳(Roman Reiner)的支持是发现了一个实际上并不能使我看起来不错的错误。

tne answered 2019-11-18T22:13:40Z
5 votes

使用2620224248236999999680运算符:

public bool Equals(object obj)
{
  if (obj is MyStruct)
  {
    var o = (MyStruct)obj;
    ...
  }
}
Dan Story answered 2019-11-18T22:14:04Z
0 votes

添加到现有答案中。

如果附加?,您仍然可以具有可为空的值。 在结构名称之后(这适用于每个值对象)

int?

也可以通过致电(MyStructName)variableName进行投射

jpabluz answered 2019-11-18T22:14:41Z
translate from https://stackoverflow.com:/questions/2542693/overriding-equals-method-in-structs