确定实例已覆盖 GetHashCode 和 Equals?

determine that an instance has GetHashCode and Equals overridden?

给定一个 C# 对象实例,我如何确定该对象是否具有值语义?换句话说,我想保证我的 API 中使用的对象适合用作字典键。我在想这样的事情:

var type = instance.GetType();
var d1 = FormatterServices.GetUninitializedObject(type);
var d2 = FormatterServices.GetUninitializedObject(type);
Assert.AreEqual(d1.GetHashCode(), d2.GetHashCode());

您如何看待这种方法?

FormatterServices.GetUninitializedObject可以使对象处于无效状态;它打破了只读字段等的保证分配。任何假定字段不会 null 的代码都会中断。我不会用那个。

你可以通过反射检查GetHashCodeEquals是否被覆盖,但这还不够。您可以覆盖方法调用基础 class 方法。这不算作值语义。

顺便说一句,值语义并不意味着相等的哈希码。也可能是碰撞;值语义意味着具有 equals 属性的两个对象应该 return 相同的 hashcode 以及 equals 方法应该评估为真。

我建议你创建一个实例,分配一些属性,克隆它;现在两个哈希码应该相等并且调用 object.Equals(original, clone) 应该评估为真。

您可以通过以下方式测试 Equals()GetHashCode() 的实现:

s.GetType().GetMethod("GetHashCode").DeclaringType == s.GetType()

或者更确切地说,根据@hvd 的建议:

s.GetType().GetMethod("GetHashCode").DeclaringType != typeof(object)

Some some object,如果GetHashCode()没有被它的类型实现,这个就是false,否则为true。

需要注意的一件事是,这不会防止 Equals()GetHashCode() 的实施不当 - 即使实施是 public override int GetHashCode() { },这也会评估为 true。

考虑到缺点,我倾向于记录您的类型 ("this type should / should not be used for a dictionary key..."),因为这不是您最终可以依赖的东西。如果 Equals()GetHashCode() 的实现有缺陷而不是缺失,它将通过此​​测试但仍然有 运行 时间错误。

你可以看到一个对象是否定义了它自己的 EqualsGetHashCode 使用 DeclaringType 属性 在相应的 MethodInfo:

bool definesEquality = type.GetMethod("Equals", new[] { typeof(object) }).DelcaringType == type && type.GetMethod("GetHashCode", Type.EmptyTypes).DeclaringType == type;