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

C# 为什么C的重载解析不能在Func和Action之间工作?,c#,linq,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; } } 。。。编译器抱怨它的返回类型

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

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
    {
        action(item);
        yield return item;
    }
}
。。。编译器抱怨它的返回类型错误,因为它需要一个void方法。因此,为采用Func而非操作的运行添加重载:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Func<T, T> action)
{
    return source.Select(action).ToList().AsEnumerable();
}
现在编译器抱怨调用在以下方法之间不明确


因此,我的问题是,当Run方法的操作重载对方法组无效时,它如何导致歧义?

请以正确的方式尝试重载:

public static IEnumerable<TDest> Run<TSource, TDest>(this IEnumerable<TSource> source, 
    Func<TSource, TDest> action) 
{ 
 return source.Select(action).ToList(); 
} 
我无法回答为什么,但为了解决歧义,您可以显式强制转换函数:

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

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

// 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));

这本书的可读性要好得多。除非您有充分的理由需要Run方法,否则我建议您不要使用它。从我所看到的来看,没有这样一个原因,至少对于动作过载来说是这样。

埃里克和乔恩在回答问题时已经解释了这一点。长话短说——这就是C编译器的工作原理;确切地说,在处理方法组转换时,决定将其转换为哪个委托时使用重载解析,而重载解析不考虑返回类型:

<> P>这里的原则是,确定方法组可兑换需要使用过载解决方法从方法组中选择方法,并且过载分辨率不考虑返回类型。
在您的示例中,编译器将Action和Func视为Add的最佳匹配。这总共有两个可能的选择,并且由于需要一个,因此会发出相应的错误。

db.InvoiceItems.Add的签名是什么?简短回答:x=>x.ToString这个lambda应该简单地调用ToString还是调用ToString并返回其结果?换句话说,这个lambda应该作为func或action处理吗?编译器无法为您做出此决定,因此出现错误。@Polity但此处没有lambda。从一个方法组转换为一个委托永远不能将一个返回到某个对象的方法更改为一个无效的返回委托。@svick-Lambdas是.NET生态系统中的奇怪公民。只有它们依赖于声明的类型,因此不允许使用变量。分配给操作的lambda不会返回。当重载解析必须在操作和func之间进行选择时,这真是一个丑陋的选择。现在,我假设*编译器提前检测到这些问题,并因此生成错误。@Polity我相信Eric Lippert对roken链接的问题的回答很好地描述了这个问题。没有任何区别。您应该删除此处的.ToList,以避免执行query@SteveB方法名运行时,但并非Lazyrun是一种常见的函数式声明性操作,我不同意foreach表单更具可读性。此外,一旦你把大括号放进去,它是四行而不是一行。我为六个儿童收藏品做这件事;在每个foreach之间添加一个空行,即大约30行代码,这使得包含的方法太长,因此我将每个foreach重构为一个单独的方法。然后我重构该方法以保持干燥,嘿,普雷斯托,反正我有一个Run方法。@MarkRendle你的Run功能不太好。函数式编程的很大一部分是编写没有副作用的函数。而Run只对副作用有用。我同意Tim的观点:foreach更具可读性,使用Run之类的方法不是一个很好的做法。@svick我不同意。@svick有很多语言和/或框架,函数式的和其他的,它们都有类似Run方法/函数的东西;想想Ruby和Python中的each方法,或者Javascript中的array.forEach方法,或者Scheme中的for each标准库函数。@MarkRendle还有一个原因,就是在IEnumerable上并没有这样的扩展方法。
invoice.Items.Run(x => db.InvoiceItems.Add(x));
// 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);