9

不知何故,以下代码不会在 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

4 回答 4

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

原始答案

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

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


以下将被编译

Console.WriteLine(string.Join(", ", names.Select( name => foo.GetName(name))))
于 2013-01-14T14:41:46.920 回答
1

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

于 2013-07-25T23:07:21.953 回答
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 中都可以工作(因为规范中的措辞没有改变),但事实并非如此。所以我推断这是一个错误。

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

// 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)会这样做。

于 2013-07-26T00:46:45.783 回答
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;
        }
    }
}
于 2013-01-14T14:53:46.887 回答