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

时间:2013-01-14 14:39:38

标签: c# .net visual-studio-2010 linq visual-studio-2012

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

names.Select(foo.GetName)
  

错误CS1928:'string []'不包含'Select'的定义和最佳扩展方法重载'System.Linq.Enumerable.Select< TSource,TResult>(System.Collections.Generic.IEnumerable< TSource> ,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;
        }
    }
}

4 个答案:

答案 0 :(得分:3)

更新了答案

我已检查过代码段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在评论中指出的那样,上面将通过创建一个新方法来添加另一个函数调用。

原始回答

  

为什么此代码无法在VS 4.0中使用.NET 4.0进行编译?

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


以下将编译

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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;
        }
    }
}

答案 3 :(得分:0)

看起来这是c#4编译器中的错误,已在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中都能正常工作(因为规范中的措辞不会改变),但事实并非如此。所以我推断这是一个错误。

这是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<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),则会这样做。