2010-10-04 12 views
9

He pasado por un tiempo y me he golpeado la cabeza contra la pared por un tiempo ahora he buscado varias frases y palabras clave pero no encuentro nada parecido a una respuesta así que espero que alguien aquí pueda arrojar algo de luz.¿Por qué no puedo crear el mismo Árbol de Expresión manualmente que mi lambda recta produce

Básicamente estoy trabajando en el buceo bastante profundamente en manipular, crear y modificar los árboles de expresión en C# 4.0

me encontré con una anomalía extraña que no puedo dar sentido a

si escribo algo como esto

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

Cuando llego a depurar y miro el árbol de expresión que es similar a esto

  • F (NodeType = Lambda)
    • cuerpo (NodeType = Añadir)
      • Izquierda (NodeType = Añadir)
        • Izquierda (NodeType = constante, Valor = "Este")
        • derecho (NodeType = condicional)
          • ifFalse (NodeType = Añadir)
            • izquierda (NodeType = Parámetro, Name = "Insertar")
            • derecho (NodeType = constante, Valor = " ")
          • ifTrue (NodeType = constante, Valor = "")
          • prueba (NodeType = Igual)
            • Izquierda (NodeType = parámetros, cuyo nombre = "Insertar")
            • derecho (NodeType = constante, Valor = "")
      • derecho (NodeType = constante, Valor = "Eso")
    • Paramerters (Count = 1)
      • Parámetros [0] (NodeType = Parámetro, Name = "Insertar")

puedo llamar

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

y salga como espero

"Esto es algo intermedio que"

Ahora bien, si lo intento y reconstruir esa forma manual utilizando los métodos estáticos de la clase base Expresión me encuentro con un tema interesante.(He desglosado cada paso en su propia expresión para propósitos de depuración)

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>> 

que en base a los valores del árbol de expresión generada por encima de recrear el mismo árbol expresión básica que el anterior (al menos con el mismo "look")

Todo pasos a través de bien hasta que llegan a esta línea

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

El compilador lanza un InvalidOperationException era no controlada

El operador binario Agregar no está definido para 'System.String' y 'System.String'

Ahora ¿por qué es esto?

¿Por qué cuando dejo que C# convierta un Lambda en Expression obviamente usa el Add NodeType, y la pantalla de Tipos muestra que definitivamente está usando System.String pero cuando intento hacer lo mismo manualmente no deja que el código ¿continuar?

Como nota final incluso he intentado lo siguiente:

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

mismo error.

Tengo curiosidad por lo que parece al menos con lo que he podido encontrar hasta ahora que la concatenación de cadenas en los árboles de expresiones solo funciona si no se intenta construir manualmente un árbol de expresiones que agregue constantes y variables de tipo Sistema. Cuerda.

Gracias a todos por adelantado por las respuestas.

Respuesta

6

Compruebe la documentación: el operador '+' en realidad no está definido en la clase String. Supongo que el compilador solo sabe que significa "concatenar las cadenas", y lo transforma en una llamada al Concat. Por lo tanto, cuando llame al Expression.Add, debe especificar el método que implementa la operación (en ese caso, el método String.Concat).

I decompilados la expresión con reflector, se da el siguiente resultado (reordenada):

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

(Tenga en cuenta que methodof no es un operador real, es sólo lo Reflector muestra para la instrucción ldtoken IL En C#. tiene que recuperar el método utilizando la reflexión.)

+3

+1, pero para mayor claridad, un ejemplo conciso: 'Expression.Add (Expression.Constant (" a "), Expression.Constant (" b "), typeof (string)) .GetMethod ("Concat", nuevo [] {typeof (cadena), typeof (cadena)})); ' –

+0

Ya sabes, apúntate esto a simple deb ug ceguera. Lo volví a ejecutar. Nunca pensé en mirar la Propiedad del Método 8-O Sin embargo, ahí está System.String.Concat .... Gracias por la respuesta rápida que me aclara :-) – TofuBug

Cuestiones relacionadas