自定义GUID总是在object上返回false。等于

时间:2018-07-27 13:38:44

标签: c# guid equality custom-type

我们的系统中有GUID作为标识符。由于很容易弄乱一个实体的ID并将其传递到需要另一个实体的ID的方法中(假设您误将OrderId传递给InvoiceId,因为它们都是Guid),所以我们创建了自己的类型对于Guid,编译器可以轻松地告诉我“嘿,不要在这里传递OrderId,我希望输入InvoiceId”。

因此,基本上,我们在Guid周围有很多包装纸。这些包装程序运行良好,它们基本上是Guid接口的副本,将所有工作委托给其内部存储的Guid。

我不知道的一件事是,我们两个自定义标识符上的Assert.AreEqual(a, b)将失败。它会调用object.Equals(a, b),然后依次调用a == b,并且不会调用我的operator ==,而是会调用其他内容并返回false。但是,它不适用于Guid,而且我无法弄清错过的地方。

要使我的自定义类型能够实际工作并在true上返回object.Equals(a, b),并且已经在operator ==上返回,我需要实现什么?

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

2 个答案:

答案 0 :(得分:3)

您的代码在这里:

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

会自动打开 ,但不会打开 other 实例,因此:如果obj是{{1 }},因为CustomId不会被交给Guid(它需要CustomId)。也许应该是:

Guid

请注意, public bool Equals(object obj) => obj is CustomId cid && cid == this; 应该相似:

CompareTo

答案 1 :(得分:1)

马克·格雷韦尔(Marc Gravell)的回答是正确的,但我想指出一点。

  

它调用object.Equals(a,b)依次调用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);
}