web-dev-qa-db-ja.com

デリゲートキーワードとラムダ表記

コンパイルしたら、次の間に違いがありますか?

delegate { x = 0; }

そして

() => { x = 0 }

175
MojoFilter

短い答え:いいえ。

関係のない長い回答:

  • ラムダをデリゲート型(FuncActionなど)に割り当てると、匿名デリゲートが取得されます。
  • ラムダをExpression型に割り当てると、匿名デリゲートの代わりに式ツリーが取得されます。その後、式ツリーを匿名のデリゲートにコンパイルできます。

編集:式のリンクを次に示します。

  • System.Linq.Expression.Expression(TDelegate) (ここから開始)。
  • デリゲート(System.Funcなど)のメモリ内のLinqは、 System.Linq.Enumerable を使用します。式を使用したLinq to SQL(およびその他のもの)は、 System.Linq.Queryable を使用します。これらのメソッドのパラメーターを確認してください。
  • ScottGuからの説明 。一言で言えば、Linqインメモリはクエリを解決するための匿名メソッドを生成します。 Linq to SQLは、クエリを表す式ツリーを生成し、そのツリーをT-SQLに変換します。 Linq to Entitiesは、クエリを表す式ツリーを生成し、そのツリーをプラットフォームに適したSQLに変換します。
137
Amy B

私はデビッドの答えが好きですが、私はつまらないと思いました。質問は、「一度コンパイルされる」と言います。これは、両方の式haveがコンパイルされたことを示唆しています。どちらもコンパイルできますが、一方はデリゲートに変換され、もう一方は式ツリーに変換されますか?これは扱いにくいものです。匿名メソッドの別の機能を使用する必要があります。ラムダ式で共有されない唯一のもの。パラメータリストを指定せずに匿名メソッドを指定する場合atこれは、voidを返すデリゲートタイプおよびoutパラメータなしで互換性があります。この知識があれば、2つのオーバーロードを作成して、式を完全に明確にすることができますが、非常に異なるものにすることができます。

しかし、災害が発生します!少なくともC#3.0では、ブロック本体を持つラムダ式を式に変換することはできません。また、本体に代入を持つラムダ式を変換することもできません(戻り値として使用されている場合でも)。これは、C#4.0および.NET 4.0で変更される可能性があり、式ツリーでより多くを表現できます。つまり、MojoFilterがたまたま与えた例では、この2つは常にalmostに変換されます。 (詳細は1分で。)

ただし、ボディを少し変更する場合は、デリゲートパラメータートリックを使用できます。

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

ちょっと待って!十分に巧妙であれば、式ツリーを使用しなくても2つを区別できます。以下の例では、オーバーロード解決ルール(および匿名デリゲートマッチングトリック)を使用しています...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

痛い。子供を思い出してください。ベースクラスから継承したメソッドをオーバーロードするたびに、小さな子猫が泣き始めます。

122
Jon Skeet

上記の2つの例では、違いはありません、ゼロです。

表現:

() => { x = 0 }

は文本体を持つLambda式であるため、式ツリーとしてコンパイルすることはできません。実際、0の後にセミコロンが必要なため、コンパイルすらしません。

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
2
Olmo

デビッドBは正しいです。式ツリーを使用することには利点があることに注意してください。 LINQ to SQLは式ツリーを調べ、それをSQLに変換します。

また、ラムダと式ツリーを使用して、リファクタリングに対して安全な方法でフレームワークにクラスメンバーの名前を効果的に渡すこともできます。 Moq はこの例です。

2
Daniel Plaisted