2010-11-30 12 views
6

ユーザが選択できるフィルタのセットを提供したいと思います。各フィルタはExpression<Func<X, bool>>に対応します。したがって、私は利用可能な項目( 'Joe'、 'Steve'、 'Pete'など)の動的リストを取得し、それらの名前に基づいて「ハードコードされた」フィルタのコレクションを作成し、彼は彼が使用したいフィルターを使用します。私の問題は、ダイナミックリストの文字列値に基づいて式を「ハードコーディング」しようとしても、式は匿名型のハングオフプロパティとして値を格納していることです私はどのようにanonをシリアル化するのか分からない。申し訳ありませんが、これは混乱している場合、私はこれを明確にする方法はあまりよく分かりません。ここでLINQ:値ベースの参照を強制するには?

は私のサンプルコードです:

(x.Name = value(ConsoleApplication1.Program+<>c__DisplayClass3).value) 

とき、私が本当に:

public class Foo 
    { 
     public string Name { get; set; } 
    } 
    static void Main(string[] args) 
    { 
     Foo[] source = new Foo[] 
      { 
       new Foo() { Name = "Steven" } , 
       new Foo() { Name = "John" } , 
       new Foo() { Name = "Pete" }, 
      }; 

      List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>(); 
      foreach (Foo f in source) 
      { 
       Expression<Func<Foo, bool>> exp = x => x.Name == f.Name; 
       filterLst.Add(exp); 
      } 
    } 
} 

私の問題は、私は私の式の本体を見たときに私が見たときに、次のように、それが読み込まということです最初のものは次のようになります。

(x.Name = "Steven") 

(これに私のコードを変更すると、それは正確ですlyが、私は何を得る:

 Expression<Func<Foo, bool>> exp = x => x.Name == "Steven"; 

は、私は式にそれを貼り付ける前に、ローカルの文字列値への私の値を強制的に試してみたが、助けていないようです:

List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>(); 
    foreach (Foo f in source) 
    { 
     string value = f.Name; 
     Expression<Func<Foo, bool>> exp = x => x.Name == value; 
     filterLst.Add(exp); 
    } 

文字列に宣言されたローカル変数を使用していても、何らかの匿名型がまだ見えているのはなぜ(または実際どのように)わかりません。私が望むようにこの作品を作る方法はありますか?

+0

(式ツリービルディングを表示する答えが更新されました) –

+0

もっと複雑なシナリオを簡略化していますか?オブジェクトプロパティの配列をフィルタリングする簡単な方法があります。 –

+0

@Jonas:はい、実際のシナリオをかなり単純化しました。私は常に定数の文字列に対してNameプロパティを常にフィルタリングするつもりはありません。私は実際にはもっと複雑なオブジェクトを扱っています。私は、ユーザーに異なるプロパティまたは複数のプロパティ条件に基づいているかもしれない複数の "フィルタ"コレクションを提供しています。最終的には、これらの「フィルタ」はすべて同じオブジェクトタイプ(式)に抽象化されます。これを最終的にクエリを生成するIQueryableソースリストに追加します。 – Steven

答えて

7

実際には、anon-typeは、変数のキャプチャを実行するために使用しているコンパイラ生成型です。 デリゲートでは、キャプチャを手動で実装することでこの問題を回避できますが、エクスプレッションツリーにコンパイルされたラムダ式は使用できません。

つの選択肢:

  • が、後者は実際にはそれほど悪くないですアノンタイプを処理する方法を

を学ぶExpression.Constantなどを経由してコードに明示的に式ツリーを構築します。通常はMemberExpressionだけですが、私はいくつかのコードを徹底的に詳しく説明していますが、私は表現木のビルドの例を提供することもできますが、私は現時点でPCではなく、iPodの入力にはうまく対応していません...

から質問を読む2番目のオプションよりも最初のオプションを見てください。

ああ、注意してください。問題の最初 foreachのコードは、悪名高いL値のキャプチャの問題の影響を受けやすくなります。)


編集:私はPCを見つけた; P

 var param = Expression.Parameter(typeof(Foo), "x"); 
     var body = Expression.Equal(
      Expression.PropertyOrField(param, "Name"), 
      Expression.Constant(f.Name, typeof(string))); 

     var exp = Expression.Lambda<Func<Foo, bool>>(body, param); 
     filterLst.Add(exp); 
+0

これは私がやる必要があるように見えますが、ラムダを取ってこれを直接生成することを望んでいました...ちょっとした仕事で私を殺してくれるとは思わない:)悪名高いI-Valueキャプチャの問題は何ですか? – Steven

+0

@Steven - 最初のコードブロックで、 '' Pete ''に対する3つのフィルターをすべて見つけることができます。 *変数*(変数の*値*ではない)を取得しているので、ループ変数はループの外側に(技術的に)宣言されており、3つの式の間で共有されています。 –

+0

お返事ありがとうございます。私はそれについて少し考えなければなりませんでしたが、私は理解していると思います。私が正しいとすれば、問題は私が各反復でこれとは異なるバージョンが得られると仮定したことです:value(ConsoleApplication1.Program + <> c__DisplayClass3)(おそらく__DisplayClass4、__DisplayClass5?) "f"参照。ループが終了すると、常に "Pete"という名前になります。私はそれ以来ずっとずっと私が気づいていなかったので、警告に感謝します。 – Steven

4

マルクGravellの答えが正しいこと、および

var filters = 
    from f in source 
    let param = Expression.Parameter(typeof(Foo),"x") 
    select Expression.Lambda<Func<Foo, bool>>(
     Expression.Equal(
      Expression.Property(param, "Name"), 
      Expression.Constant(f.Name)), param); 

しかし、これは通常は必要ありません。 2番目のforループの例は、すべての主要なLINQプロバイダで動作するはずです。定数を使うために表現が必要な理由はありますか?

+0

(これまで編集を編集したこともあります...) –

+0

投稿した後に気づいた。 :P – StriplingWarrior

+0

あなたの努力のためにまだ小道具をくれました;) – Steven

関連する問題