自定义 GUID 在 object.Equals 上总是 return false

Custom GUID always return false on object.Equals

我们的系统中有 GUID 作为标识符。由于很容易搞砸并将一个实体的 ID 传递给需要另一个实体 ID 的方法(假设您错误地将 OrderId 传递给了 InvoiceId,因为它都是 Guids)我们创建了自己的类型对于 Guid,编译器可以很容易地告诉我 "hey, don't pass an OrderId here, I expect an InvoiceId"。

所以基本上,我们有很多围绕 Guid 的包装器。这些包装器运行良好,它们基本上是 Guid 接口的副本,将所有工作委托给它们内部存储的 Guid。

有一件事我无法弄清楚,我们的两个自定义标识符上的 Assert.AreEqual(a, b) 会失败。它调用 object.Equals(a, b),后者又调用 a == b,而不会调用我的 operator ==,而是调用其他东西,return 为 false。但它不适用于 Guid,我不知道我错过了什么。

如果我的自定义类型已经在 operator == 上运行,我需要实现什么才能让我的自定义类型真正工作并且 return trueobject.Equals(a, b) 上运行?

namespace ConsoleApp13
{
    using System;
    using System.Runtime.InteropServices;

    //// Same signature, interfaces and and attributes as
    //// https://referencesource.microsoft.com/#mscorlib/system/guid.cs

    [StructLayout(LayoutKind.Sequential)]
    [Serializable]
    [ComVisible(true)]
    // not accessible for me: [System.Runtime.Versioning.NonVersionable]
    public struct CustomId : IFormattable, IComparable, IComparable<CustomId>, IEquatable<CustomId>
    {
        public static readonly CustomId Empty = new CustomId();

        private readonly Guid internalGuid;

        private CustomId(Guid guid)
        {
            this.internalGuid = guid;
        }

        public static bool operator ==(CustomId a, CustomId b)
        {
            return a.internalGuid == b.internalGuid;
        }

        public static bool operator !=(CustomId a, CustomId b)
        {
            return !(a.internalGuid == b.internalGuid);
        }

        public static CustomId NewGuid()
        {
            return new CustomId(Guid.NewGuid());
        }

        public static implicit operator Guid(CustomId value)
        {
            return value.internalGuid;
        }

        public static explicit operator CustomId(Guid value)
        {
            return new CustomId(value);
        }

        public override string ToString()
        {
            return "[" + this.GetType().Name + ":" + this.internalGuid.ToString("D") + "]";
        }

        public override int GetHashCode()
        {
            return this.internalGuid.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            return this.internalGuid.Equals(obj);
        }

        public bool Equals(CustomId other)
        {
            return this.internalGuid.Equals(other.internalGuid);
        }

        public int CompareTo(object obj)
        {
            return this.internalGuid.CompareTo(obj);
        }

        public int CompareTo(CustomId other)
        {
            return this.internalGuid.CompareTo(other.internalGuid);
        }

        public string ToString(string format, IFormatProvider formatProvider)
        {
            return this.internalGuid.ToString(format, formatProvider);
        }
    }

    internal static class Program
    {
        internal static void Main()
        {
            {
                var a = CustomId.NewGuid();
                var b = a;

                // shows true false
                Console.WriteLine("{0} {1}", a == b, object.Equals(a, b));
            }

            {
                var a = Guid.NewGuid();
                var b = a;

                // shows true true
                Console.WriteLine("{0} {1}", a == b, object.Equals(a, b));
            }

            Console.WriteLine(@"Done.");
            Console.ReadLine();
        }
    }
}

您的代码:

    public override bool Equals(object obj)
    {
        return this.internalGuid.Equals(obj);
    }

将解包 自身 ,但不会解包 other 实例,因此:如果 objCustomId,因为 Guid 不会期望得到 CustomId(它想要 Guid)。也许这应该是:

    public bool Equals(object obj) => obj is CustomId cid && cid == this;

注意 CompareTo 应该是类似的:

    public int CompareTo(object obj) => obj is CustomId cid ? this.CompareTo(cid) : -1;

Marc Gravell 的回答是正确的,但我想说明一点。

It calls object.Equals(a, b) that in turn calls a == b

这是一个错误的假设。如果两者都不为空,object.Equals(a, b) 将调用 a.Equals(b)。细微差别,但 一个区别:

https://referencesource.microsoft.com/#mscorlib/system/object.cs,d9262ceecc1719ab

public static bool Equals(Object objA, Object objB) 
{
    if (objA==objB) {
        return true;
    }
    if (objA==null || objB==null) {
        return false;
    }
    return objA.Equals(objB);
}