我实现了一个流畅的参数断言库,其中重点是编译时的强类型检查。 Intellisense应仅显示可用于断言类型的方法和扩展。
在为IEnumerable创建扩展时,我在解决正确的类型参数时遇到问题。
库中的想法是你可以在任何类型上调用ThrowIf(或ThrowIfNot),它会返回IAssertion类型的断言实例:
public static IAssertion<T> ThrowIf<T>(this T t)
{
return new IfAssertion<T>(t);
}
现在我想检查IEnumerable是否包含特定项目。将有两个重载,其中一个将类型T的对象作为参数,另一个采用函数在哪里进行评估:
public static T1 Contains<T1, T2>(this IAssertion<T1> assertion, T2 item)
where T1 : IEnumerable<T2>
{
// assertion logic
return assertion.Value;
}
public static T1 Contains<T1, T2>(this IAssertion<T1> assertion, Func<T2, bool> func)
where T1 : IEnumerable<T2>
{
// assertion logic
return assertion.Value;
}
使用带有实际类型实例的重载时,一切都很顺利。但是后者使用函数编译器无法正确推断类型参数,除非进行强制转换:
var list = new List<string>();
list.ThrowIf().Contains("foo"); // compiles
list.ThrowIf().Contains((string s) => false); // compiles
list.ThrowIf().Contains(s => false); // does not compile
如果没有为函数参数执行强制转换,有没有办法让编译器满意?
可以从此处找到更多实施细节: https://bitbucket.org/mikalkai/argument-assertions/overview
答案 0 :(得分:3)
免责声明:此答案仅在IAssertion
成为covariant时才有效。
假设IAssertion
是协变的,T1
方法不一定需要两个泛型类型参数T2
和Contains
。相反,您直接在界面中指定IEnumerable
并仅使用一个泛型类型参数:
public static IEnumerable<T> Contains<T>(this IAssertion<IEnumerable<T>> assertion, T item)
{
// assertion logic
return assertion.Value;
}
public static IEnumerable<T> Contains<T>(this IAssertion<IEnumerable<T>> assertion, Func<T, bool> func)
{
// assertion logic
return assertion.Value;
}
然后你可以像这样使用contains方法:
var list = new List<string>();
list.ThrowIf().Contains("foo"); // compiles
list.ThrowIf().Contains((string s) => false); // compiles
list.ThrowIf().Contains(s => false); // compiles now too