2010-10-04 3 views
9

przeszedłem przez chwilę i uderzyłem głową o ścianę, szukając teraz różnych fraz i słów kluczowych, ale nie mogę znaleźć niczego bliskiego odpowiedzi więc mam nadzieję, że ktoś tutaj może rzucić trochę światła.Dlaczego nie mogę ręcznie utworzyć tego samego drzewa wyrażeń, aby moja prosta lambda generowała

Zasadniczo pracuję na nurkowanie dość głęboko do manipulowania, tworzenia i modyfikowania Expression Trees w C# 4.0

natknąłem nieparzystej anomalii nie mogę zrozumieć

jeśli piszę coś takiego

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That"; 

Kiedy się debugowania i patrzeć na drzewa wyrażenie wygląda podobnie do tego

  • F (NodeType = Lambda)
    • ciała (NodeType = Dodaj)
      • lewo (NodeType = Dodaj)
        • lewo (NodeType = Constant, Value = "To")
        • Right (NodeType = Warunkowo)
          • IfFalse (NodeType = ADD)
            • Lewa (NODETYPE = parametr name = "Insert")
            • Right (NodeType = Constant, Value = "„)
          • IfTrue (NodeType = Constant, Value = "")
          • Test (NodeType = Równe)
            • lewo (NodeType = parametr Nazwa = "Insert")
            • Right (NodeType = Constant, Value = "")
      • Right (NodeType = Constant, Value = "To")
    • Paramerters (count = 1)
      • Parametry [0] (NodeType = parametr Name = "Insert")

mogę zadzwonić

Console.WriteLine(InsertAString.Compile()("Is Something In-between")); 

i wyjdę jak oczekuję

„To jest coś w międzyczasie, że”

Teraz, gdy próbuję i odbudować to ręcznie za pomocą statycznych metod klasy bazowej Expression biegnę do ciekawej kwestii.(Mam podziale każdy krok do własnej ekspresji dla celów debugowania)

ParameterExpression Insert = Expression.Parameter(typeof(object), "Insert"); 
ConstantExpression This = Expression.Constant("This "); 
ConstantExpression That = Expression.Constant("That"); 
ConstantExpression Space = Expression.Constant(" "); 
ConstantExpression NoCharacter = Expression.Constant(""); 
BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space); 
BinaryExpression InsertEqualsNoCharacter = Expression.Equal(Insert,NoCharacter); 
ConditionalExpression InsertPlusSpaceOrNothing = Expression.IfThenElse(InsertEqualsNoCharacter,NoCharacter,InsertPlusSpace); 
BinaryExpression ThisPlusInsertPlusSpaceOrNothing = Expression.Add(This,InsertPlusSpaceOrNothing); 
BinaryExpression ThisPlusInsertPlusSpaceOrNothingPlusThat = Expression.Add(ThisPlusInsertPlusSpaceOrNothing, That); 
Lambda Lambda = Expression.Lambda(ThisPlusInsertPlusSpaceOrNothingPlusThat, Middle); 
Expression<Func<string,string>> InsertAString = Lambda as Expression<Func<string,string>> 

że na podstawie wartości wygenerowanego drzewa Expression powyżej odtworzenie samego drzewa podstawowe wyrażenia jak wyżej (przynajmniej z tego samego „Look”)

Wszystko przez drobne kroki, aż dojdziesz do tej linii

BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space); 

kompilator generuje InvalidOperationException był nieobsługiwany

Operator binarny dodawania nie jest zdefiniowana dla 'System.String' i 'System.String'

Teraz Dlaczego tak jest?

Dlaczego, gdy pozwalam C# przekonwertować Lambda na Expression, to oczywiście użyć Add NodeType, a ekran Typów pokazuje, że zdecydowanie używa System.String, ale kiedy spróbuję zrobić to samo ręcznie, to nie pozwoli, aby kod dalej?

Jako ostatnia uwaga ja nawet próbowałem następujące:

BinaryExpression InsertPlusSpace = Expression.MakeBinary(ExpressionType.Add,Insert,Space); 

sam błąd.

Jestem ciekawy, dlaczego wydaje się co najmniej z tego, co udało mi się znaleźć do tej pory, że połączenie ciągów w drzewach wyrażeń działa tylko wtedy, gdy nie próbują ręcznie budować drzewa ekspresji, które dodaje stałe i zmienne typu System. Strunowy.

Dziękuję wszystkim z góry za odpowiedzi.

Odpowiedz

6

Sprawdź dokumentację: operator "+" w rzeczywistości nie jest zdefiniowany w klasie String. Domyślam się, że kompilator po prostu wie, że oznacza "połączenie ciągów", i przekształca go w wywołanie Concat. Dlatego podczas wywoływania Expression.Add należy określić metodę implementującą operację (w tym przypadku metodę String.Concat).

I dekompilowana ekspresję z reflektorem, to daje następujący wynik (przekształcony):

ParameterExpression expression2; 
Expression<Func<string, string>> expression = 
    Expression.Lambda<Func<string, string>>(
     Expression.Add(
      Expression.Add(
       Expression.Constant("This ", typeof(string)), 
       Expression.Condition(
        Expression.Equal(
         expression2 = Expression.Parameter(typeof(string), "Insert"), 
         Expression.Constant("", typeof(string)), 
         false, 
         (MethodInfo) methodof(string.op_Equality)), 
        Expression.Constant("", typeof(string)), 
        Expression.Add(
         expression2, 
         Expression.Constant(" ", typeof(string)), 
         (MethodInfo) methodof(string.Concat))), 
       (MethodInfo) methodof(string.Concat)), 
      Expression.Constant("That", typeof(string)), 
      (MethodInfo) methodof(string.Concat)), 
     new ParameterExpression[] { expression2 }); 

(Zauważ, że methodof nie jest rzeczywisty operator, jego po prostu co reflektor pokazuje dla instrukcji ldtoken IL W języku C#. musisz odzyskać metodę używając odbicia.)

+3

+1, ale dla jasności, zwięzły przykład: 'Expression.Add (Expression.Constant (" a "), Expression.Constant (" b "), typeof (ciąg znaków) .GetMethod ("Concat", new [] {typeof (string), typeof (string)})); ' –

+0

Znasz kredę do zwykłego deb ślepota ug. Przebiegłem to jeszcze raz. Nigdy nawet nie pomyślałem, aby spojrzeć na Method Property 8-O Ale jest System.String.Concat .... Dzięki za szybką reakcję oczyszczanie mnie :-) – TofuBug