Poproszono mnie o zaimplementowanie IDisposable na obiektach, które są w 100% zarządzanymi zasobami, które nie zawierają strumieni ani dużych zasobów.
Rozumiem, jak ważne jest prawidłowe pozbywanie się dużych zasobów i niezarządzanych zasobów, ale co z drugą stroną równania?
W przypadku obiektu, który nie korzysta z implementacji IDisposable (małe, w pełni zarządzane obiekty) Jaki negatywny wpływ może mieć GC.SuppressFinalize, jeśli w ogóle?
Komentarze
Odpowiedź
Jedynym celem GC.SuppressFinalize
jest:
uniemożliwienie finalizatorowi zwolnienia niezarządzanych zasobów, które zostały już zwolnione przez implementację IDisposable.Dispose.
Po usunięciu obiektu powinieneś rzeczywiście zadzwonić do GC.SuppressFinalize(this);
, jak pokazano w , odpowiedzią na pytanie ” Kiedy należy używać GC.SuppressFinalize ()? ”. Przydatna jest również reguła analizy kodu „ CA1816 .
Teraz zaimplementowano IDisposable
na obiekty, które nie mają niezarządzanych zasobów, wyglądają dość dziwnie i wątpliwie. Ponieważ jedyny powód, dla którego ta osoba Ci podała, brzmiał: „tak po prostu robiliśmy zawsze, Ty też to robisz”, zamiast podawać dane dotyczące profilowania / analizy porównawczej, które pokazują, że dodanie poprawi coś w konkretnym przypadku, nie ma faktycznych powodów, aby to zrobić.
Czy spowodowałoby to problemy z wydajnością? Trudno powiedzieć: zależałoby to od konkretnego przypadku.
Czy powoduje jakieś inne problemy? Oczywiście. Nie licząc faktu, że implementacja jest bolesna, jeśli ta praktyka jest używana zbyt często, kod staje się nieczytelny i niemożliwy do utrzymania:
public int ComputePrice(Rebate rebate) { using (var currentCurrency = this.User.FindCurrency()) { using (var priceWithoutRebate = this.GetPurePrice()) { using (var canApplyRebate = rebate.CanApplyTo(this)) { if (!canApplyRebate) { return priceWithoutRebate; } using (var priceWithRebate = priceWithoutRebate.Apply(rebate)) { return priceWithRebate; } } } } }
Komentarze
- Cóż, wydaje mi się, że historia jest taka, że dawno temu mieli jakiś związek / problemy z wyciekiem strumienia, prawdopodobnie z powodu nieszczelnego połączenia / komendy w kodzie ADO.NET, co spowodowało ogromne wycieki zasobów. Wiedzieli, że wdrożenie IDisposable na wszystkim rozwiąże problem, więc zrobili i zadziałało. I teraz j Po prostu zaimplementuj to na większości obiektów.
- @AndrewHoffman: wydaje się to wiarygodnym wyjaśnieniem. Ale nadal nie ' nie uzasadnia obecnej praktyki.
Odpowiedź
Nie musisz używać ciężkiego („bolesnego”) wzorca Dispose + Finalizer w klasie IDisposable, if klasa jest zapieczętowana, a klasa nie ma niezarządzanych zasobów.
W takiej sytuacji można użyć podzbioru wzorca Dispose + Finalizer, który ma tylko public Dispose()
, nie ma finalizatora, a klasa jest zapieczętowana.
Powodem zapieczętowania klasy jest uniknięcie problemu „Co jeśli klasa podrzędna ma niezarządzane zasoby?” oraz „A co, jeśli klasa podrzędna w przyszłości będzie miała niezarządzane zasoby?”
Joe Duffy, dawniej pracownik firmy Microsoft, opublikował obszerny (ponad 30 stron) artykuł omawiający, jak poprawnie zaimplementować IDisposable
i wszystkie implementacje podzbiorów permutacji.
Współautorami artykułu są: Herb Sutter, Brian Grunkemeyer, Jeff Richter i inni luminarze C #. Artykuł jest znacznie bardziej szczegółowy niż to, co jest prezentowane w witrynie MSDN.
Niestety, nowy silnik blogów Joe Duffyego nie spisał się dobrze, jeśli chodzi o zachowanie oryginalnego artykułu z 2005 roku, więc wygląda na to, że coś się zepsuło. Oto link: http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/
Chciałbym móc załączyć mój dokument MS-Word, który zawiera całość artykułu Joe Duffyego w znacznie lepszej format. 🙁
IDisposable
bez pisania destruktora / finalizatora. Nie ' nie wiem, czy osoba, która poprosiła cię o zaimplementowanie , miała na myśli, że powinieneś również stworzyć destruktor. Oczywiście, jeśli klasa nie ma destruktora,GC.SuppressFinalize
nie ma sensu. Jeśli klasa ma destruktor, pamiętaj, aby powiedziećGC.SuppressFinalize(this)
po uruchomieniu metodyDispose
. W przeciwnym razie instancja nie zostanie łatwo zebrana jako śmieci, zostanie umieszczona w kolejce finalizatora i zostanie zebrana tylko w kolekcji 2. generacji.