具有不满意类型约束的通用方法是隐藏扩展方法[重复]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了具有不满意类型约束的通用方法是隐藏扩展方法[重复]相关的知识,希望对你有一定的参考价值。

这个问题在这里已有答案:

我正在使用一个整洁的小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库,这将允许我的扩展方法被注意到。 (我不想将自定义重载添加到基本库,因为这些自定义类型当前不存在于其命名空间中)

答案

一个小技巧将起作用。创建一个这样的类:

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偷来的。

以上是关于具有不满意类型约束的通用方法是隐藏扩展方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript:相当于 C# 的用于扩展类的通用类型约束?

为啥我会收到错误“协议……只能用作通用约束,因为它具有自身或关联的类型要求”?

具有接口类型约束的 C# 泛型方法

包含值类型和字符串的 C# 通用约束

为啥 Nullable<T> 不匹配作为通用约束的引用类型 [重复]

无法在通用方法中将整数转换为枚举 [重复]