Анализ дерева выражений Lambda
Я пытаюсь использовать Lambda Expressions в проекте для сопоставления с API сторонних запросов. Итак, я разбираю дерево выражений вручную.
Если я передам лямбда-выражение, например:
p => p.Title == "title"
все работает.
Однако, если мое лямбда-выражение выглядит следующим образом:
p => p.Title == myaspdropdown.SelectedValue
Используя отладчик .NET, я не вижу фактического значения этой функции. Вместо этого я вижу что-то вроде:
p => p.Title = (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
Что дает? И когда я пытаюсь захватить правую часть выражения в виде строки, я получаю (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
вместо фактического значения. Как получить фактическое значение?
Ответы
Ответ 1
Помните, что когда вы используете выражение лямбда как дерево выражений, у вас нет исполняемого кода. Скорее у вас есть дерево элементов выражения, которые составляют написанное вами выражение.
Чарли Калверт хороший пост, который подробно обсуждает это. Включен пример использования визуализатора выражения для отладки выражений.
В вашем случае, чтобы получить значение правой части выражения равенства, вам нужно создать новое выражение лямбда, скомпилировать его и затем вызвать.
Я взломал вместе быстрый пример этого - надеюсь, что он поставляет то, что вам нужно.
public class Class1
{
public string Selection { get; set; }
public void Sample()
{
Selection = "Example";
Example<Book, bool>(p => p.Title == Selection);
}
public void Example<T,TResult>(Expression<Func<T,TResult>> exp)
{
BinaryExpression equality = (BinaryExpression)exp.Body;
Debug.Assert(equality.NodeType == ExpressionType.Equal);
// Note that you need to know the type of the rhs of the equality
var accessorExpression = Expression.Lambda<Func<string>>(equality.Right);
Func<string> accessor = accessorExpression.Compile();
var value = accessor();
Debug.Assert(value == Selection);
}
}
public class Book
{
public string Title { get; set; }
}
Ответ 2
Чтобы получить фактическое значение, вам нужно применить логику дерева выражений к любому контексту, который у вас есть.
Вся суть деревьев выражений состоит в том, что они представляют логику как данные, а не оценивают выражение. Вам нужно будет понять, что означает выражение лямбда. Это может означать оценку некоторых ее частей в отношении локальных данных - вам нужно решить это для себя. Деревья выражений очень мощные, но не просто проанализировать и использовать их. (Спросите любого, кто написал поставщика LINQ... Франс Бума оплакивал трудности несколько раз.)
Ответ 3
Просто борется с той же проблемой, благодаря Бевану. На добавочном номере приведен общий шаблон, который вы можете использовать для извлечения значения (используя это в моем механизме запросов).
[TestFixture]
public class TestClass
{
[Test]
public void TEst()
{
var user = new User {Id = 123};
var idToSearch = user.Id;
var query = Creator.CreateQuery<User>()
.Where(x => x.Id == idToSearch);
}
}
public class Query<T>
{
public Query<T> Where(Expression<Func<T, object>> filter)
{
var rightValue = GenericHelper.GetVariableValue(((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right.Type, ((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right);
Console.WriteLine(rightValue);
return this;
}
}
internal class GenericHelper
{
internal static object GetVariableValue(Type variableType, Expression expression)
{
var targetMethodInfo = typeof(InvokeGeneric).GetMethod("GetVariableValue");
var genericTargetCall = targetMethodInfo.MakeGenericMethod(variableType);
return genericTargetCall.Invoke(new InvokeGeneric(), new[] { expression });
}
}
internal class InvokeGeneric
{
public T GetVariableValue<T>(Expression expression) where T : class
{
var accessorExpression = Expression.Lambda<Func<T>>(expression);
var accessor = accessorExpression.Compile();
return accessor();
}
}
Ответ 4
Я не уверен, что понимаю. Где вы "видите" это? Это во время разработки или времени выполнения? Лямбда-выражения можно рассматривать как анонимные делегаты и будут работать с отсроченным исполнением. Таким образом, вы не должны ожидать, что значение будет присвоено до тех пор, пока, конечно, выполнение этой строки не пройдет.
Я не думаю, что это действительно то, что вы имеете в виду... если вы уточните вопрос, может быть, я могу помочь:)