【问题标题】:Why doesn't this code compile in VS2010 with .NET 4.0?为什么这段代码不能在 .NET 4.0 的 VS2010 中编译?
【发布时间】:2013-01-14 14:39:38
【问题描述】:

不知何故,以下代码无法在 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;
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

    更新答案

    我检查了代码 sn-p 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 的参数和返回类型。

    指定类型,代码将编译。

    以下是三个选项

    1 .投射到Func&lt;string,string&gt;

    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 在 cmets 中指出的那样,上面将通过创建一个新方法来添加另一个函数调用。

    原始答案

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

    您没有将参数传递给名称。您正在传递方法名称,而不是 Func&lt;T1,T2&gt;


    以下将被编译

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

    【讨论】:

    • 不完全是。它们不是等效的代码,相当 - 您的代码将创建一个仅委托给 GetName 的新方法,然后使用该方法创建一个委托......而原始代码将创建一个直接引用 GetName 的委托。
    • @JonSkeet:但这是编译的唯一方法,不是吗?虽然扩展方法看起来像实例方法,但它们不是,因此不能总是像它们一样使用。
    • @DanielHilgarth:不——代码原样为我编译,创建一个额外的方法。自己试试吧:)
    • @JonSkeet:但不是在 VS2010 中(我刚刚测试过),这是 OP 所要求的。
    • 当然 - 但我的意思是,您建议的解决方案不是一回事,而且您还没有真正回答 为什么 代码对 C# 有效的问题5 编译器,但不是 C# 4 编译器。
    【解决方案2】:

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

    【讨论】:

    • 欢迎来到 SO,Juls。今后,请确保您的回答不会建议用户尝试在您已经为他们提供解决方案时对您不起作用的东西。
    • @Brian 但是一切正常吗?切换到 3.5 - 尝试编译 - 编译按预期失败 - 切换回 4 - VS 启动并开始工作。
    【解决方案3】:
    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# 5 编译器而不是 C# 4 编译器编译的问题。
    • System.Core 从 4.0 到 5.0 可能有一些小的变化。让我进一步检查一下。
    • 我怀疑这是 编译器 更改,而不是 .NET 库更改。
    • 嗯...两个 Select Linq 扩展都实现如下(来源 MSDN):.NET Framework -> 4.5, 4, 3.5, .NET Framework Client Profile -> 4, 3.5 SP1。我已经尝试过将 VS2012 中的项目切换到每个兼容的框架目标,但我从未遇到过编译错误。我认为你的想法很好。
    【解决方案4】:

    看起来这是 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 中有效,前者在 VS2010 中无效。

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

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

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

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

    // 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&lt;string, string&gt;(foo.GetName),它会这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-06
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-27
      相关资源
      最近更新 更多