5

Jon Skeetによる「奇妙なクエリ式」を読んだ後、以下のコードを試しました。最後にLINQクエリが変換されることを期待していましたが、が返されるint query = proxy.Where(x => x).Select(x => x);ため、コンパイルされません。コードがコンパイルされ、「Where(x => x)」が画面に出力され、クエリが2に設定されます。Selectが呼び出されることはありませんが、コードをコンパイルするにはそこにある必要があります。何が起こっている?Whereint

using System;
using System.Linq.Expressions;

public class LinqProxy
{
    public Func<Expression<Func<string,string>>,int> Select { get; set; }
    public Func<Expression<Func<string,string>>,int> Where { get; set; }
}

class Test
{
    static void Main()
    {
        LinqProxy proxy = new LinqProxy();

        proxy.Select = exp => 
        { 
            Console.WriteLine("Select({0})", exp);
            return 1;
        };
        proxy.Where = exp => 
        { 
            Console.WriteLine("Where({0})", exp);
            return 2;
        };

        int query = from x in proxy
                    where x
                    select x;
    }
}
4

2 に答える 2

12

これは、「selectx」が事実上ノーオペレーションであるためです。コンパイラはわざわざSelect(x => x)最後に呼び出しを行うことはありません。ただし、条項を削除した場合そうなります。where現在のクエリは、縮退クエリ式と呼ばれます。詳細については、C#4仕様のセクション7.16.2.3を参照してください。特に:

縮退したクエリ式は、ソースの要素を簡単に選択する式です。翻訳の後半のフェーズでは、他の翻訳手順で導入された縮退クエリを、それらをソースに置き換えることで削除します。ただし、クエリ式の結果がソースオブジェクト自体にならないようにすることが重要です。これにより、クエリのクライアントにソースのタイプとIDが明らかになります。したがって、この手順では、ソースでSelectを明示的に呼び出すことにより、ソースコードに直接記述された縮退クエリを保護します。これらのメソッドがソースオブジェクト自体を返さないようにするのは、Selectおよびその他のクエリ演算子の実装者次第です。

したがって、3つの翻訳(データソースに関係なく)

// Query                          // Translation
from x in proxy                   proxy.Where(x => x)
where x
select x


from x in proxy                   proxy.Select(x => x)
select x               


from x in proxy                   proxy.Where(x => x)
where x                                .Select(x => x * 2)
select x * 2
于 2010-09-30T20:09:16.407 に答える
7

LINQクエリ構文は字句置換であるため、コンパイルされます。コンパイラが変わります

int query = from x in proxy
            where x
            select x;

の中へ

int query = proxy.Where(x => x);     // note it optimises the select away

次に、メソッドとが実際にのタイプに存在するかどうかをチェックしWhereます。したがって、あなたが与えた特定の例では、これがコンパイルされるために実際に存在する必要はありません。SelectproxySelect

あなたがこのようなものを持っていた場合:

    int query = from x in proxy
                select x.ToString();

その後、次のように変更されます。

int query = proxy.Select(x => x.ToString());

そして、Selectメソッドが呼び出されます。

于 2010-09-30T20:09:37.130 に答える