Най-добри практики за използване на Dispose и Finalize в .Net

Microsoft .Net Framework осигурява събирач на боклук, който работи във фонов режим и освобождава паметта, заета от управлявани обекти, когато те вече не са препратени към вашия код. Въпреки че събирачът на боклук е вещ в почистването на паметта, заета от управлявани обекти, не е гарантирано, че паметта, заета от неуправляеми обекти, ще бъде почистена при изпълнение на следващия GC цикъл. Ако имате неуправлявани ресурси във вашето приложение, трябва да се уверите, че освобождавате такива ресурси изрично, когато сте готови да ги използвате. В тази статия ще подчертая най-добрите практики, които трябва да следвате, за да почистите ресурсите, използвани във вашето приложение.

GC използва поколения, за да поддържа и управлява относителния живот на обектите, които са създадени в паметта. Обектите, които се създават нови, се поставят в поколение 0. Основното предположение е, че новосъздаденият обект може да има по-кратък живот, докато обектът, който е стар, може да има по-дълъг живот. Когато обектите, пребиваващи в поколение 0, не се възстановяват след цикъл на GC, те се преместват в поколение 1. По същия начин, ако обектите, пребиваващи в поколение 1, оцелеят при почистване на GC, те се преместват в поколение 2. Обърнете внимание, че GC работи по-често в по-ниските поколения, че при по-висшите. Така че обектите, които се намират в поколение 0, ще бъдат почиствани по-често в сравнение с обектите, които се намират в поколение 1. Така че,по-добра практика за програмиране е да се гарантира, че използвате повече локални обекти, които са обекти в по-високия обхват, за да избегнете преместването на обекти към по-високи поколения.

Имайте предвид, че когато имате деструктор във вашия клас, изпълнението го третира като метод Finalize (). Тъй като финализирането струва скъпо, трябва да използвате деструктори само ако е необходимо - когато имате някои ресурси във вашия клас, които ще трябва да почистите. Когато имате финализатор във вашия клас, обектите от тези класове се преместват в опашката за финализиране. Ако обектите са достъпни, те се преместват в опашката „Freachable“. GC възстановява паметта, заета от обекти, които не са достъпни. Периодично GC проверява дали обектите, които се намират в опашката "Freachable", са достъпни. Ако не са достъпни, паметта, заета от тези обекти, се възстановява. И така, очевидно е, че обектите, които се намират в опашката "Freachable", ще се нуждаят от повече време, за да бъдат почистени от събирача на боклука.Лоша практика е да имате празни деструктори във вашия C # клас, тъй като обектите за такива класове ще бъдат премествани в опашката за финализиране и след това в опашката "Freachable", ако е необходимо.

Финализаторът се имплицитно извиква, когато паметта, заета от обекта, се възстанови. Въпреки това, GC не гарантира, че ще бъде извикан финализатор - той може или не може да бъде извикан изобщо. По същество финализаторът работи в недетерминиран режим - изпълнението не гарантира, че финализаторът изобщо ще бъде извикан. Можете обаче да принудите финализатора да бъде извикан, макар че това изобщо не е добра практика, тъй като има свързани наказания за изпълнение. Финализаторите винаги трябва да бъдат защитени и винаги да се използват само за почистване на управлявани ресурси. Никога не трябва да разпределяте памет във финализатора, да пишете код, за да внедрите безопасност на нишките или да извиквате виртуални методи от финализатора.

Методът Dispose от друга страна осигурява подход за „детерминирано почистване“ към изчистването на ресурси в .Net. Въпреки това методът Dispose за разлика от финализатора трябва да бъде извикан изрично. Ако имате метод за изхвърляне, дефиниран в клас, трябва да се уверите, че той е извикан. Така че методът Dispose трябва да бъде извикан изрично от клиентския код. Но какво, ако забравите да извикате метода Dispose, изложен от клас, който използва неуправлявани ресурси? Клиентите на екземпляр на клас, който реализира интерфейса IDisposable, трябва да извикат изрично метода Dispose. В този случай трябва да се обадите на Dispose от финализатора. Тази автоматична детерминирана стратегия за финализиране гарантира, че неуправляваните ресурси, използвани във вашия код, се почистват.

Трябва да приложите IDisposable за всеки тип, който има финализатор. Препоръчва се практика да се прилагат както Dispose, така и Finalize, когато имате неуправлявани ресурси във вашия клас.

Следният кодов фрагмент илюстрира как можете да приложите шаблона за окончателно изхвърляне в C #.

защитена виртуална празнина Dispose (изхвърляне на bool)

        {

            ако (изхвърляне)

            {

                // запис на код за почистване на управлявани обекти

            }

            // запис на код за почистване на неуправляеми обекти и ресурси

        }

Този параметризиран метод Dispose може да бъде извикан автоматично от деструктора, както е показано в кодовия фрагмент по-долу.

  ~ Ресурси ()

        {

            ако (! изхвърлено)

            {

                разпоредено = вярно;

                Изхвърляне (невярно);

            }

        }