为什么使用generics的“等于”方法解析与显式调用不同

我有以下示例:

namespace ComparisonExample { class Program { static void Main(string[] args) { var hello1 = new Hello(); var hello2 = new Hello(); // calls Hello.Equals var compareExplicitly = hello1.Equals(hello2); // calls Object.Equals var compareWithGenerics = ObjectsEqual(hello1, hello2); } private static bool ObjectsEqual(TValue value1, TValue value2) { return value1.Equals(value2); } } class Hello : IEquatable { public bool Equals(Hello other) { return true; // doesn't matter } } } 

问题是为什么在第二个“Equals”调用中,我被重定向到Object.Equals而不是Hello.Equals,即使我在generics参数中指定了确切的类型?

因为您没有告诉generics方法您的对象实现了IEquatable

立即尝试:

 private static bool ObjectsEqual(TValue value1, TValue value2) where TValue : IEquatable // IMPORTANT!!! { return value1.Equals(value2); } 

在您的ObjectsEqual方法中,您只能访问在object类中定义的TValue方法/属性/字段以及在约束中定义的接口/基类中定义的方法。 没有约束=>你只能访问Equals(object)GetHashCode()GetType() ,(如果你有约束classoperator==operator!= 。)这两个是虚拟的( Equals(object)GetHashCode() ),因此您将使用“正确”版本,第三个通常不会被覆盖( GetType() ),因此您可能会使用“正确”版本。 只有两个运算符== / !=经常被覆盖而且看得出来! 在您的通用方法中,您不能使用两者的“正确”版本! 🙂

来自MSDN的补充

无界类型参数。
没有约束的类型参数(例如public class SampleClass { }称为无界类型参数
无界类型参数具有以下规则:

  • 无法使用!===运算符,因为无法保证具体类型参数将支持这些运算符。
  • 它们可以转换为System.Object或从System.Object转换为显式转换为任何接口类型。
  • 你可以比较为null 。 如果将无界参数与null进行比较,则如果type参数是值类型,则比较将始终返回false

在这种情况下, TValue将转换为System.ObjectEquals方法。