2010-12-02 3 views
5

estoy experimentando con análisis de árboles de expresión y he escrito el siguiente código:C#: el parámetro String se restableció misteriosamente a vacío, ¡por favor ayuda!

private void TestExpressionTree() 
    { 
    Expression<Func<int, bool>> expression = x => x == 1 || x == 2; 
    string output = String.Empty; 
    HandleExpression(expression.Body, output); 
    Output("Output", output); 
    } 

    private void HandleExpression(Expression expression, string output) 
    { 
    switch (expression.NodeType) 
    { 
     case ExpressionType.Conditional: 
     HandleConditionalExpression(expression, output); 
     break; 
     case ExpressionType.OrElse: 
     HandleOrElseExpression(expression, output); 
     break; 
     case ExpressionType.Equal: 
     HandleEqualExpression(expression, output); 
     break; 
     case ExpressionType.Parameter: 
     HandleParameterExpression(expression, output); 
     break; 
     case ExpressionType.Constant: 
     HandleConstantExpression(expression, output); 
     break; 
    } 
    } 

    private void HandleConditionalExpression(Expression expression, string output) 
    { 
    ConditionalExpression conditionalExpression = (ConditionalExpression) expression; 
    HandleExpression(conditionalExpression.Test, output); 
    } 

    private void HandleOrElseExpression(Expression expression, string output) 
    { 
    BinaryExpression binaryExpression = (BinaryExpression)expression; 
    HandleExpression(binaryExpression.Left, output); 
    output += "||"; 
    HandleExpression(binaryExpression.Right, output); 
    } 

    private void HandleEqualExpression(Expression expression, string output) 
    { 
    BinaryExpression binaryExpression = (BinaryExpression)expression; 
    HandleExpression(binaryExpression.Left, output); 
    output += "="; 
    HandleExpression(binaryExpression.Right, output); 
    } 

    private void HandleParameterExpression(Expression expression, string output) 
    { 
    ParameterExpression parameterExpression = (ParameterExpression)expression; 
    output += parameterExpression.Name; 
    } 

    private void HandleConstantExpression(Expression expression, string output) 
    { 
    ConstantExpression constantExpression = (ConstantExpression)expression; 
    output += constantExpression.Value.ToString(); 
    } 

La idea del código es para analizar el árbol de expresión y escribir información sobre los nodos en la cadena de salida variable. Sin embargo, cuando esta variable se escribe en la página (utilizando el método Output()), me parece que está vacía.

Cuando utilizo el depurador para recorrer el código, me parece que la salida está ajustado para 'x' cuando se ejecuta el código HandleParameterExpression() por primera vez, pero tan pronto como el control vuelve a HandleParameterExpression() volver al bloque de interruptores en HandleExpression(), la variable está misteriosamente vacía de nuevo.

Dado que las cadenas son tipos de referencia, simplemente debería ser capaz de pasar la referencia entre los métodos y los cambios en su valor realizado por los métodos deben conservarse, ¿verdad? ¿Hay alguna sutileza de paso de parámetros en C# de la que no estoy al tanto?

+0

tigres Fácil! Cuatro respuestas en cinco minutos! Muchas gracias a todos! – David

+0

¿Por qué no devuelve la salida modificada? ¿Me estoy perdiendo de algo? –

+0

@Omar: porque no es necesario. Es posible usar métodos para modificar parámetros y simplemente devolver algo. – David

Respuesta

6

Debe pasar CUALQUIER variable que desee cambiar realmente por referencia.Así, en su ejemplo, que tendría que hacerlo de esta manera:

private void HandleOrElseExpression(Expression expression, ref string output) 

Y luego, cuando se llama a la función, que haría de esta manera:

HandleOrElseExpression(expression, ref output) 
+0

Así que si paso en una instancia de clase a un método que está destinado a cambiar su estado, ¡necesito pasar explícitamente por referencia! Juro que nunca pasé nada por referencia y nunca he tenido este problema antes ... – David

+1

Si está pasando un objeto y está estableciendo propiedades en él o llamando a métodos, entonces no, no necesita pasarlo por referencia. Pero si quiere establecer ese objeto como otra instancia de él (como decir myObject = new Object(), entonces necesita pasarlo por referencia (o como un parámetro de salida, como se mencionó en Botz3000) para poder usar el nuevo valor fuera de la función –

+0

@David: No, si está cambiando el estado de un * objeto *, no necesita pasar por referencia. Es si está tratando de cambiar * a qué objeto se refiere una variable * que desea considerar el uso de 'ref'. –

8

Nunca cambiará los datos dentro de la cadena, porque es inmutable.

Cada vez que usted tiene:

output += something; 

que está diciendo:

output = output + something; 

El valor de "salida + algo" es en realidad el resultado de llamar String.Concat(output, something) - es decir, una referencia a una nueva cadena. Por lo tanto, su código está cambiando el valor de la variable output para hacer referencia a la nueva cadena. Los datos en la cadena existente permanecen sin cambios.

Cambiar el valor de un parámetro no cambio el valor correspondiente en la persona que llama, a menos que el parámetro se pasa por referencia (usando ref o out). Vea mi article on parameter passing para más detalles. Tenga en cuenta la diferencia entre pasar una referencia por valor y pasar una variable por referencia.

Le sugiero que cambie su código para usar StringBuilder en su lugar.

+0

Siempre pensé que los tipos de referencia (por ejemplo, cadenas) se pasaron por referencia POR DEFECTO. Pensé que eso era lo que significaba el nombre ... – David

+0

@David: No, significa que el valor de una expresión de ese tipo es una referencia a un objeto que contiene los datos, en lugar de almacenar los datos en sí. Lee mi artículo para más detalles. –

1

Parece que desea utilizar out params:

Ejemplo: Cambio private void HandleExpression(Expression expression, string output) a private void HandleExpression(Expression expression, out string output) y reemplazar HandleExpression(expression.Body, output); con HandleExpression(expression.Body, out output); Entonces escrito a output en el método afectará el argumento pasado a la función.

3

Las cadenas son inmutables, por lo que asignando otro valor a output, no está cambiando output, sino que crea una nueva variable de cadena.

Es posible que desee declarar el parámetro output como ref.

Cuestiones relacionadas