具有不满足类型约束的泛型方法隐藏了一个扩展方法

Generic Method with Unsatisfied Type Constraint is Hiding an Extension Method

我正在使用一个简洁的小 HashCode 实用程序,其中包含一些有助于快速生成哈希值的实例方法。我已经针对这个问题进行了简化,但基本上是这样的:

public struct HashCode
{
    private readonly int _hashCode;
    public HashCode(int hashCode) { _hashCode = hashCode; }
    override int GetHashCode() => _hashCode;

    public static HashCode Start => new HashCode(17);    
    public HashCode Hash(int integer) => new HashCode(unchecked(integer + hashCode * 31));    
    public HashCode Hash<T>(T value) where T : struct => Hash(value.GetHashCode());    
    public HashCode Hash(string value) => Hash(value?.GetHashCode() ?? 17);
    public HashCode Hash<T>(T? nullable) where T : struct =>
        Hash(nullable?.GetHashCode() ?? 17);
    // Other reference types, user must explicitly specify a comparer
    public HashCode Hash<T>(T obj, IEqualityComparer<T> comparer) =>
        Hash(obj == null ? 17: comparer.GetHashCode(obj));  

    public static implicit operator int(HashCode hashCode) => hashCode.GetHashCode();
}

允许灵活的哈希实现,例如:

class Person {
    // ...
    override GetHashCode() => HashCode.Start.Hash(name).Hash(age).Hash(...);
}

如您所见,它竭尽全力避免装箱等。如果您直接散列引用类型,则必须指定一个比较器以确保您知道它是如何被散列的。似乎有道理


现在,我希望在我的一个项目中添加一些扩展方法(即 无需复制和修改库),以便我可以轻松地添加一些 consice 散列函数我自己的常用类型,并使用完全相同的语法使用它们:

public static class HashCode_Extensions
{
    public static HashCode Hash(this HashCode hc, DateRange range) =>
        hc.Hash(range?.begin).Hash(range?.end);

    public static HashCode Hash(this HashCode hc, IEnumerable<T> list) where T : struct =>
        list.Aggregate(hc, (hc, elem) => hc.Hash(elem));

    // etc...
}

我以为我很聪明,大量复制粘贴的代码会消失。

class Meeting {
    // ...
    override GetHashCode() => HashCode.Start.Hash(name).Hash(dateRange).Hash(invitees);
}

不幸的是,编译器选择了通用实例方法而不是我自己的方法,即使我的方法非常适合并且通用方法的类型约束不是

CS0453 The type 'DateRange' must be a non-nullable value type in order to use it as parameter 'T' in the generic type of method 'HashCode.Hash(T)'

显然编译器已经决定 Hash 的 "best overload" 是不满足泛型类型约束的那个。太糟糕了,因为我的扩展方法非常适合。


有什么方法可以诱使编译器使用正确的方法,而不必求助于使用不同的函数名称或在我对 Hash 的调用中包含 'dummy' 个参数?

如果有一些向后兼容的方式让我更改 HashCode 库,让我的扩展方法受到关注,我也会很高兴。 (我不想将我的自定义重载添加到基础库中,因为这些自定义类型当前不存在于它的命名空间中)

一个小技巧就可以了。像这样创建一个 class:

public class RequireStruct<T> where T : struct { }

将其作为可选参数放入通用方法中,如下所示:

public struct HashCode
{
    private readonly int _hashCode;
    public HashCode Hash<T>(T value, RequireStruct<T> ignore = null) where T : struct => Hash(value.GetHashCode());

}

现在,当您执行此操作时,它会按照您的意愿选择您的扩展方法:

HashCode code = new HashCode();
code.Hash(new DateRange());

here.

偷来的