Además de las respuestas dadas por Marc Gravell y Jon Skeet, es importante tener en cuenta que los objetos y otros tipos de referencia se comportan de manera similar cuando se devuelven, pero tienen algunas diferencias.
El "Qué" que se devuelve sigue la misma lógica que los tipos simples:
class Test {
public static Exception AnException() {
Exception ex = new Exception("Me");
try {
return ex;
} finally {
// Reference unchanged, Local variable changed
ex = new Exception("Not Me");
}
}
}
La referencia que se devuelve ya se ha evaluado antes de que a la variable local se le asigne una nueva referencia en el bloque finalmente.
La ejecución es esencialmente:
class Test {
public static Exception AnException() {
Exception ex = new Exception("Me");
Exception CS$1$0000 = null;
try {
CS$1$0000 = ex;
} finally {
// Reference unchanged, Local variable changed
ex = new Exception("Not Me");
}
return CS$1$0000;
}
}
La diferencia es que aún sería posible modificar los tipos mutables utilizando las propiedades / métodos del objeto, lo que puede generar comportamientos inesperados si no se tiene cuidado.
class Test2 {
public static System.IO.MemoryStream BadStream(byte[] buffer) {
System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer);
try {
return ms;
} finally {
// Reference unchanged, Referenced Object changed
ms.Dispose();
}
}
}
Una segunda cosa a considerar acerca de try-return-finally es que los parámetros pasados "por referencia" aún pueden modificarse después del retorno. Solo se ha evaluado el valor de retorno y se almacena en una variable temporal en espera de ser devuelta, cualquier otra variable aún se modifica de la manera normal. El contrato de un parámetro de salida incluso puede quedar sin cumplir hasta que finalmente se bloquee de esta manera.
class ByRefTests {
public static int One(out int i) {
try {
i = 1;
return i;
} finally {
// Return value unchanged, Store new value referenced variable
i = 1000;
}
}
public static int Two(ref int i) {
try {
i = 2;
return i;
} finally {
// Return value unchanged, Store new value referenced variable
i = 2000;
}
}
public static int Three(out int i) {
try {
return 3;
} finally {
// This is not a compile error!
// Return value unchanged, Store new value referenced variable
i = 3000;
}
}
}
Como cualquier otra construcción de flujo, "try-return-finally" tiene su lugar y puede permitir un código de aspecto más limpio que escribir la estructura con la que realmente se compila. Pero debe usarse con cuidado para evitar los gotcha's.