12

所以,一个相当常见的 IEnumerable 扩展方法,运行:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
    {
        action(item);
        yield return item;
    }
}

例如,当我尝试将它与 DbSet.Add 一起使用时:

invoice.Items.Run(db.InvoiceItems.Add);
// NB: Add method signature is
// public T Add(T item) { ... }

...编译器抱怨它有错误的返回类型,因为它需要一个 void 方法。因此,为 Run 添加一个使用 Func 而不是 Action 的重载:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Func<T, T> action)
{
    return source.Select(action).ToList().AsEnumerable();
}

现在编译器抱怨“以下方法之间的调用不明确......”

所以我的问题是,当 Run 方法的 Action 重载对方法组无效时,它怎么会导致歧义?

4

4 回答 4

5

Eric 和 Jon 在回答这个问题时已经解释了这一点。长话短说——这就是 C# 编译器的工作原理;确切地说,在处理方法组转换时,决定将其转换为哪个委托时会利用重载决议,它不考虑返回类型

这里的原则是确定方法组可转换性需要使用重载决议从方法组中选择一个方法,而重载决议不考虑返回类型。

在您的示例中,编译器将Action<T>Func<T, T>视为. 这增加了两种可能的选择,并且由于它需要一个 - 发出适当的错误。Add

于 2012-06-27T12:24:23.853 回答
0

以正确的方式尝试超载:

public static IEnumerable<TDest> Run<TSource, TDest>(this IEnumerable<TSource> source, 
    Func<TSource, TDest> action) 
{ 
 return source.Select(action).ToList(); 
} 
于 2012-06-27T11:29:37.497 回答
0

我无法回答为什么,但要解决歧义,您可以显式转换您的函数:

invoice.Items.Run((Func<T,T>)db.InvoiceItems.Add); 

或使用 lambda

invoice.Items.Run(x => db.InvoiceItems.Add(x));
于 2012-06-27T11:43:21.473 回答
0

我不知道为什么它不能自动解决它,但这里有两个解决方法:

// with T replaced with the actual type:
invoice.Items.Run((Func<T, T>)db.InvoiceItems.Add);
invoice.Items.Run(new Func<T, T>(db.InvoiceItems.Add));

为什么你仍然需要这些方法?有什么问题:

foreach (var item in invoice.Items)
    db.InvoiceItems.Add(item);

这个的可读性要好得多。除非您有充分的理由需要该Run方法,否则我建议您不要使用它。据我所见,没有这样的原因,至少对于Action<T>过载而言。

于 2012-06-27T11:45:00.127 回答