自定义 GUID 在 object.Equals 上总是 return false
Custom GUID always return false on object.Equals
我们的系统中有 GUID 作为标识符。由于很容易搞砸并将一个实体的 ID 传递给需要另一个实体 ID 的方法(假设您错误地将 OrderId 传递给了 InvoiceId,因为它都是 Guid
s)我们创建了自己的类型对于 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 true
在 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();
}
}
}
您的代码:
public override bool Equals(object obj)
{
return this.internalGuid.Equals(obj);
}
将解包 自身 ,但不会解包 other 实例,因此:如果 obj
是 CustomId
,因为 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);
}
我们的系统中有 GUID 作为标识符。由于很容易搞砸并将一个实体的 ID 传递给需要另一个实体 ID 的方法(假设您错误地将 OrderId 传递给了 InvoiceId,因为它都是 Guid
s)我们创建了自己的类型对于 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 true
在 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();
}
}
}
您的代码:
public override bool Equals(object obj)
{
return this.internalGuid.Equals(obj);
}
将解包 自身 ,但不会解包 other 实例,因此:如果 obj
是 CustomId
,因为 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);
}