Доступ к значению выражения члена - PullRequest
59 голосов
/ 11 апреля 2010

Если у меня есть продукт.

var p = new Product { Price = 30 };

и у меня есть следующий запрос linq.

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

В провайдере IQueryable я получаю выражение MemberExpression для p.Price, которое содержит выражение константы, однако я не могу получить от него значение «30».

Обновление Я пробовал это, но это не похоже на работу.

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

Приветствие.

Ответы [ 8 ]

97 голосов
/ 11 апреля 2010

Вы можете скомпилировать и вызвать лямбда-выражение, тело которого является доступом к члену:

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 делает это точно во многих местах.

28 голосов
/ 10 декабря 2010
 MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
 Expression.Lambda(right).Compile().DynamicInvoke();
24 голосов
/ 11 апреля 2010

Постоянное выражение будет указывать на класс захвата, сгенерированный компилятором. Я не включил пункты решения и т. Д., Но вот как получить 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);

price теперь 30. Обратите внимание, что я предполагаю, что Price является свойством, но на самом деле вы бы написали GetValue метод, который обрабатывает свойство / поле.

1 голос
/ 18 мая 2017

Использование Expression.Lambda(myParameterlessExpression).Compile().Invoke() имеет несколько недостатков:

  • .Compile() - это медленно . Это может занять несколько миллисекунд даже для небольших фрагментов выражения. Впоследствии Invoke -вызов является сверхбыстрым, для простых арифметических выражений или доступа к элементам требуется всего несколько наносекунд.
  • .Compile() будет генерировать (испускать) код MSIL. Это может звучать идеально (и объясняет превосходную скорость выполнения), но проблема в том, что этот код занимает память, , которую нельзя освободить до завершения работы приложения , даже когда GC собрал ссылку на делегат!

Можно либо вообще избежать Compile(), чтобы избежать этих проблем, либо кэшировать скомпилированные делегаты для их повторного использования. Эта небольшая моя библиотека предлагает как интерпретацию из Expressions, так и кэшированную компиляцию , где все константы и замыкания выражения автоматически заменяются дополнительными параметрами , которые затем повторно вставляются в замыкание, которое возвращается пользователю. Оба процесса хорошо протестированы, используются в производстве, оба имеют свои плюсы и минусы друг против друга, но намного более чем в 100 раз быстрее, чем Compile() - и позволяют избежать утечки памяти!

1 голос
/ 11 апреля 2010

Можете ли вы использовать следующее:

var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
1 голос
/ 11 апреля 2010

q относится к типу List<Product>. В списке нет свойства Price - только отдельные товары.

Первый или последний Товар будет иметь цену.

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

Если вы знаете, что в коллекции есть только один, вы также можете выровнять его с помощью Single

q.Single().Price
0 голосов
/ 22 ноября 2017

Если у вас был класс:

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 голосов
/ 11 апреля 2010

А что именно вы пытаетесь достичь?

Поскольку для доступа к значению Price вам нужно сделать что-то вроде:

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