Очень простой философский вопрос …
Почему не запрещено (на этапе компиляции), создание экземпляра класса
реализующего интерфейс IDisposable, вне конструкции using?
Примеры:
// 1. ссылка не стеке (контролируем); ОКusing(StreamReader sr = new StreamReader(...))
return sr.ReadToEnd();
// 2. ссылка не стеке (контролируем); FAIL
StreamReader sr = new StreamReader(...);
return sr.ReadToEnd();
// 3. ссылка в упр. куче (неконтролируем); OK
StreamReader[] arr = new StreamReader[10];
for(int i = 0; i < 10; i++)
arr[i] = new StreamReader(...);
// 4. странный пример, возможно ислючение из общего
// правила контроля ссылок на стеке; OKobject o = new StreamReader(...);
Накладные расходы не велики, и уберегает начинающих программистов,
от множества проблем связанных, например, с некорректным освобождением ресурсов
(в случае RAII).
Здравствуйте, Denis2005, Вы писали:
D>Очень простой философский вопрос … D>Почему не запрещено (на этапе компиляции), создание экземпляра класса D>реализующего интерфейс IDisposable, вне конструкции using?
D>Примеры:
D>
D>// 1. ссылка не стеке (контролируем); ОК
D>using(StreamReader sr = new StreamReader(...))
D> return sr.ReadToEnd();
D>// 2. ссылка не стеке (контролируем); FAIL
D>StreamReader sr = new StreamReader(...);
D>return sr.ReadToEnd();
D>// 3. ссылка в упр. куче (неконтролируем); OK
D>StreamReader[] arr = new StreamReader[10];
D>for(int i = 0; i < 10; i++)
D> arr[i] = new StreamReader(...);
D>// 4. странный пример, возможно ислючение из общего
D>// правила контроля ссылок на стеке; OK
D>object o = new StreamReader(...);
D>
Потому, что невозможно определить (существующим компилятором) scope созданного объекта. Что делать, если мы вернули объект из метода? Разрешать? А если он там не был использован? Почему пример 3 Ok? Где там Dispose()?
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Denis2005, Вы писали:
D>Очень простой философский вопрос … D>Почему не запрещено (на этапе компиляции), создание экземпляра класса D>реализующего интерфейс IDisposable, вне конструкции using?
Часто приходится делать класс хранящий несколько unmanaged-ресурсов. Что-то типа:
class A : IDisposable {
_f1 : StreamReader;
_f2 : StreamReader;
public A() {
_f1 = new StreamReader();
_f2 = new StreamReader();
}
public void Dispose() {
_f1.Dispose();
_f2.Dispose();
}
}
Сам StreamReader примерно так и реализован. C using такого не сделаешь.
Здравствуйте, Denis2005, Вы писали:
D>// 2. ссылка не стеке (контролируем); FAIL D>StreamReader sr = new StreamReader(...); D>return sr.ReadToEnd();
А можено ведь
return new StreamReader(...)
и освобождать его может кто-то еще в другом месте.
Т.е. в общем случае решить проблему невозможно. А для частных случаев могла бы быть некоторая проверка в конкретном компиляторе шарпа. Возможно, что со временем подобный варнинг появится, но в виде просто дополнения и расширения. Видимо, пока что MS предпочитают тратить ресурсы на что-то более важное
Dispose() это самый обычный метод с точки зрения рантайма. Есть просто соглашение о таком именовании.
Поэтому возможено и подобное:
StreamReader sr = new StreamReader(...
//use
sr.Close()
//process
sr.Open()
//use
sr.Close()
Здравствуйте, Denis2005, Вы писали:
D>Очень простой философский вопрос … D>Почему не запрещено (на этапе компиляции), создание экземпляра класса D>реализующего интерфейс IDisposable, вне конструкции using?
ЭЭЭ а если тебе нужно время жизни этого объекта расширить (не ограничивать временем выполнения метода)?
F>Dispose() это самый обычный метод с точки зрения рантайма. Есть просто соглашение о таком именовании.
но с точки зрения модели освобождения ресурсов в MS, очень даже нужный метод ) F>Поэтому возможено и подобное:
F>StreamReader sr = new StreamReader(... F>//use F>sr.Close() F>//process F>sr.Open() F>//use F>sr.Close()
F>использование полностью корректно, а диспоза нету
это кстати не очень хороший пример, Close() — метод для "удобства" программистов, которые привыкли к Close() и только
Здравствуйте, Sinclair, Вы писали:
S>Потому, что невозможно определить (существующим компилятором) scope созданного объекта. Что делать, если мы вернули объект из метода? Разрешать? А если он там не был использован?
Инстанцирование было в другом месте, вот в нем и заботься о корректности высвобождения, т.е.
StreamReader MyProc()
{
return new StreamReader;
}
void Proc()
{
StreamReader sr = MyProc();
}
при инстанцировании StreamReader ссылка на стеке не закреплена (в MyProc) => не контролируем.
S> Почему пример 3 Ok? Где там Dispose()?
Контролируем только ссылки закрепленные на стеке функции.
Здравствуйте, Denis2005, Вы писали:
D>Здравствуйте, Sinclair, Вы писали:
S>>Потому, что невозможно определить (существующим компилятором) scope созданного объекта. Что делать, если мы вернули объект из метода? Разрешать? А если он там не был использован?
D>Инстанцирование было в другом месте, вот в нем и заботься о корректности высвобождения, т.е.
D>
D>StreamReader MyProc()
D>{
D> return new StreamReader;
D>}
D>void Proc()
D>{
D> StreamReader sr = MyProc();
D>}
D>
Ниче не понятно. В примере позаботиться там, где было инстанцирование, невозможно. D>при инстанцировании StreamReader ссылка на стеке не закреплена (в MyProc) => не контролируем.
Ценность идеи равна нулю. Disposable объекты практически никогда не диспозятся там же, где создаются. Есть очень много фабричных методов. Совершенно невозможно понять, действительно ли это была именно та ссылка на объект, на которой надо было вызывать Dispose. Или это всего лишь какая-то временная переменная. Я могу тут нафигачить примеров, которые поставят в тупик самый умный компилятор. В итоге овчинка не стоит выделки.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Denis2005, Вы писали:
D>Очень простой философский вопрос … D>Почему не запрещено (на этапе компиляции), создание экземпляра класса D>реализующего интерфейс IDisposable, вне конструкции using?
Помимо фабричных методов, о которых сказал Sinclair, disposable-объекты могут быть мемберами других disposable-объектов, т.е. опять же, диспозиться не там, где создаются.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, Denis2005, Вы писали:
V>Помимо фабричных методов, о которых сказал Sinclair, disposable-объекты могут быть мемберами других disposable-объектов, т.е. опять же, диспозиться не там, где создаются.
Тоже самое можно сказать в целом о конструкции using,
что во всех Вами оговоренных примерах она не применима,
и следовательно вообще она плохая.
Я говорю про контроль в тех случаях, где using(...) имеет место.
V>>Помимо фабричных методов, о которых сказал Sinclair, disposable-объекты могут быть мемберами других disposable-объектов, т.е. опять же, диспозиться не там, где создаются.
D>Тоже самое можно сказать в целом о конструкции using, D>что во всех Вами оговоренных примерах она не применима, D>и следовательно вообще она плохая. D>Я говорю про контроль в тех случаях, где using(...) имеет место.
Этот контроль невозможен, включи воображение:
— ты настаиваешь на контроле СТЕКОВЫХ объектов;
— примеры с фабричным методом и членами классов ты отверг;
— но забыл про случаи, когда мы можем передать IDisposable-объект как параметр, и не должны диспозить при выходе из скопа;
— даже если включить твой контроль в оставшийся один случай, т.е.: локальная переменная на стеке никуда не передается и не возвращается, только вызываются методы, то... как ты проконтроллируешь, что в одном из методов IDisposable-объект не регистрирует себя в глобальном мапе, или же в гипотетическом контексте, которым будет являться (или коссвенно ссылаться, не суть) один из параметров при вызове методов этого объекта.
Здравствуйте, vdimas, Вы писали: V>Этот контроль невозможен, включи воображение: V>- ты настаиваешь на контроле СТЕКОВЫХ объектов; V>- примеры с фабричным методом и членами классов ты отверг; V>- но забыл про случаи, когда мы можем передать IDisposable-объект как параметр, и не должны диспозить при выходе из скопа; V>- даже если включить твой контроль в оставшийся один случай, т.е.: локальная переменная на стеке никуда не передается и не возвращается, только вызываются методы, то... как ты проконтроллируешь, что в одном из методов IDisposable-объект не регистрирует себя в глобальном мапе, или же в гипотетическом контексте, которым будет являться (или коссвенно ссылаться, не суть) один из параметров при вызове методов этого объекта.
Совершенно верное изложение проблемы. Получается, что контроль отловит очень малый % реальных утечек Disposable, зато будет стоить довольно дорого в реализации. Подобная тема уже обсуждалась год или два назад; в результате кто-то даже пробовал изобрести правила для FxCOP, посвященные отлову подозрительных действий с IDisposable.
В принципе, наверное более эффективной техникой был бы отладчик/профайлер, который бы отлавливал вызовы Dispose из финализатора. Собственно, именно эти "запоздалые" вызовы и являются признаком плохого использования IDisposable.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>В принципе, наверное более эффективной техникой был бы отладчик/профайлер, который бы отлавливал вызовы Dispose из финализатора. Собственно, именно эти "запоздалые" вызовы и являются признаком плохого использования IDisposable.
Угу, для роперов над хендлами это помогло бы. В моём случае и это не поможет.
Я тут много работаю с роперами над КОМ-объектами, так в финализаторе вызывать Marshal.ReleaseComObject() совсем нет смысла, т.е. обхожусь без двухступенчатого Dispose(bool disposing).