2010-01-07 8 views
14

cuando hago una asignación a un parámetro out o ref, es el valor inmediatamente asignado a la referencia proporcionada por la persona que llama, o son los out y ref parámetros valores asignados a las referencias cuando el método devuelve? Si el método arroja una excepción, ¿se devuelven los valores?¿Cuándo se devuelve realmente el valor de un parámetro C# 'out' o 'ref' al llamante?

Por ejemplo:

int callerOutValue = 1; 
int callerRefValue = 1; 
MyMethod(123456, out callerOutValue, ref callerRefValue); 

bool MyMethod(int inValue, out int outValue, ref int refValue) 
{ 
    outValue = 2; 
    refValue = 2; 

    throw new ArgumentException(); 

    // Is callerOutValue 1 or 2? 
    // Is callerRefValue 1 or 2? 
} 

Respuesta

26

Desde ref y out parámetros permiten un método para trabajar con las referencias reales que la persona que llama aprobada en, todos los cambios en esas referencias se reflejan inmediatamente a la persona que llama cuando se devuelve el control.

Esto significa que en su ejemplo anterior (si se va a coger el ArgumentException por supuesto), y outValuerefValue habría tanto ajustarse a 2.

También es importante señalar que out y ref son conceptos idénticos en un nivel de IL: solo el compilador de C# aplica la regla adicional para out que requiere que un método establezca su valor antes de regresar. Por lo tanto, desde una perspectiva CLR outValue y refValue tienen una semántica idéntica y se tratan de la misma manera.

+1

Hmm ... eso no es lo que esperaba, ¡pero la prueba lo confirma! +1. –

+0

Esta es información útil. : O – Sapph

+0

Recuerdo micro-optimizar un método que recibió un parámetro 'out'. El método fue llamado en un bucle muy cerrado y quería eliminar la inicialización requerida cada vez que se llamaba, ya que el parámetro no cambiaba, así que hice que tomara un parámetro 'ref' y funcionó de manera mensurable peor. ¿Alguien tiene una explicación para esto, o fue un golpe de suerte? – JulianR

14

Andrew es correcto; Me limitaré a agregar un par de detalles adicionales.

En primer lugar, la forma correcta de pensar en los parámetros out/ref es que son alias para las variables. Es decir, cuando tiene un método M (ref int q) y lo llama M (ref x), q y x son dos nombres diferentes para exactamente la misma variable. Una variable es una ubicación de almacenamiento; usted almacena algo en q, también lo está almacenando en x, porque son dos nombres diferentes para la misma ubicación.

En segundo lugar, la alternativa que está describiendo se denomina referencia de "copiado/copiado". En este esquema, hay dos ubicaciones de almacenamiento y el contenido de una se copia al inicio de la llamada de función, y se copia de nuevo cuando termina. Como observa, la semántica de copiar-en-copiar-salir es diferente de la semántica de las referencias de alias cuando se lanzan excepciones.

También son diferentes en situaciones extrañas como esta:

void M(ref int q, ref int r) 
{ 
    q = 10; 
    r = 20; 
    print (q); 
} 

... 

M(ref x, ref x); 

En aliasing, X, Q y R son todos iguales ubicación de almacenamiento, por lo que este imprime 20. En copy-in-copia-afuera referencia , esto imprimiría 10, y el valor final de x dependería de si la copia fue de izquierda a derecha o de derecha a izquierda.

Finalmente, si mal no recuerdo, existen situaciones raras y extrañas en la implementación de árboles de expresiones donde implementamos la semántica de copia en la copia en parámetros de ref. Debería revisar ese código y ver si puedo recordar exactamente cuáles son esos escenarios.

Cuestiones relacionadas