2009-10-28 6 views
13

Questa non è una vittima di Calling a method with ref or out parameters from an anonymous methodPerché un parametro out non è consentito all'interno di un metodo anonimo?

Mi chiedo il motivo per cui fuori parametri non sono ammessi all'interno dei metodi anonimi. Il fatto di non consentire i parametri ref mi sembra un po 'più sensato, ma i parametri out non sono tanto.

quali sono i tuoi pensieri su questo

+0

Mi chiedo PERCHE 'tu abbia mai passato un tipo anonimo al di fuori del corpo del metodo, a meno che tu non voglia imparare la gioia che è Reflection. – Will

+0

Questa risposta alla domanda di collegamento originale non risponde sufficientemente? http://stackoverflow.com/questions/801147/scope-of-anonymous-methods/801563#801563 –

+1

@Will: questo non ha nulla a che fare con i tipi anonimi. – SLaks

risposta

28

In un certo senso questa è una vittima. I parametri Out sono i parametri ref. C'è semplicemente un attributo extra sul valore usato dal linguaggio C#. Il motivo per cui non sono consentiti è esattamente lo stesso dei parametri ref.

Il problema qui ha origine dall'uso di un valore dichiarato al di fuori del metodo anonimo all'interno del metodo anonimo. Fare ciò catturerà il valore all'interno della lambda e per necessità estenderà arbitrariamente la sua durata oltre quella della funzione corrente. Questo non è compatibile con i parametri out che hanno una durata prefissata.

Immaginate ad esempio che il parametro out si riferisca a una variabile locale nello stack. Il lambda può essere eseguito in qualsiasi punto arbitrario in futuro e quindi potrebbe essere eseguito quando quel frame dello stack non era più valido. Cosa significa il parametro out?

+1

+1 Aveva iniziato a digitare un esempio simile al tuo ultimo paragrafo. Grazie per avermi salvato il fastidio :) –

+0

Secondo paragrafo accendere la lampadina. Grazie @JaredPar! –

+0

Ma non si applica a nessun altro tipo di variabile? Voglio dire, cosa succede se uso una variabile all'interno di un'espressione lambda che si trova nello stack, basta accedere ad alcuni campi. Se lambda expressión viene eseguito quando lo stack non è più valido, l'oggetto non sarebbe già stato raccolto? Se non lo è, quando il garbage collector raccoglierà quell'oggetto? –

6

Questo è fondamentalmente a che fare con il fatto che i parametri di un anonimo delegato/espressioni lambda sono variabili catturati, e catturando ref/out variabili non ha alcun senso in C#/CLR, poiché richiederebbe ref/outcampi internamente. Inoltre, tieni presente che accoppi entrambe queste parole chiave perché sono effettivamente le stesse.

Se si desidera una spiegazione completa, Eric Lippert discussed this design point in detail sul proprio blog. (Vedere paragrafi vicino al fondo in particolare.)

1

L'unica differenza tra out e ref parametri è che un parametro out avrà un gettone [out] applicato ad esso. Sono la stessa cosa per quanto riguarda il CLR.

Per implementarlo, il compilatore dovrebbe generare refcampi, che non sono supportati.

Se ci pensi, ti renderai conto che non ha senso consentire ad un metodo anonimo di utilizzare un parametro out.

A cosa sarebbe il seguente codice?

static Func<object, object> Mess(out object param) { 
    param = "Original"; 
    return i => param = i; 
} 
static Func<object, object> MessCaller() { 
    object local; 
    return Mess(out local); 
} 
static vouid Main() { 
    Console.WriteLine(MessCaller()("New")); 
    //The local variable that the lambda expression writes to doesn't exist anymore. 
} 
+1

L'attributo 'OutAttribute' non è la cosa applicata ai parametri' out' in C#. 'OutAttribute' è un attributo personalizzato considerato durante il marshalling. I parametri 'out' sono annotati da un altro token di metadati (' [out] in IL'). È possibile verificare questo fatto interrogando gli attributi di un 'ParameterInfo' dichiarato come' out' nel codice C#. Non c'è '[OutAttribute]' lì. –

+0

riparato; grazie. – SLaks

1

Mi sono imbattuto in questo enigma mentre sviluppavo codice di gestione degli errori. Volevo passare un riferimento (out) a un messaggio di errore che verrebbe registrato. Ciò ha dato ai miei metodi anonimi la possibilità di eseguire controlli multipli, ognuno dei quali impostava il messaggio di errore secondo necessità.

Ho finito per scrivere un nuovo wrapper per il metodo anonimo che ha funzionato in modo diverso.Ma quello che pensavo potesse essere di qualche valore per qualcuno, è che avrei potuto semplicemente creare un metodo privato che aveva un parametro out, e definito un delegato, e ha fatto in modo che il mio codice lo usasse. Spero che questo aiuti/ispira qualcuno.

protected delegate void OutStringDelegate(int divider, out string errorText); 
    protected void codeWrapper(int divider, OutStringDelegate del) 
    { 
     string ErrorMessage = "An Error Occurred."; 

     try 
     { 
      del(divider, out ErrorMessage); 
     } 
     catch 
     { 
      LogError(ErrorMessage); 
     } 
    } 
    public void UseWrapper(int input) 
    { 
     codeWrapper(input, codeToCall); 
    } 
    private int somePrivateValue = 0; 
    private void codeToCall(int divider, out string errorMessage) 
    { 
     errorMessage = "Nice Error Message here!"; 
     somePrivateValue = 1/divider; // call me with zero to cause error. 
    } 
    private void LogError(string msg) 
    { 
     Console.WriteLine(msg); 
    }