以下代码在VS2010中无法编译,但在VS2012中可以编译而无需更改。VS2010中有问题的行是
names.Select(foo.GetName)
错误CS1928: 'string[]'不包含'Select'的定义,并且最佳扩展方法重载'System.Linq.Enumerable.Select<TSource,TResult>(System.Collections.Generic.IEnumerable
, System.Func<TSource,TResult>)'有一些无效参数。
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)使它成为可能。
但是在错误
之后The type arguments for method 'System.Linq.Enumerable.Select<TSource,TResult>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,TResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
似乎Enumerable.Select
不能推断出foo.GetName
的参数和返回类型。
指定类型,代码将被编译。
以下是3个选项
1。铸造到Func<string,string>
string.Join(", ", names.Select<string,string>(foo.GetName).ToArray())
2。在Select
子句中指定类型作为泛型参数
string.Join(", ", names.Select((Func<string,string>)foo.GetName).ToArray())
3。在匿名委托中显式调用函数。
Console.WriteLine(string.Join(", ", names.Select( name => foo.GetName(name))))
但是正如Jon Skeet在注释中指出的那样,上面将通过创建一个新方法来添加另一个函数调用。
原始回答
为什么这个代码不能在。net 4.0的VS2010中编译?
您没有将Parameter传递给名称。您正在传递方法名称,而不是Func<T1,T2>
。
下面将编译
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编译器中的一个bug,在c# 5编译器中已经修复了。
Console.WriteLine(string.Join(", ", names.Select(foo.GetName)));
是
的语法糖Console.WriteLine(string.Join(", ", names.Select(new Func<string, string>(foo.GetName))));
即使foo。GetName是一个扩展方法。后者可以在VS2010中工作,而前者不能。
c#语言规范的第6.6节在讨论方法的隐式转换时描述了转换如何发生的过程,并说:
注意,这个过程可以导致创建一个委托给扩展方法,如果§7.6.5.1的算法无法找到实例方法,但成功地将E(A)的调用处理为扩展方法调用(§7.6.5.2)。这样创建的委托捕获扩展方法及其第一个参数。
基于此,我完全希望这一行在VS2010和VS2012中都能工作(因为规范中的措辞没有改变),但它没有。所以我推断这是一个bug。
这是在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<string, string>::.ctor(object, native int)
// pops the delegate and the names variable
// calls Linq.Enumerable.Select extension method
// pops the result (IEnumerable<string>)
L_0029: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!1> [System.Core]System.Linq.Enumerable::Select<string, string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>)
// pops comma, the IEnumerable<string>
// pushes joined string
L_002e: call string [mscorlib]System.String::Join(string, class [mscorlib]System.Collections.Generic.IEnumerable`1<string>)
// pops joined string and displays it
L_0033: call void [mscorlib]System.Console::WriteLine(string)
// method finishes
L_0038: ret
正如你所看到的,委托是由对象实例(foo)和方法指针创建的,这也正是VS2010中应该发生的事情。如果你显式地指定委托创建new Func<string, string>(foo.GetName)
,它就会。