2013-01-14 9 views
9

次のコードは、VS2010ではコンパイルされませんが、VS2012では変更されずにコンパイルされます。 「文字列が[]」「を選択」の定義と最高の拡張メソッドのオーバーロード「System.Linq.Enumerable.Select < TSOURCE、TResultが含まれていません:VS2010での問題の行がなぜこのコードは.NET 4.0でVS2010でコンパイルされませんか?

names.Select(foo.GetName) 

エラーCS1928であります>(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.Selectfoo.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)))) 

しかし、ジョンスキートはコメントで指摘したように上記の新しい方法を作成することによって、別の関数呼び出しを追加します。このコードは、.NET 4.0とVS2010でコンパイルできない理由

ORIGINAL回答

あなたはパラメータに名前を渡していません。 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コンパイラの変更ではなく、.NETライブラリの変更だと思います。 –

1

をコンパイルされます私はそれが3にターゲットフレームワークを変更することにより、固定しまったVSS 2010年にはこれと同じ問題を抱えていました。5.ビルドしようとしています。予想どおり、ビルドは失敗しますが、このキックはVSS 2010の内部フラグを開始またはリセットします。今度は.NET 4.0に戻ってVSSが正しく構築されます。

+0

SO、Julsへようこそ。将来的には、解決策を提示したときにユーザーがうまく行かなかったことをユーザーが回答していないことを確認してください。 – Brian

+0

@Brianしかし、すべてが機能しましたか? 3.5に切り替える - コンパイルを試みる - コンパイルが期待通りに失敗する - 4に戻る - VSがキックを起こして作業を開始する。 – GSerg

0

これは、C#5コンパイラで修正されたc#4コンパイラのバグのようです。

Console.WriteLine(string.Join(", ", names.Select(foo.GetName))); 

foo.GetNameは、拡張メソッドであるにもかかわらず

Console.WriteLine(string.Join(", ", names.Select(new Func<string, string>(foo.GetName)))); 

ためのシンタックスシュガーです。後者はVS2010で動作しますが、前者は動作しません。

C#言語仕様のセクション6.6には、メソッドの暗黙的な変換の話をするときの変換が起こるかのプロセスを説明し、以上は言う:このプロセスは、デリゲートの創造につながることができます

に留意されたいです。 §7.6.5.1のアルゴリズムが インスタンスメソッドを見つけることに失敗したが、 拡張メソッド呼出し(7.6.5.2)としてE(A)の呼出しを処理することに成功した場合には、 拡張メソッドに変換する。このようにして作成された代理人 は、拡張メソッドとその最初の引数を取り込みます。これに基づき

(文言が仕様に変更されませんので)私は完全にこの行は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)を明示的に指定すると、そのようになります。

関連する問題