В IDisposable модел трябва ли базов клас да позволява на производни класове да споделят своя флаг за изхвърляне?

В момента работя върху коригирането на C# кодова база, която няма добър модел на използване на Dispose.

Това е голяма кодова база, изискваща ресурси и използва много персонализирани неуправляеми c++ библиотеки на ниско ниво.

Разбирам добре модела на изхвърляне. Прекарах известно време в разбиране на статията, която според мен е златен стандарт по въпроса: Статия за изхвърляне на Джо Дъфи

В опит да сведем до минимум дублирането на код, обмисляхме някои помощни класове за изхвърляне и затова въпросът ми:

Ако базов клас имплементира стандартен модел на Dispose, трябва ли той да позволи споделянето на неговия флаг за dispose, т.е. маркирани като защитени?

За да изясня, имам предвид, трябва ли да има само едно булево състояние в рамките на наследствена херархия, което определя дали екземпляр на обект е бил изхвърлен или трябва да има частно булево състояние на всяка стъпка от стълбата на наследяване?

Примерите в MSDN и в горната връзка задават флаг на всяко ниво, но никога не обясняват мотивите зад него. Двоен съм по въпроса, какви са вашите мисли и причини?


person morechilli    schedule 03.07.2009    source източник


Отговори (4)


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

Например, разгледайте сценария, при който имате свойство Disposed само за четене в най-основния клас. Подкрепеното поле е зададено на true само в метода Dispose(disposing) на основния клас. Това означава, че свойството Disposed може да върне true само ако се извика базовият клас Dispose (с изключение на злото отражение, разбира се). Това позволява на базовия клас да предостави изпълним договор.

Сега помислете за обратното, където има защитен сетер. Всеки клас вече може произволно да зададе свойството Disposed на true, без да изхвърля нищо. Това създава възможност за Dispose да върне true, когато нищо не е изхвърлено.

Бих избрал първия вариант, защото осигурява най-изпълнимия договор.

person JaredPar    schedule 03.07.2009

Бих препоръчал да имате свойство Disposed в базовия клас с публичен гетер и защитен сетер.

person SLaks    schedule 03.07.2009
comment
Благодаря - но за да изясня защо предпочитате единствената защитена собственост пред частната собственост на всяко ниво? - person morechilli; 03.07.2009
comment
По-малко дублиране на код. Тъй като не можете да разполагате с всеки слой поотделно, няма смисъл да имате отделни свойства. Освен това, ако имате отделни свойства, кое трябва да бъде изложено на клиентите на класовете? - person SLaks; 03.07.2009

В случай, че имате методи, които трябва да направят нещо различно, когато класът е бил изхвърлен, например хвърляне на изключение, бих създал защитено свойство само за получаване за базовия клас, което чете частното поле на основния клас. По този начин позволявате на всеки наследник да разбере дали е в състояние да извърши операция.

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

person jmservera    schedule 03.07.2009

В допълнение към отговора на JaredPar, бих добавил, че не винаги е необходимо да има флаг _disposed. Много често другите "свързани с ресурсите" полета на обекта естествено предоставят начин за представяне на изхвърленото състояние.

Например, външен COM обект, който трябва да бъде Shutdown, преди да бъде отхвърлен, ще бъде представен чрез препратка към COM обекта и така съответната част от Dispose ще бъде:

if (_comObject != null)
{
    _comObject.Shutdown();
    _comObject = null;
}

Това може безопасно да се изпълнява многократно, без да се извиква Shutdown повече от веднъж, както се изисква. Други методи, които се опитват да използват _comObject след Dispose, ще получат NullReferenceException или в идеалния случай тези методи ще проверят полето _comObject и ще хвърлят ObjectDisposedException.

Намирам, че това е по-често вярно, отколкото не - след като често прилагах IDisposable, не мога да си спомня някога да съм се нуждаел от отделно поле _disposed. Въвеждането на такъв би увеличило степените на свобода (увеличи броя на начините да го прецакам).

Така че това е малко вероятно да бъде толкова полезно за производни класове, дори и да е безопасна идея (което не е, както обяснява JaredPar).

person Daniel Earwicker    schedule 03.07.2009