为什么C#的重载解析在Func <t,t>和Action <t>之间不起作用?</t> </t,t>

时间:2012-06-27 11:24:55

标签: c# linq

因此,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 个答案:

答案 0 :(得分:5)

Eric和Jon在this question的答案中已经解释过这一点。长话短说 - 这就是C#编译器的工作原理;确切地说,在处理方法组转换时,决定将转换为哪个委托使用重载决策,不会在帐户中使用返回类型

  

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

在您的示例中,编译器将Action<T>Func<T, T>视为最佳匹配 Add。这增加了两个可能的选择,因为它需要一个 - 发出适当的错误。

答案 1 :(得分:0)

以正确的方式尝试过载:

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

答案 2 :(得分:0)

我无法回答为什么,但要解决您可以明确转换功能的歧义:

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

或使用lambda

invoice.Items.Run(x => db.InvoiceItems.Add(x));

答案 3 :(得分: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>超载时。