为什么这个代码不能用VS 4.0在VS2010中编译?

以下代码不能在VS2010中编译,但在VS2012中编译而不做任何更改。 VS2010中有问题的一行是

names.Select(foo.GetName) 

错误CS1928:’string []’不包含’Select’的定义和最佳扩展方法重载’System.Linq.Enumerable.Select (System.Collections.Generic.IEnumerable ,System。 Func )’有一些无效的参数。

 using System; using System.Linq; namespace ConsoleApplication1 { class Program { static void Main() { var foo = new Foo(); var names = new[] {"Hello"}; Console.WriteLine(string.Join(", ", names.Select(foo.GetName))); } } public class Foo { } static class Extensions { public static string GetName(this Foo foo, string name) { return name; } } } 

更新的答案

我已经检查过代码片段names.Select(foo.GetName)在VS 2012中编译,并且不在VS2010上编译。

我不知道原因(确切地说是C#5.0或.NET 4.5或新API中的新function)使其成为可能。

但是跟着错误

 The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. 

它看起来像Enumerable.Select无法推断foo.GetName的参数和返回类型。

指定类型,代码将编译。

以下是3个选项

1。 转换为Func

 string.Join(", ", names.Select(foo.GetName).ToArray()) 

2。 在Select子句中将类型指定为通用参数

 string.Join(", ", names.Select((Func)foo.GetName).ToArray()) 

3。 在匿名委托中显式调用函数。

  Console.WriteLine(string.Join(", ", names.Select( name => foo.GetName(name)))) 

但正如Jon Skeet在评论中指出的那样,上面将通过创建一个新方法添加另一个函数调用。

原文答案

为什么这个代码不能用VS 4.0在VS 4.0中编译?

您没有将参数传递给名称。 您正在传递方法名称,而不是Func


以下将编译

 Console.WriteLine(string.Join(", ", names.Select( name => foo.GetName(name)))) 

我在VSS 2010中遇到了同样的问题。我通过将目标框架更改为3.5来解决此问题。 然后尝试建立。 正如预期的那样,您的构建将失败但是这一启动或重置VSS 2010中的一些内部标志。现在,切换回.NET 4.0并且VSS将开始正确构建。

 using System; using System.Linq; namespace ConsoleApplication1 { public static class Program { public static void Main() { Foo foo = new Foo(); String[] names = new String[] { "Hello" }; Console.WriteLine(String.Join(", ", names.Select(name => foo.GetName(name)))); } } public class Foo { } public static class Extensions { public static String GetName(this Foo foo, String name) { return name; } } } 

看起来这是c#4编译器中修复的错误。

 Console.WriteLine(string.Join(", ", names.Select(foo.GetName))); 

是一种语法糖

 Console.WriteLine(string.Join(", ", names.Select(new Func(foo.GetName)))); 

即使foo.GetName是一个扩展方法。 后者在VS2010工作,前者没有。

在讨论方法的隐式转换时,C#语言规范的第6.6节描述了转换发生的过程,而不是说:

请注意,如果§7.6.5.1的算法无法找到实例方法但成功处理E(A)的调用作为扩展方法调用,则此过程可以导致创建扩展方法的委托(第7.6节) .5.2)。 这样创建的委托捕获扩展方法及其第一个参数。

基于此,我完全希望这条线在VS2010和VS2012中都能正常工作(因为规范中的措辞不会改变),但事实并非如此。 所以我推断这是一个错误。

这是IL在VS 2012中编译时的样子(评论是我的):

 // pushes comma L_0017: ldstr ", " // pushes names variable L_001c: ldloc.1 // pushes foo variable L_001d: ldloc.0 // pushes pointer to the extension method L_001e: ldftn string ConsoleApplication3.Extensions::GetName(class ConsoleApplication3.Foo, string) // pops the instance of foo and the extension method pointer and pushes delegate L_0024: newobj instance void [mscorlib]System.Func`2::.ctor(object, native int) // pops the delegate and the names variable // calls Linq.Enumerable.Select extension method // pops the result (IEnumerable) L_0029: call class [mscorlib]System.Collections.Generic.IEnumerable`1 [System.Core]System.Linq.Enumerable::Select(class [mscorlib]System.Collections.Generic.IEnumerable`1, class [mscorlib]System.Func`2) // pops comma, the IEnumerable // pushes joined string L_002e: call string [mscorlib]System.String::Join(string, class [mscorlib]System.Collections.Generic.IEnumerable`1) // pops joined string and displays it L_0033: call void [mscorlib]System.Console::WriteLine(string) // method finishes L_0038: ret 

如您所见,委托是从对象实例(foo)和方法指针创建的,这正是VS2010中应该发生的事情。 如果您明确指定委托创建new Func(foo.GetName)