Chcę tylko wiedzieć, czy dzwonienie return
wewnątrz using
bloku jest bezpieczne / dobre .
Na przykład.
using(var scope = new TransactionScope())
{
// my core logic
return true; // if condition met else
return false;
scope.Complete();
}
Wiemy, że w końcu najbardziej zakręcony aparat dispose()
zostanie odwołany. Ale co będzie w powyższym przypadku, skoro return
wyskakuje sterowanie poza podany zakres (AFAIK) ...
- Czy moje
scope.Complete()
wezwanie? - I tak w przypadku metody lunety
dispose()
.
using{}
zakres się skończy, odpowiednie obiekty zostaną usunięte,return
„zepsują” zasięg - więc obiekty zostaną usunięte zgodnie z oczekiwaniamiscope.Complete()
połączenie nigdy nie zostanie odebrane przy użyciu dostarczonej próbki, więc transakcja zawsze zostanie wycofana.using
sdispose()
, po powrocie funkcja zawierająca tenusing
blok zostanie zwrócona i wszystko, co do niej należy, zostanie osierocone. Więc nawet jeśliscope
nie został usunięty „przezusing
” (będzie, jak wyjaśniali inni), i tak zostanie usunięty, ponieważ funkcja zakończyła się. Gdyby C # miałgoto
stwierdzenie - czy skończyłeś się już śmiać? dobrze - wtedy zamiast powrotu możnagoto
by po nawiasie zamykającym, bez powrotu. Logicznie rzecz biorąc,scope
nadal byłby usunięty, ale właśnie umieściłeśgoto
C #, więc kogo obchodzi logika na tym etapie.Odpowiedzi:
Wykonywanie połączeń
return
wewnątrzusing
bloku jest całkowicie bezpieczne , ponieważ blok using to tylkotry/finally
blok.W powyższym przykładzie po powrocie
true
zakres zostanie usunięty, a wartość zwrócona.return false
Iscope.Complete()
będzie nie sprawdzony.Dispose
jednak zostanie wywołany niezależnie, ponieważ znajduje się wewnątrz ostatniego bloku.Twój kod jest zasadniczo taki sam jak ten (jeśli to ułatwia zrozumienie):
var scope = new TransactionScope()) try { // my core logic return true; // if condition met else return false; scope.Complete(); } finally { if( scope != null) ((IDisposable)scope).Dispose(); }
Należy pamiętać, że transakcja nigdy nie zostanie zatwierdzona, ponieważ nie ma sposobu, aby
scope.Complete()
ją zatwierdzić.źródło
Dispose
zostaniesz wezwany. Jeśli OP nie wie, co się dziejeusing
, prawdopodobnie nie wie, co się dziejefinally
.using
, na przykładusing (var callersVar = MyFunc(..)) ..
, zamiast posiadania using wewnątrz „myFunc” - oznacza, że rozmówca otrzymuje strumień i jest odpowiedzialny za zamykania poprzezusing
lub bezpośrednio, lub (b) niech MyFunc wyodrębni wszystkie potrzebne informacje do innych obiektów, które można bezpiecznie przekazać z powrotem - wtedy bazowe obiekty danych lub strumień mogą zostać usunięte przezusing
. Nie powinieneś pisać nieszczelnego kodu.W porządku -
finally
klauzule (co robi zamykający nawias klamrowyusing
klauzuli pod maską) zawsze są wykonywane po opuszczeniu zakresu, bez względu na sposób.Jest to jednak prawdziwe tylko dla instrukcji, które znajdują się w ostatnim bloku (którego nie można jawnie ustawić podczas używania
using
). Dlatego w twoim przykładziescope.Complete()
nigdy nie zostałoby wywołane (oczekuję jednak, że kompilator ostrzeże Cię o nieosiągalnym kodzie).źródło
Ogólnie jest to dobre podejście. Ale w twoim przypadku, jeśli wrócisz przed wywołaniem
scope.Complete()
, po prostu usunie to TransactionScope. Zależy od twojego projektu.Tak więc w tym przykładzie Complete () nie jest wywoływana, a zakres jest usuwany, przy założeniu, że dziedziczy on interfejs IDisposable.
źródło
scope.Complete należy zdecydowanie wywołać wcześniej
return
. Kompilator wyświetli ostrzeżenie i ten kod nigdy nie zostanie wywołany.Jeśli chodzi o
return
siebie - tak, można to bezpiecznie nazwaćusing
oświadczeniem wewnętrznym . Użycie jest tłumaczone na blok try-final za sceną i ostatecznie blok ma być z pewnością wykonany.źródło
W podanym przykładzie występuje problem;
scope.Complete()
nigdy nie jest wezwany. Po drugie, nie jest dobrą praktyką stosowaniereturn
instrukcji wewnątrzusing
instrukcji. Zapoznaj się z następującymi informacjami:using(var scope = new TransactionScope()) { //have some logic here return scope; }
W tym prostym przykładzie chodzi o to; wartość
scope
will będzie zerowa po zakończeniu używania instrukcji.Dlatego lepiej nie wracać do środka za pomocą instrukcji.
źródło
scope
will nie będzie zerowa - jedyną rzeczą, która się wydarzy, jest to, żeDispose()
zostanie wywołana w tej instancji, dlatego instancja nie powinna być już używana (ale nie jest pusta i nic nie stoi na przeszkodzie, aby spróbować użyć usuwany przedmiot, nawet jeśli rzeczywiście jest to niewłaściwe użycie przedmiotu jednorazowego użytku).return scope
odwołanie do tego obiektu. W ten sposób, jeśli przypiszesz to odwołanie po powrocie, zapobiegniesz czyszczeniu usuniętego obiektu przez GC.Aby upewnić się, że
scope.Complete()
zostanie wywołana, owiń jątry/finally
.dispose
Nazywa bo trzeba zawinąć go zusing
który jest alternatywątry/finally
blok.using(var scope = new TransactionScope()) { try { // my core logic return true; // if condition met else return false; } finally { scope.Complete(); } }
źródło
W tym przykładzie scope.Complete () nigdy nie zostanie wykonana. Jednak polecenie return wyczyści wszystko, co jest przypisane na stosie. GC zajmie się wszystkim, do czego nie ma odniesienia. Więc jeśli nie ma przedmiotu, którego GC nie może odebrać, nie ma problemu.
źródło