Когда и в каком сценарии использовать Дерево выражений
Я читал о функции дерева выражений и о том, как вы можете создавать делегаты, используя лямбда-выражения. Я до сих пор не могу понять, в каком сценарии это полезно и в каком реальном мире я должен использовать его.
Ответы
Ответ 1
Основное использование для деревьев выражений - для поставщиков LINQ, отличных от процесса, таких как LINQ to SQL.
Когда вы пишете что-то вроде этого:
var query = people.Where(x => x.Age > 18)
.Select(x => x.Name);
эти лямбда-выражения могут быть преобразованы в делегаты, которые затем могут быть выполнены (как они есть в LINQ to Object), или они могут быть преобразованы в деревья выражений, которые могут быть проанализированы источником запроса и действуют соответственно (например, путем превращения их в SQL, вызовы веб-сервисов и т.д.). Разница в том, что деревья выражений представляют код как данные. Они могут быть скомпилированы в делегаты, если это необходимо, но обычно (в LINQ в любом случае) они никогда не выполняются напрямую - просто проверяются, чтобы найти содержащуюся в них логику.
Деревья выражений также широко используются в среде выполнения динамического языка, где они представляют код, который должен выполняться при вычислении динамического выражения. Деревья выражений хорошо подходят для этого, так как они могут быть скомпонованы и разбиты снова, и после их компиляции полученный IL выполняется JIT-компиляцией как обычно.
Большинство разработчиков никогда не должны связываться с API дерева выражений, хотя у него есть несколько других применений.
Ответ 2
Помимо LINQ, другой очень простой пример использования - извлечь как имя, так и значение свойства. Я использую это в свободном API для проверки объектов передачи данных. Безопаснее передавать один параметр лямбда для определения имени и значения, а не иметь второй строковый параметр для имени и риск того, что разработчики ошибаются.
Вот пример (минус все проверки безопасности и другое домашнее хозяйство):
public Validator<T> Check<T>(Expression<Func<T>> expr) {
// Analyse the expression as data
string name = ((MemberExpression) expr.Body).Member.Name;
// Compile and execute it to get the value
T value = (expr.Compile())();
return new Validator<T>(name, value);
}
Пример использования:
Check(() => x.Name).NotNull.MinLength(1);
Check(() => x.Age).GreaterThan(18);
Ответ 3
Я использовал деревья выражений, чтобы сделать нуль-безопасный оценщик:
string name = myObject.NullSafeEval(x => x.Foo.GetBar(42).Baz.Name, "Default");
Эти методы анализируют и перезаписывают дерево выражений, чтобы вставлять нулевые проверки перед тем, как каждое свойство или метод вызовет путь "путь" к Name
. Если на пути встречается нуль, возвращается значение по умолчанию.
См. реализацию здесь
Деревья выражений также обычно используются, чтобы избежать обращения к свойству путем жесткого кодирования его имени в строке:
private string _foo;
public string Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged(() => Foo);
// Rather than:
// OnPropertyChanged("Foo");
}
}
static string GetPropertyName<T>(Expression<Func<T>> expr)
{
var memberExpr = expr.Body as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("expr", "The expression body must be a member expression");
return memberExpr.Member.Name;
}
protected void OnPropertyChanged<T>(Expression<Func<T>> expr)
{
OnPropertyChanged(GetPropertyName(expr));
}
Это позволяет проверять время компиляции и рефакторинг имен