C# 为什么';这段代码是否在VS2010中使用.NET4.0编译?

C# 为什么';这段代码是否在VS2010中使用.NET4.0编译?,c#,.net,visual-studio-2010,linq,visual-studio-2012,C#,.net,Visual Studio 2010,Linq,Visual Studio 2012,不知何故,下面的代码并没有在VS2010中编译,而是在VS2012中编译,没有任何更改。VS2010中有问题的一行是 names.Select(foo.GetName) 错误CS1928:“字符串[]”不包含“选择”的定义,并且最佳扩展方法重载“System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable,System.Func)”具有一些无效参数 更新的答案 我已经检查了代码片段names.Select(foo.Ge

不知何故,下面的代码并没有在VS2010中编译,而是在VS2012中编译,没有任何更改。VS2010中有问题的一行是

names.Select(foo.GetName)
错误CS1928:“字符串[]”不包含“选择”的定义,并且最佳扩展方法重载“System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable,System.Func)”具有一些无效参数


更新的答案

我已经检查了代码片段
names.Select(foo.GetName)
是否在VS2012中编译,而不是在VS2010中编译

我不知道是什么原因(确切地说是C#5.0或.NET4.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.
二,。在
Select
子句中将类型指定为泛型参数

string.Join(", ", names.Select((Func<string,string>)foo.GetName).ToArray())
但是正如Jon Skeet在评论中指出的那样,上面将通过创建一个新方法来添加另一个函数调用

原始答案

为什么这段代码没有在VS2010中使用.NET4.0编译

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


将汇编以下文件

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

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

看起来这是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))));
Console.WriteLine(string.Join(“,”,names.Select(newfunc(foo.GetName)));
即使foo.GetName是一个扩展方法。后者适用于VS2010,而前者不适用

C#语言规范第6.6节在谈到方法的隐式转换时,描述了转换如何发生的过程,并指出:

请注意,此过程可能导致创建对 扩展方法,如果§7.6.5.1中的算法未能找到 实例方法,但成功地将E(A)的调用处理为 扩展方法调用(§7.6.5.2)。这样创建的委托 捕获扩展方法及其第一个参数

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

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

//逗号
L_0017:ldstr“
//推送名称变量
L_001c:ldloc.1
//foo变量
L_001d:ldloc.0
//将指针推送到扩展方法
L_001e:ldftn字符串ConsoleApplication3.Extensions::GetName(类ConsoleApplication3.Foo,字符串)
//弹出foo实例和扩展方法指针,并按下委托
L_0024:newobj实例void[mscorlib]System.Func`2::.ctor(对象,本机int)
//弹出委托和名称变量
//调用Linq.Enumerable.Select扩展方法
//弹出结果(IEnumerable)
L_0029:调用类[mscorlib]System.Collections.Generic.IEnumerable`1[System.Core]System.Linq.Enumerable::Select(类[mscorlib]System.Collections.Generic.IEnumerable`1,类[mscorlib]System.Func`2)
//pops逗号,IEnumerable
//推动连接的字符串
L_002e:调用字符串[mscorlib]System.string::Join(字符串,类[mscorlib]System.Collections.Generic.IEnumerable`1)
//弹出连接的字符串并显示它
L_0033:调用void[mscorlib]System.Console::WriteLine(字符串)
//方法完成
L_0038:ret

如您所见,委托是由对象实例(foo)和方法指针创建的,这也正是VS2010中应该发生的事情。如果您显式地指定委托创建
newfunc(foo.GetName)
,它就会这样做。

不太好。它们并不是等价的代码——您的代码将创建一个新方法,该方法只委托给GetName,然后使用该方法创建一个委托。。。而原始版本会创建一个直接引用GetName的委托。@JonSkeet:但这是编译的唯一方法,不是吗?虽然扩展方法看起来像实例方法,但它们不是,因此不能总是像实例方法一样使用。@DanielHilgarth:Nope-代码按原样为我编译,不会创建额外的方法。你自己试试:)@JonSkeet:但不是在VS2010中(我刚刚测试过),这是OP的要求。当然-但我的观点是,你建议的解决方案不是一回事,而且你还没有回答为什么代码在C#5编译器上有效,而在C#4编译器上无效的问题。那会有用的,但是它没有回答为什么原始代码使用C#5编译器而不是C#4编译器编译的问题。System.Core从4.0到5.0可能有一些小的变化。让我进一步检查一下。我怀疑这是编译器的更改,而不是.NET库的更改。嗯。。。这两个Select Linq扩展的实现方式如下(源MSDN):.NET Framework->4.5,4,3.5、.NET Framework客户端概要文件->4,3.5 SP1。我曾在VS2012中尝试过切换到每个兼容的框架目标的项目,但从未出现过编译错误。我想你的想法很好。欢迎来到SO,朱尔斯。将来,请确保您的答案不会建议用户尝试您已经给出解决方案的情况下对您无效的东西。@Brian,但一切都有效吗?切换到3.5-尝试编译-编译按预期失败-切换回4-VS收到提示并开始工作。
Console.WriteLine(string.Join(", ", names.Select( name => foo.GetName(name))))
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;
        }
    }
}
Console.WriteLine(string.Join(", ", names.Select(foo.GetName)));
Console.WriteLine(string.Join(", ", names.Select(new Func<string, string>(foo.GetName))));
// 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