Aggiungendo alle risposte fornite da Marc Gravell e Jon Skeet, è importante notare che gli oggetti e altri tipi di riferimento si comportano in modo simile quando vengono restituiti, ma presentano alcune differenze.
Il "Cosa" che viene restituito segue la stessa logica dei tipi semplici:
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");
}
}
}
Il riferimento che viene restituito è già stato valutato prima che alla variabile locale venga assegnato un nuovo riferimento nel blocco finally.
L'esecuzione è essenzialmente:
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 differenza è che sarebbe ancora possibile modificare i tipi mutabili usando le proprietà / i metodi dell'oggetto che possono provocare comportamenti imprevisti se non si presta attenzione.
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 seconda cosa da considerare su try-return-finally è che i parametri passati "per riferimento" possono ancora essere modificati dopo il ritorno. È stato valutato solo il valore restituito ed è memorizzato in una variabile temporanea in attesa di essere restituito, eventuali altre variabili vengono comunque modificate nel modo normale. Il contratto di un parametro out può anche non essere completato fino a quando non si blocca in questo modo.
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;
}
}
}
Come qualsiasi altro costrutto di flusso "try-return-finally" ha il suo posto e può consentire un codice più pulito rispetto alla scrittura della struttura in cui si compila effettivamente . Ma deve essere usato con attenzione per evitare i gotcha.