Ci sono effetti collaterali del ritorno dall'interno di un'istruzione using ()?


125

Restituire un valore di metodo dall'interno di un'istruzione using che ottiene un DataContext sembra funzionare sempre bene , in questo modo:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        var transaction = (from t in db.Transactions
                              orderby t.WhenCreated descending
                              where t.Id == singleId
                              select t).SingleOrDefault();
        return transaction;
    }
}

Ma ho sempre la sensazione di dover chiudere qualcosa prima di uscire dalle parentesi quadre usando, ad esempio, definendo la transazione prima dell'istruzione using, ottenendo il suo valore tra parentesi quadre e poi ritornando dopo le parentesi quadre.

Definire e restituire la variabile al di fuori delle parentesi usando sarebbe una pratica migliore o conserverebbe le risorse in qualche modo?


1
Potrebbe essere interessante guardare l'IL generale per le varianti di questo. Ho il sospetto che ci sarebbe poca differenza nell'IL generato. Normalmente non mi preoccuperei nemmeno di dichiarare la transazione var - restituisco solo il risultato dell'espressione.
Jonesie

Risposte:


164

No, penso che sia più chiaro in questo modo. Non preoccuparti, Disposeverrà comunque chiamato "in uscita" - e solo dopo che il valore di ritorno sarà stato completamente valutato. Se viene generata un'eccezione in qualsiasi momento (inclusa la valutazione del valore restituito) Disposeverrà comunque chiamata.

Sebbene tu possa certamente prendere il percorso più lungo, sono due linee extra che aggiungono solo la cruft e il contesto extra da tenere traccia (mentalmente). In realtà, non hai davvero bisogno della variabile locale aggiuntiva, anche se può essere utile in termini di debug. Si potrebbe semplicemente avere:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        return (from t in db.Transactions
                orderby t.WhenCreated descending
                where t.Id == singleId
                select t).SingleOrDefault();
    }
}

In effetti, potrei anche essere tentato di usare la notazione a punti e inserire la Wherecondizione tra SingleOrDefault:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        return db.Transactions.OrderByDescending(t => t.WhenCreated)
                              .SingleOrDefault(t => t.Id == singleId);
    }
}

2
Sine sei tu @jon, è ancora sicuro se viene generata un'eccezione all'interno del blocco using?
Dave Archer

6
sì. usare è semplicemente zucchero sintattico per un costrutto try / finally
Mitch Wheat

@ David: Come dice Mitch, va bene - ho aggiornato la risposta per renderlo più chiaro :)
Jon Skeet

2
Perché usare OrderByDescending in combinazione con SingleOrDefault?
erikkallen,

2
@erikkallen: LINQ non ha un "MaxBy", sfortunatamente - quindi non puoi ottenere la riga con il valore massimo. Per LINQ to Objects puoi scriverne uno abbastanza facilmente, ma in questo caso non sono sicuro di un modo migliore per farlo. Cosa suggeriresti invece?
Jon Skeet,

32

dai un'occhiata a questo

Comprensione dell'istruzione "using" in C #

Il CLR converte il codice in MSIL. E l'istruzione using viene tradotta in un tentativo e infine blocco. Ecco come l'istruzione using è rappresentata in IL. Una dichiarazione di utilizzo è tradotta in tre parti: acquisizione, utilizzo e smaltimento. La risorsa viene prima acquisita, quindi l'utilizzo è racchiuso in un'istruzione try con una clausola finally. L'oggetto viene quindi disposto nella clausola finally.


4
Una visione interessante. Grazie.
Kangkan,

1
Ciò traduce la domanda in: Eventuali effetti collaterali del ritorno dal blocco di prova di un tentativo-finalmente?
Henk Holterman,


6

Non ci sono nessun effetti collaterali di ritorno da all'interno di una using()dichiarazione.

Se rende il codice più leggibile è un'altra discussione.


0

Penso che sia tutto uguale. Non c'è niente di male nel codice. Il framework .NET non importa dove viene creato l'oggetto. L'importante è che sia referenziato o meno.


-1

Sì, può esserci un effetto collaterale. Ad esempio, se si utilizza la stessa tecnica nel metodo di azione ASP.NET MVC, verrà visualizzato il seguente errore: "L'istanza di ObjectContext è stata eliminata e non può più essere utilizzata per operazioni che richiedono una connessione"

public ActionResult GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        var transaction = (from t in db.Transactions
                              orderby t.WhenCreated descending
                              where t.Id == singleId
                              select t).SingleOrDefault();
        return PartialView("_transactionPartial", transaction);
    }
}

2
se si definisce una transazione al di fuori dell'utilizzo dell'istruzione, si otterrà lo stesso errore. l'utilizzo della parola chiave non è correlato in questo caso.
Costa
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.