Czy są jakieś skutki uboczne powrotu z wnętrza instrukcji using ()?

125

Zwracanie wartości metody z wnętrza instrukcji using, która pobiera DataContext, wydaje się zawsze działać dobrze , na przykład:

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;
    }
}

Ale zawsze czuję, że powinienem coś zamknąć, zanim wyrwę się z używania nawiasów, np. Definiując transakcję przed instrukcją using, wpisując jej wartość w nawiasach, a potem wracając po nawiasach.

Czy zdefiniowanie i zwrócenie zmiennej poza nawiasami używanymi byłoby lepszą praktyką lub w jakikolwiek sposób oszczędziłoby zasoby?

Edward Tanguay
źródło
1
Może być interesujące przyjrzenie się ogólnemu IL dla wariantów tego. Podejrzewam, że różnica w generowanym IL byłaby niewielka. Normalnie nawet nie zawracałbym sobie głowy deklarowaniem transakcji var - po prostu zwróć wynik wyrażenia.
Jonesie

Odpowiedzi:

164

Nie, myślę, że w ten sposób jest to jaśniejsze. Nie martw się, Disposenadal będzie nazywany „w drodze” - i dopiero po całkowitym oszacowaniu wartości zwracanej. Jeśli wyjątek zostanie zgłoszony w dowolnym momencie (w tym ocena wartości zwracanej) Dispose, nadal będzie wywoływany.

Chociaż z pewnością mógłbyś wybrać dłuższą trasę, są to dwie dodatkowe linie, które po prostu dodają cruft i dodatkowy kontekst do śledzenia (mentalnie). W rzeczywistości nie potrzebujesz dodatkowej zmiennej lokalnej - chociaż może być przydatna przy debugowaniu. Państwo mogłoby po prostu:

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();
    }
}

Rzeczywiście, mógłbym nawet ulec pokusie, aby użyć notacji z kropką i umieścić Wherewarunek w SingleOrDefault:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        return db.Transactions.OrderByDescending(t => t.WhenCreated)
                              .SingleOrDefault(t => t.Id == singleId);
    }
}
Jon Skeet
źródło
2
Czy to ty @jon, czy nadal jest bezpieczne, jeśli wyjątek zostanie wyrzucony wewnątrz bloku using?
Dave Archer
6
tak. używanie jest po prostu cukrem syntaktycznym dla próby / wreszcie konstruktu
Mitch Wheat
@David: Jak mówi Mitch, wszystko w porządku - zaktualizowałem odpowiedź, aby była jaśniejsza :)
Jon Skeet
2
Dlaczego warto korzystać z OrderByDescending w połączeniu z SingleOrDefault?
erikkallen
2
@erikkallen: LINQ nie ma niestety „MaxBy” - więc nie możesz pobrać wiersza z maksymalną wartością. W przypadku LINQ to Objects możesz dość łatwo napisać własne, ale nie jestem pewien, jak to zrobić w tym przypadku lepiej. Co byś zaproponował zamiast tego?
Jon Skeet
32

Zerknij na to

Zrozumienie instrukcji „using” w języku C #

Środowisko CLR konwertuje kod na MSIL. Instrukcja using zostaje przetłumaczona na try i wreszcie blok. W ten sposób instrukcja using jest reprezentowana w języku IL. Instrukcja używania jest tłumaczona na trzy części: nabycie, użycie i utylizacja. Zasób jest najpierw pozyskiwany, a następnie użycie jest zawarte w instrukcji try z klauzulą ​​final. Obiekt zostaje następnie usunięty w klauzuli last.

Adriaan Stander
źródło
4
Ciekawy wgląd. Dzięki.
Kangkan
1
To przekłada się na pytanie: Jakiekolwiek skutki uboczne powrotu z bloku próbnego w końcu próby?
Henk Holterman
3
Nie, w końcu zawsze zostanie wezwany. techinterviews.com/interview-questions-for-c-developers
Adriaan Stander
6

Nie ma żadnych skutków ubocznych powrotu z wnętrza using()instrukcji.

To, czy tworzy najbardziej czytelny kod, to inna dyskusja.

Mitch Wheat
źródło
0

Myślę, że to wszystko jedno. W kodzie nie ma nic złego. Platforma .NET nie obchodziłaby, gdzie tworzony jest obiekt. Liczy się to, czy istnieje odniesienie, czy nie.

Kerido
źródło
-1

Tak, może wystąpić efekt uboczny. Na przykład, jeśli użyjesz tej samej techniki w metodzie ASP.NET MVC Action, pojawi się następujący błąd: „Wystąpienie ObjectContext zostało usunięte i nie można go już używać do operacji wymagających połączenia”

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);
    }
}
przydatne
źródło
2
jeśli zdefiniujesz transakcję poza instrukcją using, otrzymasz ten sam błąd. using słowo kluczowe nie jest w tym przypadku powiązane.
Costa