// Освобождение ресурсов
if (some != null)
{
some.Dispose();
}
Раз этот код сильно похож и выделяется в шаблон, то его логично вынести в отдельный метод, а еще лучше в Extension Method для типа IDisposable:
static class IDisposableExtention
{
// Безопасный, относительно NullReferenceException Dispose
public static void SafeDispose(this IDisposable self)
{
if (self != null)
{
self.Dispose();
}
}
}
После чего, искомые четыре строчки кода для Dispose-вызова свернуться до одной - вызова нашего метода SafeDispose:
some.SafeDispose();
some.SafeDispose(); // При повторном вызове ничего не произойдет.
Присвоение self = null тут лишнее. Ведь фактически вы обнуляете ссылку на экземпляр лишь в локальной переменной-параметре self.
ОтветитьУдалитьТо есть после вызова some.SafeDispose(); переменная some не станет равной null.
@zhe
ОтветитьУдалитьСпасибо! Подправил.
Так ведь всё равно some не станет равной null.
ОтветитьУдалитьДа, занулить some тогда никак не получиться, впринципе, можно и без зануления обойтись (обычный Dispose).
ОтветитьУдалитьМожет есть у кого идеи, как еще и занулять some?
Как-то так.
ОтветитьУдалитьМожно использовать класс-хелпер, в единственный метод которого можно передавать переменную по ссылке:
static class DisposeHelper
{
public static void SafeDisposeAndNull(ref object instance)
{
var disposable = instance as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
instance = null;
}
}
использование:
DisposeHelper.SafeDispose(some);
Но тут теряется сама идея расширения за счёт extension-методов.
Упс. Накосячил с использованием. :) Должно быть так.
ОтветитьУдалитьDisposeHelper.SafeDispose(ref some);
Опять накосячил ))))) Должно быть так:
ОтветитьУдалитьDisposeHelper.SafeDisposeAndNull(ref some);
Что-то я не пойму, зачем занулять some?
ОтветитьУдалитьВедь после того как мы вызвали Dispose, это уже не мы заботимся о переменной, а сборщик мусора.
По идее для нас Dispose фактически тоже самое, что и уничтожение объекта, и далее его использовать нельзя.
2Vadim Sentyaev:
ОтветитьУдалитьтеоретически возможна ситуация, когда вы сможете попытаться вызвать метод уже диспоузнутого экземпляра. И этот метод может даже успешно выполниться :)
Видимо автор занулением some пытается избежать таких ситуаций и сделать код более робустным ))
Лучше, если вызов такого метода упадёт по NullReferenceException, нежели выполнится.
@zhe
ОтветитьУдалитьВ идеале конечно же надо ловить ObjectDisposedException, но когда объект null , все сомнения в том, что объект еще можно использовать отпадают).
@Vadim Sentyaev
Но все-таки главная идея избавиться от проверки на null.
В качестве бонуса можно добавить свой код pre/post код перед/после Dispose:
public static void MyDispose(this IDisposable self)
{
if (self != null)
{
// pre-code
self.Dispose();
// post-code
}
}
zhe:
ОтветитьУдалитьЭто да.
С другой стороны есть
using (Some some = new Some()) {}
а далее к нему уже не обратишься.
Vadim Sentyaev, согласен. С using вообще не надо городить весь огород с проверками, обнулениями и т. д.
ОтветитьУдалитьАвтор видать имеет в виду примеры, когда using использовать низя. Например, если создание объекта происходит в одном месте, а его уничтожение - вообще хрен знает где :)
Бывают и такие ситуации.
Бывает даже так:
ОтветитьУдалитьSome some;
using (some = new Some()) { ... }
Или даже так :)
ОтветитьУдалитьSome some = new Some();
using (some) { ... }
Походу у меня знаний не хватает...
ОтветитьУдалитьusing (Some some = new Some()) {}
разворачивается на:
Some some = new Some()
try
{
...
}
finally
{
((IDisposable)some).Dispose();
}
Тогда в зачем использовать
Some some;
using (some = new Some()) { ... }
или
Some some = new Some();
using (some) { ... }
Ведь в любом случае после блока using к переменным нельзя обращаться, они уже как-бы "уничтожены", ну или если это применить к Stream, то он может быть закрыть.
Vadim Sentyaev, рассуждаете правильно. Но язык разрешает это делать. Ничего не поделаешь )))
ОтветитьУдалитьЯ читал на codeproject статейку, в которой индийский программист призывал всех создавать строки DataRow примерно таким способом:
ОтветитьУдалитьusing(DataRow row = table.NewRow()){
//тут строка заполняется значениями
table.Rows.Add(row);
}
потом идет работа с таблицей...
И ведь работает, к сожалению... А тот программист был уверен что это очень наглядный пример использования IDisposable
Почему называется safe?
ОтветитьУдалитьThread safe диспоузинга вроде нет.
@Omari
ОтветитьУдалитьSafe относительно NullReferenceException.
Например, если some=null, то some.SafeDispose(); отработает нормально, и проверка на null не нужна.
Насколько я помню, под MDX еще имеет смысл проверять IsDisposed или то-то вроже этого.
ОтветитьУдалитьМне кажется, что присвоить ссылке null после вызова Dispose - это хороший тон.
ОтветитьУдалитьЧто мешает писать:
ОтветитьУдалитьusing (some) {}
в качестве безопасного Dispose?
@zhe
ОтветитьУдалитьвариант с ref object работать не будет.
можно так:
static public void SafeDisposeAndNull(ref T self) where T : class, IDisposable {
if ( null != self ) {
self.Dispose();
self = null;
}
}
;)
@Илья Дубаденко
ОтветитьУдалитьМне кажется, что присвоить ссылке null после вызова Dispose - это хороший тон.
Мне почему то думается что не совсем это хорошая идея.
Давайте посмотрим на пару IDisposable классов.
1. Stream
После Dispose можно проверить CanRead, CanWrite
2. SqlConnection
После Dispose можно проверить ConnectionState.
Причем соединение может закрыться и до вызова Dispose. Так что в любом случае нужно обрабатывать ошибки.
3. SerialPort
После Dispose можно проверить IsOpen
И порт может независимо от нас закрыться. Например зависнит подключеный к COM порту GSM модем (стандартная ситуация).
Ну и такое.
Например если мы реализуем пул соединений, то вызов Dispose() на конкретном соединении из пула просто вернет его в пул.
Мне кажется за Dispose нужно программисту следить.
А то получается мы хотим сдлать деструктор.
В итоге на мой взгляд лучший вариант, если уж очень хочется занулить объект после Dispose это:
some.SafeDispose();
some = null;
dogwatch, это почему это мой вариант не будет работать?
ОтветитьУдалитьdogwatch, "if ( null != self )" - Йода-код детектед )))
ОтветитьУдалить"Мне кажется, что присвоить ссылке null после вызова Dispose - это хороший тон"
ОтветитьУдалитьПо моему ясней, когда это явно делает объект - владелец ссылки. К тому же он еще и отвечает за время жизни объекта.
А так получается, что ссылка вдруг обнулилась и слово "Safe" не вносит ясности. Может "DisposeAndNullify"?
@zhe
ОтветитьУдалитьтип должен совпадать. иначе ошибка компиляции
@Omari
ОтветитьУдалитьСовершенно с Вам согласен с тем, что занулением должен заниматься владелец объекта. Вот только хотелось бы придумать красивый путь зануления через extention-метод (похоже такого решения не существует). Но пока занулять можно явно присвоить null или через Helper-метод.
2dogwatch:
ОтветитьУдалитьВставил свой код в студию, компилируется и работает нормально. Что я делаю не так?