2010-08-20 11 views

Odpowiedz

12

Podstawowym zastosowaniem drzewek wyrażeń jest poza procesami dostawców LINQ, takich jak LINQ do SQL.

Kiedy piszesz coś takiego:

var query = people.Where(x => x.Age > 18) 
        .Select(x => x.Name); 

te wyrażenia lambda może albo być konwertowane do delegatów, które następnie mogą być wykonane (są one w LINQ do obiektu) lub mogą być konwertowane do drzewek wyrażeń, które mogą być analizowane przez źródło zapytań i odpowiednio do nich stosowane (np. przez przekształcenie ich w SQL, wywołania usługi sieciowej itp.). Różnica polega na tym, że drzewa wyrażenia reprezentują kod jako danych. W razie potrzeby można je skompilować w delegatów, ale zwykle (w każdym razie w LINQ) nie są wykonywane bezpośrednio - po prostu są sprawdzane, aby znaleźć logikę, którą zawierają.

Drzewa wyrażeń są również szeroko używane w środowisku wykonawczym języka dynamicznego, gdzie reprezentują kod, który powinien zostać wykonany podczas oceny wyrażenia dynamicznego. Drzewa ekspresji są do tego odpowiednie, ponieważ można je ponownie skomponować i zepsuć, a po ich skompilowaniu uzyskany IL jest kompilowany JIT jak zwykle.

Większość deweloperów nigdy nie będzie musiała mieszać z API drzewa wyrażeń, chociaż ma kilka innych zastosowań.

+0

Dzięki Jon. Chciałbym uzyskać przykład z życia, w którym mogę go użyć. Dostałem tę koncepcję nieco, ale jeśli nie mogę wymyślić scenariusza, w którym używanie drzewa Expression jest lepsze niż konwencjonalny sposób działania, nie będę czuł się komfortowo w moim umyśle. – DotNetInfo

+0

@Nimesh: It * can * może czasami być przydatny w tworzeniu programów odpornych na refaktory, które muszą korzystać z refleksji. Określając właściwość poprzez wyrażenie lambda skonwertowane na delegata zamiast nazwy (jako łańcuch), można dodać ciąg znaków w czasie wykonywania i mieć pewność, że jest poprawny. Ale jest to naprawdę poważny przypadek - dotyczy głównie LINQ i DLR (edycja odzwierciedla to drugie). –

5

Oprócz LINQ, kolejnym bardzo prostym przypadkiem jest wyodrębnienie zarówno nazwy, jak i wartości właściwości. Używam tego w płynnym API do sprawdzania poprawności obiektów transferu danych. Bezpieczniej jest przekazać jeden parametr lambda, aby zdefiniować zarówno nazwę, jak i wartość, zamiast mieć drugi parametr dla nazwy i ryzykować, że programiści popełnią błąd.

Oto przykład (minus wszystkie kontrole bezpieczeństwa i inny personel sprzątający):

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); 
} 

Przykład zastosowania:

Check(() => x.Name).NotNull.MinLength(1); 
Check(() => x.Age).GreaterThan(18); 
+0

Bardzo ładne API sprawdzania poprawności, chociaż wydajność prawdopodobnie nie jest tak dobra z powodu kompilacji wyrażeń –

+0

Tak, wystąpi narzut wydajności. W sumie pomyślałem, że zysk z niezawodności był tego wart. Kompilacja jest wykonywana tylko raz dla każdej właściwości, a następnie można młotkować skonstruowany obiekt walidatora tak bardzo, jak chcesz. –

4

użyłem wyrażenia drzew, aby null-safe oceniającego:

string name = myObject.NullSafeEval(x => x.Foo.GetBar(42).Baz.Name, "Default"); 

Ta metoda analizuje i przepisuje drzewo wyrażeń, aby wstawiać sprawdzenia zerowe przed każdym pro perty lub wywołanie metody wzdłuż "ścieżki" do Name. Jeśli napotkana zostanie wartość pusta po drodze, zwracana jest wartość domyślna.

Zobacz realizacja here

ekspresyjne drzewa są również powszechnie stosowane w celu uniknięcia odnoszące się do nieruchomości przez twarde kodowania jej nazwę w ciąg:

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)); 
} 

To pozwala skompilować czasu sprawdzania i nazwisko refaktoringu

+0

Cool. Gdyby tylko jakiś rodzaj odpowiednika syntaktycznego został wbudowany w język ...

+0

@ Christiana Haytera: Czy mógłbyś pokazać przykład? – Douglas

+0

@Douglas: Przykład czego? –

Powiązane problemy