首页 > 解决方案 > 类型约束不满足的泛型方法隐藏了扩展方法

问题描述

我正在使用一个简洁的小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 类型“DateRange”必须是不可为空的值类型,才能在方法“HashCode.Hash(T)”的泛型类型中将其用作参数“T”

显然,编译器已经决定“最佳重载”Hash是具有不满足泛型类型约束的那个。太糟糕了,因为我的扩展方法非常适合。


有什么方法可以诱使编译器使用正确的方法,而不必求助于使用不同的函数名或在我的调用中包含“虚拟”参数Hash

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

标签: c#genericsextension-methods

解决方案


一个小技巧就会奏效。像这样创建一个类:

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());

从这里偷来的。


推荐阅读