通用接口上的扩展方法,其中 T 是可枚举的 <K>
Extension methods on a generic interface where T is Enumerable<K>
我正在实现一个流畅的参数断言库,其中重点是编译时的强类型检查。 Intellisense 应该只显示可用于断言类型的方法和扩展。
我在为 IEnumerable 创建扩展时遇到了解析正确类型参数的问题。
库中的想法是您可以在任何类型上调用 ThrowIf(或 ThrowIfNot),这将 return 您是 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
免责声明:此答案仅在 IAssertion
可以 covariant 时才有效。
假设 IAssertion
是协变的,那么 Contains
方法不一定需要两个泛型类型参数 T1
和 T2
。相反,您直接在界面中指定 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
我正在实现一个流畅的参数断言库,其中重点是编译时的强类型检查。 Intellisense 应该只显示可用于断言类型的方法和扩展。
我在为 IEnumerable 创建扩展时遇到了解析正确类型参数的问题。
库中的想法是您可以在任何类型上调用 ThrowIf(或 ThrowIfNot),这将 return 您是 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
免责声明:此答案仅在 IAssertion
可以 covariant 时才有效。
假设 IAssertion
是协变的,那么 Contains
方法不一定需要两个泛型类型参数 T1
和 T2
。相反,您直接在界面中指定 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