Когда и в каком сценарии использовать Дерево выражений

Я читал о функции дерева выражений и о том, как вы можете создавать делегаты, используя лямбда-выражения. Я до сих пор не могу понять, в каком сценарии это полезно и в каком реальном мире я должен использовать его.

Ответы

Ответ 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));
}

Это позволяет проверять время компиляции и рефакторинг имен