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

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

public static IEnumerable Run(this IEnumerable source, Action 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 Run(this IEnumerable source, Func action) { return source.Select(action).ToList().AsEnumerable(); } 

现在编译器抱怨说“以下方法之间的调用是模糊的……”

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

Eric和Jon已经在回答这个问题时对此进行了解释。 长话短说 – 这就是C#编译器的工作原理; 确切地说,在处理方法组转换时,决定将转换为哪个委托利用重载解析,这不会占用帐户中的返回类型

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

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

尝试以正确的方式过载:

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

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

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

为什么你还需要这些方法? 出了什么问题:

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

这个的可读性要好得多。 除非你有充分的理由需要Run方法,否则我建议不要使用它。 从我所看到的,没有这样的理由,至少对于Action重载。

我无法回答为什么,但为了解决你可以明确表达你的function的歧义:

 invoice.Items.Run((Func)db.InvoiceItems.Add); 

或者使用lambda

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