简体   繁体   English

自定义GUID始终在对象上返回false。

[英]Custom GUID always return false on object.Equals

We have GUIDs as identifiers in our systems. 我们的系统中有GUID作为标识符。 As it's easy to mess up and pass the id of one entity into a method that expects the id of another entity (lets say you pass the OrderId to the InvoiceId by mistake because it's all Guid s) we created our own types for Guids, so the compiler can easily tell me "hey, don't pass an OrderId here, I expect an InvoiceId". 由于很容易弄乱一个实体的ID并将其传递到需要另一个实体的ID的方法中(假设您将OrderId错误地传递给InvoiceId,因为它们都是Guid ),所以我们为Guid创建了自己的类型,因此编译器可以很容易地告诉我“嘿,不要在这里传递OrderId,我希望收到InvoiceId”。

So basically, we have lots of wrappers around Guid. 因此,基本上,我们在Guid周围有很多包装纸。 Those wrappers work well, they are basically copies of the Guid interface delegating all the work to their internally stored Guid. 这些包装程序运行良好,它们基本上是Guid接口的副本,将所有工作委托给其内部存储的Guid。

One thing that I cannot figure out is that Assert.AreEqual(a, b) on two of our custom identifiers will fail. 我无法弄清的一件事是,我们两个自定义标识符上的Assert.AreEqual(a, b)将失败。 It calls object.Equals(a, b) that in turn calls a == b and that will not call my operator == but instead call something else and return false. 它调用object.Equals(a, b) ,后者依次调用a == b ,并且不会调用我的operator == ,而是调用其他内容并返回false。 It does not for Guid though and I cannot figure out what I missed. 不过,这并不适合Guid而且我无法弄清楚自己错过了什么。

What do I need to implement for my custom types to actually work and return true on object.Equals(a, b) given that it already does on operator == ? 我需要实现什么使我的自定义类型实际工作并在object.Equals(a, b)上返回trueobject.Equals(a, b)是它已经对operator ==起作用object.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();
        }
    }
}

Your code here: 您的代码在这里:

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

is going to unwrap itself , but it doesn't unwrap the other instance, so : it will always fail if obj is a CustomId , as the Guid won't expect to be handed a CustomId (it wants a Guid ). 是去解开本身 ,但它并没有解开另一个实例,所以:如果它总是会失败objCustomId ,为Guid不会希望交到CustomId (就是了一Guid )。 Perhaps this should be: 也许应该是:

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

Note that CompareTo should probably be similar: 请注意, CompareTo可能应该类似:

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

The answer from Marc Gravell is correct but I want to note something. 马克·格雷韦尔(Marc Gravell)的答案是正确的,但我想指出一点。

It calls object.Equals(a, b) that in turn calls a == b 它调用object.Equals(a,b),依次调用a == b

This is a wrong assumption. 这是一个错误的假设。 object.Equals(a, b) will call a.Equals(b) if both of them are not null. 如果两个对象都不为null,则a.Equals(b) object.Equals(a, b)将调用a.Equals(b) Subtle difference but it is a difference: 细微的差别,但这不同的:

https://referencesource.microsoft.com/#mscorlib/system/object.cs,d9262ceecc1719ab 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM