通用接口上的扩展方法,其中T是可枚举<k>

时间:2016-03-29 19:17:05

标签: c# interface extension-methods func inferred-type

我实现了一个流畅的参数断言库,其中重点是编译时的强类型检查。 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

1 个答案:

答案 0 :(得分:3)

免责声明:此答案仅在IAssertion成为covariant时才有效。

假设IAssertion是协变的,T1方法不一定需要两个泛型类型参数T2Contains。相反,您直接在界面中指定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