c# - 类型约束不满足的泛型方法隐藏了扩展方法
问题描述
我正在使用一个简洁的小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());
从这里偷来的。