синтаксис за изхвърляне на c#

Наскоро трябваше да внедря функционалност Dispose() и се натъкнах на метод с 1 ред, 2 метода на ред и по-изчерпателни методи.

Метод/функция от 1 ред просто би извикал нещо като "context.Dispose", но методът, който избрах, беше следният:

    bool _disposed = false;

    public void Dispose(bool disposing)
    {
        if (!_disposed && disposing)
        {
            _context.Dispose();
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

Дали този синтаксис просто спира извикването на Dispose() повече от веднъж?


person dotnetnoob    schedule 26.08.2012    source източник
comment
Забравихте финализатора. Също така направете Dispose(bool) виртуален.   -  person Maarten    schedule 26.08.2012
comment
Това може да помогне: Внедряване на IDisposable и Dispose Pattern Правилно   -  person Şafak Gür    schedule 26.08.2012
comment
Това е наследеният модел за изхвърляне. IMO има малка причина да го използвате повече.   -  person CodesInChaos    schedule 26.08.2012
comment
@CodesInChaos - Ако случаят е такъв, какво трябва да се използва на негово място?   -  person dotnetnoob    schedule 26.08.2012
comment
Зависи от ситуацията. SafeHandle като директен собственик на неуправляваните ресурси и прост Dispose метод без финализатор като непряк собственик.   -  person CodesInChaos    schedule 26.08.2012
comment
@CodesInChaos: Това, че ти не си виждал необходимостта да го използваш напоследък, не означава, че вече изобщо не е необходимо.   -  person Johann Gerell    schedule 26.08.2012
comment
@Maarten Добавянето на финализатор просто за извикване на Dispose(false) обикновено не е добро нещо. Вижте bluebytesoftware.com/blog/2011/11/12/ за подробности относно недостатъците на наличието на финализатор, който не води до освобождаване на неуправлявани ресурси   -  person Peter Ritchie    schedule 26.08.2012


Отговори (3)


Това, което сте публикували, е частично модел за изхвърляне. Както някой посочи, трябва да има съответен Dispose(false) във финализатор ("деструктор"). Финализаторът трябва да се използва за изхвърляне на неуправлявани ресурси. Ако нямате неуправляеми ресурси, с които да се справите (т.е. нямате какво да правите, когато disposing е false), нямате нужда от Dispose(false) и следователно не се нуждаете от финализатор. Това означава, чеDispose(true) е единственият път, така че не се нуждаете от Dispose (bool) (и следователно не е необходимо да прилагате шаблона за изхвърляне) и можете да преместите тялото му в Dispose (и да премахнете отметката за disposing) и просто да внедрите Dispose. Например:

public void Dispose()
{
    _context.Dispose();
}

Класовете, които не имплементират собствен финализатор (деструктор), не се поставят в списъка на финализаторите, така че няма нужда да се извиква GC.SuppressFinalize.

Като цяло това е достатъчно, ако създавате клас. Но понякога можете да извлечете от класове, които прилагат този модел. В който случай трябва да внедрите поддръжка за него във вашия клас (заменете Dispose(bool) и направете disposing проверка и Dispose на всички управлявани ресурси). Тъй като базовият клас имплементира IDisposable и извиква virtual Dispose(bool) в своя Dispose(), не е нужно да внедрявате Dispose() сами, но трябва да извикате Dispose(bool) на базата във вашия Dispose(bool). Например:

protected override void Dispose(bool disposing)
{
    if(disposing) _context.Dispose();
    base.Dispose(disposing);
}

Ако се обаждате в базата и тя е имплементирала Dispose Pattern, тогава също не е необходимо да извиквате GC.SuppressFinalize(), защото тя вече го прави.

Можете да направите цялото disposed нещо, ако искате; Намирам обаче, че крие многофункционални грешки.

person Peter Ritchie    schedule 26.08.2012
comment
Питър също ли казваш, че трябва да игнорирам GC.SupressFinalize() също? - person dotnetnoob; 26.08.2012
comment
Ако нямате финализатор, няма какво да потискате. т.е. C# класовете без финализатор (деструктор) не се поставят в списъка за финализиране и не е необходимо да бъдат потискани. - person Peter Ritchie; 26.08.2012
comment
Питър, благодаря за помощта - всичко това е много ново, така че ясните обяснения са добре дошли. - person dotnetnoob; 27.08.2012

Това е само част от модела. Другата липсваща част тук е, че Dispose(false) ще бъде извикана от финализатор.

Флагът за състояние _disposed може също да се използва за проверка и хвърляне на ObjectDisposedExceptions във вашите методи.

Пълният модел е тук

Jon Skeet предоставя добра информация тук и IMO този модел е прекалено много за повечето ситуации, освен ако нямате и неуправляеми ресурси. Ако не, просто изхвърлете вашите управлявани ресурси и GC.SuppressFinalize в реализацията на интерфейса Dispose(). Използвайте флага _disposed само ако възнамерявате да хвърлите ObjectDisposedExceptions.

person StuartLC    schedule 26.08.2012
comment
Джон всъщност не навлиза в много подробности там, освен че аз не следвам този модел често. Джо Дъфи навлиза в някои подробности относно финализирането и последствията от наличието на финализатор, който не освобождава неуправлявани ресурси тук: bluebytesoftware.com/blog/2011/11/12/ - person Peter Ritchie; 26.08.2012

Използвам следните две форми на изхвърляне въз основа на нуждите на класа:

Метод 1 (за клас с управлявани и неуправлявани ресурси или с производни класове):

class ClassHavingManagedAndUnManagedCode : IDiposable
    {
        private volatile bool _disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    //Do managed disposing here.
                }

                //Do unmanaged disposing here.
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);

            _disposed = true;
        }

        ~ClassHavingManagedAndUnManagedCode()
        {
            Dispose(false);
        }
    }

Метод 2 (За клас само с управляван ресурс / запечатан клас / клас, който няма дъщерни класове):

    class ClassHavingOnlyManagedCode  : IDiposable
    {
        private volatile bool _disposed = false;

        public void Dispose()
        {
            if (!_disposed)
            {
                //Dispose managed objects.
                _disposed = true;
            }
        }
    }

Всички дъщерни класове на ClassHavingManagedAndUnManagedCode трябва просто да следват модела на метода на защитеното изхвърляне и да извикат base.Dispose в края на метода на Dispose.

Също така пазете всички публични методи (поне тези, които използват членовете, които са изхвърлени), като използвате метод /check, който хвърля ObjectDisposedException, ако екземплярът на класа вече е изхвърлен.

FxCop винаги ще ви помоли да внедрите формата ClassHavingManagedAndUnManagedCode на Dispose, дори ако нямате неуправлявани ресурси.

person Ganesh R.    schedule 26.08.2012
comment
Ако нямате неуправляеми ресурси, нямате нужда от финализатора. Всъщност, притежавайки го, вие увеличихте напрежението върху GC. Обект не може да бъде събран, докато финализаторът не се изпълни, така че обектът се поддържа жив, докато системата има време да извика финализатора. Вижте моя отговор за подробности относно липсата на нужда от финализатора и това е следствие на Dispose(bool) Вижте също bluebytesoftware.com/blog/2011/11/12/ - person Peter Ritchie; 26.08.2012
comment
@PeterRitchie Съгласен съм с вас, че нямаме нужда от финализатора, ако нямаме неуправляван код. Затова дадох два различни варианта на шаблон за изхвърляне, един за клас с управляван код и един без. Освен това, ако направим GC.SupressFinalize, не съм сигурен дали добавяме някакъв стрес в GC. - person Ganesh R.; 26.08.2012
comment
Имате грешка в кода на метод 2, не изхвърляте нищо, освен ако Dispose не бъде извикано два пъти if(_disposed) {/* dispose managed objects */} _disposed = true; - person Peter Ritchie; 26.08.2012
comment
@PeterRitchie Благодаря. Поправих го. - person Ganesh R.; 26.08.2012
comment
Ако направите цялото почистване в Dispose(bool), когато disposing е true и имате финализатор (деструктор), тогава трябва да имате извикване на GC.SuppressFinalize (или в Dispose() или Dispose(bool), когато disposing е true), за да намалите напрежението върху GC (е, финализаторът – но без SuppressFinalize увеличавате риска обектите да останат живи до gen2, което натоварва GC) - person Peter Ritchie; 26.08.2012
comment
@Ganesh - благодаря за приноса, беше много оценен. Приех отговора на Peter, тъй като ми помогна да разбера малко повече за Dispose(), но гласувах за вашия отговор, тъй като беше особено полезен и ще бъде използван отново в бъдеще. - person dotnetnoob; 27.08.2012
comment
Има ли някакъв сценарий, при който клас с неуправлявани ресурси, изискващ Finalize-базирано почистване, трябва да наследи от клас, чиято цел не е съсредоточена около такова почистване? Във всеки случай, за който се сещам, би било по-добре да обвия неуправляемия ресурс в негов собствен финализиран обект (който може да бъде частен клас, вложен в типа, който го използва), така че съдържащият клас няма да има нужда да се тревожи за финализирането на почистването . - person supercat; 27.08.2012