2013-01-14 189 views
9

不知何故,下面的代碼不會在VS2010中編譯,而是在VS2012中編譯而不做任何更改。在VS2010有問題的線路是爲什麼不使用.NET 4.0在VS2010中編譯此代碼?

names.Select(foo.GetName) 

錯誤CS1928:「串[]」不包含「選擇」 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; 
     } 
    } 
} 

回答

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。在匿名委託中顯式調用Function。

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

但是,正如Jon Skeet指出的那樣,上面的代碼會通過創建一個新方法來添加另一個函數調用。

原來的答案

爲什麼這個代碼不VS2010與.NET 4.0編譯?

您未將參數傳遞給名稱。您正在傳遞方法名稱,而不是Func<T1,T2>


以下將被編譯

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

不太好。它們不是等價的代碼,相當 - 你的代碼將創建一個新方法,它只是委託給GetName,然後使用該方法創建一個委託...而原始代碼將創建一個直接引用GetName的委託。 –

+0

@JonSkeet:但這是編譯的唯一方法,不是嗎?雖然擴展方法看起來像實例方法,但它們不是,因此不能總是像它們一樣使用。 –

+0

@DanielHilgarth:沒有 - 代碼是爲我編譯的,*不*創建一個額外的方法。嘗試一下:) –

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

這將工作,但它不回答爲什麼原始代碼與C#5編譯器編譯而不是C#4編譯器編譯的問題。 –

+0

可能在System.Core中有小的變化,從4.0到5.0。讓我進一步檢查。 –

+2

我懷疑這是*編譯器*更改,而不是.NET庫更改。 –

1

我曾在2010年VSS這個同樣的問題,我把它固定,通過改變目標框架3。5.然後試圖建立。正如預期的那樣,您的構建將失敗,但是這個腳本啓動或重置VSS 2010中的某些內部標誌。現在,切換回.NET 4.0,VSS將開始正確構建。

+0

歡迎來到SO,Juls。將來,請確保您的答案沒有建議用戶嘗試某些在您給他們解決方案時不起作用的東西。 – Brian

+0

@布萊恩但一切工作?切換到3.5 - 嘗試編譯 - 編譯失敗,如預期 - 切換回4 - VS獲得一個踢並開始工作。 – GSerg

0

看起來它是c#5編譯器中修復的c#4編譯器中的一個bug。

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 

正如你所看到的,是創造出來的對象實例(富)和方法指針的委託,而這正是VS2010中應該發生的事情。如果您明確指定了代表創建new Func<string, string>(foo.GetName),則會發生這種情況。

相關問題