[英]Why doesn't this code compile in VS2010 with .NET 4.0?
不知怎的,下面的代碼不能在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;
}
}
}
更新的答案
我已經檢查過代碼片段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在VS 4.0中編譯?
您沒有將參數傳遞給名稱。 您正在傳遞方法名稱,而不是Func<T1,T2>
。
以下將編譯
Console.WriteLine(string.Join(", ", names.Select( name => foo.GetName(name))))
我在VSS 2010中遇到了同樣的問題。我通過將目標框架更改為3.5來解決此問題。 然后試圖建立。 正如預期的那樣,您的構建將失敗但是此踢法啟動或重置VSS 2010中的一些內部標志。現在,切換回.NET 4.0並且VSS將開始正確構建。
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#4編譯器中修復的c#4編譯器中的錯誤。
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)
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.