web-dev-qa-db-ja.com

メンバー式の値にアクセスする

製品がある場合。

var p = new Product { Price = 30 };

そして、私は次のlinqクエリを持っています。

var q = repo.Products().Where(x=>x.Price == p.Price).ToList()

IQueryableプロバイダーで、定数式を含むp.PriceのMemberExpressionを取得しますが、値 "30"を取得できないようです。

更新これを試しましたが、うまくいかないようです。

var memberExpression = (MemberExpression)GetRootConstantExpression(m);
var fi = (PropertyInfo)memberExpression.Member;
var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null);

乾杯。

61
Schotime

ボディがメンバーアクセスであるラムダ式をコンパイルして呼び出すことができます。

private object GetValue(MemberExpression member)
{
    var objectMember = Expression.Convert(member, typeof(object));

    var getterLambda = Expression.Lambda<Func<object>>(objectMember);

    var getter = getterLambda.Compile();

    return getter();
}

ローカル評価は、式ツリーを解析する際の一般的な手法です。 LINQ to SQLは、非常に多くの場所でこの正確なことを行います。

100
Bryan Watts
 MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
 Expression.Lambda(right).Compile().DynamicInvoke();
29
Glennular

定数式は、コンパイラーによって生成されたキャプチャークラスを指します。決定点などは含めていませんが、そこから30を取得する方法は次のとおりです。

var p = new Product { Price = 30 };
Expression<Func<Product, bool>> predicate = x => x.Price == p.Price;
BinaryExpression eq = (BinaryExpression)predicate.Body;
MemberExpression productToPrice = (MemberExpression)eq.Right;
MemberExpression captureToProduct = (MemberExpression)productToPrice.Expression;
ConstantExpression captureConst = (ConstantExpression)captureToProduct.Expression;
object product = ((FieldInfo)captureToProduct.Member).GetValue(captureConst.Value);
object price = ((PropertyInfo)productToPrice.Member).GetValue(product, null);

price30Priceがプロパティであると仮定していることに注意してくださいが、実際には、プロパティ/フィールドを処理するGetValueメソッドを記述します。

24
Marc Gravell

qはタイプList<Product>。リストには価格プロパティはありません-個々の製品のみです。

最初または最後の製品には価格があります。

q.First().Price
q.Last().Price

コレクションに1つしかない場合は、Singleを使用してフラット化することもできます

q.Single().Price
1
Kirk Broadhurst

次のものを使用できますか?

var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
1
Henk Holterman

Expression.Lambda(myParameterlessExpression).Compile().Invoke()を使用すると、いくつかの欠点があります。

  • .Compile()slowです。小さな式のフラグメントであっても、完了するまでに数ミリ秒かかる場合があります。 Invoke- callはその後非常に高速で、単純な算術式またはメンバーアクセスに数ナノ秒しかかかりません。
  • .Compile()は、MSILコードを生成(発行)します。それは完璧に聞こえるかもしれません(そして優れた実行速度を説明します)が、問題は次のとおりです:そのコードはメモリを占有します。これはアプリケーションが終了する前に解放できません 、GCがデリゲート参照を収集した場合でも!

Compile()を完全に回避してこれらの問題を回避するか、コンパイルされたデリゲートをキャッシュして再利用できます。 これ 私の小さなライブラリはExpressions解釈キャッシュされたコンパイルの両方を提供します、式のすべての定数とクロージャーが追加のパラメーターに自動的に置き換えられます。これらのパラメーターはクロージャーに再挿入され、ユーザーに返されます。両方のプロセスは十分にテストされ、本番環境で使用されており、長所と短所がありますが、Compile()よりも100倍以上高速であり、メモリリークを回避します。

1

そして、あなたは正確に何を達成しようとしていますか?

Priceの値にアクセスするには、次のようなことをする必要があります。

var valueOfPrice = q[0].Price;
0
Paulo Santos

クラスがある場合:

public class Item
{
    public int Id { get; set; }
}

およびオブジェクトのインスタンス:

var myItem = new Item { Id = 7 };

次のコードを使用して、式を使用してIdの値を取得できます。

Expression<Func<Item, int>> exp = x => x.Id;
var me = exp.Body as MemberExpression;
var propInfo = me.Member as PropertyInfo;
var value = propInfo.GetValue(myItem, null);

値には「7」が含まれます

0
t_warsop