Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 17.05.13 18:19
Оценка:
Привет всем.

Задумал замутить производный класс от System.IO.Stream....

И реально не догоняю о чем (и чем) думал программер который мутил такую реализацию Close, Dispose в Stream

  Скрытый текст
// System.IO.Stream
/// <summary>Закрывает текущий поток и отключает все ресурсы (например, сокеты и файловые дескрипторы), связанные с текущим потоком.</summary>
/// <filterpriority>1</filterpriority>
public virtual void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

// System.IO.Stream
/// <summary>Освобождает все ресурсы, используемые объектом <see cref="T:System.IO.Stream" />.</summary>
public void Dispose()
{
    this.Close();
}

// System.IO.Stream
/// <summary>Освобождает неуправляемые (а при необходимости и управляемые) ресурсы, используемые объектом <see cref="T:System.IO.Stream" />.</summary>
/// <param name="disposing">
///           Значение true позволяет освободить управляемые и неуправляемые ресурсы; значение false позволяет освободить только неуправляемые ресурсы.</param>
protected virtual void Dispose(bool disposing)
{
    if (disposing && this._asyncActiveEvent != null)
    {
        this._CloseAsyncActiveEvent(Interlocked.Decrement(ref this._asyncActiveCount));
    }
}

Ну вот, черт, возьми и поменяй реализации Close и Dispose местами. Так, как это (фактически) сделано в System.IO.TextReader. И все будут счастливы.

Одним могут сделать Close/Dispose монописуальными.

Другие могут замутить так, что Close!=Dispose. То есть после Close еще можно вызвать Dispose, а вот после Dispose вызвать Close уже нельзя (ObjectDisposedException).

А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Close, Dispose и System.IO.Stream
От: Tom Россия http://www.RSDN.ru
Дата: 18.05.13 13:11
Оценка:
КД>Задумал замутить производный класс от System.IO.Stream....
Изначально не правильная идея.

КД>А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose

Почему???
Народная мудрось
всем все никому ничего(с).
Re[2]: Close, Dispose и System.IO.Stream
От: Jack128  
Дата: 18.05.13 14:26
Оценка:
Здравствуйте, Tom, Вы писали:

КД>>Задумал замутить производный класс от System.IO.Stream....

Tom>Изначально не правильная идея.

Вот как. Почему??
Re[2]: Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 18.05.13 16:13
Оценка:
Здравствуйте, Tom, Вы писали:

КД>>Задумал замутить производный класс от System.IO.Stream....

Tom>Изначально не правильная идея.

Просвети темных — что не правильно, и как правильно?
КД>>А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose
Tom>Почему???

А что — есть способ? Если есть, то я его не асилил.

Вообще, я решил забить на эти тонкости. Раз System.IO.Stream.Close это полный аналог его Dispose — то и черт с ними.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Close, Dispose и System.IO.Stream
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 20.05.13 05:01
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose


А зачем тебе это знать ?
Re[2]: Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 20.05.13 05:18
Оценка:
Здравствуйте, Ikemefula, Вы писали:

КД>>А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose


I>А зачем тебе это знать ?


Чтобы после Close другие методы (кроме Close, Dispose) возвращали ошибку (InvalidOperationException) "Вызов такого-то метода невозможен, потому что объект потока был закрыт.". Кстати, у меня в закрытое состояние Stream может перейти и при вызове Close у родительского подключения.

А после Dispose все другие методы (кроме самого Dispose) возвращали ошибку (ObjectDisposedException) "Вызов такого-то метода невозможен, потому что объект потока был разрушен."

Сдается мне Я точно знаю, что чем точнее и подробнее буду сообщать о причинах отказа в обслуживании, тем проще будет осуществлять удаленную поддержку. Поэтому так и заморачиваюсь
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Close, Dispose и System.IO.Stream
От: HowardLovekraft  
Дата: 20.05.13 05:40
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Чтобы после Close другие методы (кроме Close, Dispose) возвращали ошибку (InvalidOperationException) "Вызов такого-то метода невозможен, потому что объект потока был закрыт.".

КД>А после Dispose все другие методы (кроме самого Dispose) возвращали ошибку (ObjectDisposedException) "Вызов такого-то метода невозможен, потому что объект потока был разрушен."
Заставлять различать извне и наделять Close и Dispose различным поведением — очень плохая идея.
Само по себе наличие Close — ошибка дизайна, но это не значит, что вокруг нужно разложить еще несколько граблей. Close должен быть эквивалентным Dispose.

КД>Кстати, у меня в закрытое состояние Stream может перейти и при вызове Close у родительского подключения.

Что такое "родительское подключение"?
Re[4]: Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 20.05.13 06:10
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Заставлять различать извне и наделять Close и Dispose различным поведением — очень плохая идея.

HL>(1) Само по себе наличие Close — ошибка дизайна, но это не значит, что вокруг нужно разложить еще несколько граблей. (2) Close должен быть эквивалентным Dispose.

(1) Вот о том и речь. Надо было делать Close обычным методом. Раз уж приспичило иметь такой метод. А не виртуальным. И всю логику закрытия потока предлагать реализовывать в Dispose(bool disposing).

(2) Это субъективное мнение смотрящего.

Лично я предпочитаю разделять управление ресурсом и управление объектом:
1. Close — закрывает ресурс.
2. Dispose — "убивает" объект.

Надо быть последовательными. Потому что у некоторых классов после Close объект остается в живых и позволяет с собой работать.

КД>>Кстати, у меня в закрытое состояние Stream может перейти и при вызове Close у родительского подключения.

HL>Что такое "родительское подключение"?
А, ну да. Это подключение к базе данных, в рамках которого был создан выше-обозначенный Stream.

Я делаю потомка System.IO.Stream аналогичного (internal) System.Data.SqlClient.SqlStream. В рамках своего ADO.NET провайдера.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Close, Dispose и System.IO.Stream
От: HowardLovekraft  
Дата: 20.05.13 06:45
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>(1) Вот о том и речь. Надо было делать Close обычным методом.

Его не надо было делать вообще.

КД>(2) Это субъективное мнение смотрящего.

КД>Лично я предпочитаю разделять управление ресурсом и управление объектом:
Вы, конечно, вправе предпочитать что угодно, но Close был задуман всего лишь как метод с более специфичным для предметной области наименованием, нежели Dispose.
Да, иделогически — это тупо синоним. В 99.9% случаев Close и Dispose эквивалентны. Как можно догадаться, исходя из этого большинство юзеров вашей библиотеки так же не будут делать различия между Close и Dispose.

КД>Потому что у некоторых классов после Close объект остается в живых и позволяет с собой работать.

Сколько таких классов вы можете привести в качестве примера, не залезая в гугл?
У скольких из тех, что вы вспомнили, вы сходу сможете объяснить разницу в поведении?
Зачем брать пример с нескольких исключений (причем не факт, что для них это было удачное или хотя бы оправданное решение), если есть вполне определенное правило?
Re[6]: Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 20.05.13 07:23
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>(1) Вот о том и речь. Надо было делать Close обычным методом.

HL>Его не надо было делать вообще.

Я же написал — если приспичило. А в самом первом сообщение я написал — "А в текущем виде этот Close — вот он даром не впился". И дело не в том, что он не нужен. А в том, что Stream.Dispose так реализован, что Stream.Close становится бессмысленным. Особенно забавляет виртуальность Close.

Бага, собственно говоря, заключается в том, что Dispose вызывает Close вместо Dispose(/*disposing*/true);

КД>>Потому что у некоторых классов после Close объект остается в живых и позволяет с собой работать.

HL>Сколько таких классов вы можете привести в качестве примера, не залезая в гугл?

Зачем мне их вспоминать, если я такие уже реализовывал

Объекты, у которых есть Open. То есть после Close можно снова открыть.

Ну можно придумать еще класс без Open, но который после Close сохраняет некоторую статистику о проделанной работе и позволяет её получать. А после Dispose — объект переходит в режим "я разрушен" и отклоняет все вызовы.

HL>У скольких из тех, что вы вспомнили, вы сходу сможете объяснить разницу в поведении?


Это что — так сложно различать Close/Dispose?

В COM, к примеру, есть IUnknown::Release. И его почему-то не путают с Close-подобными методами в интерфейсе.

HL>Зачем брать пример с нескольких исключений (причем не факт, что для них это было удачное или хотя бы оправданное решение), если есть вполне определенное правило?


Вот это вопрос я не понял

Да и вообще я не понял — что конкретно вы пытаетесь мне объяснить? Ранее я уже написал — Раз System.IO.Stream.Close это полный аналог его Dispose — то и черт с ними.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Close, Dispose и System.IO.Stream
От: Tom Россия http://www.RSDN.ru
Дата: 20.05.13 07:59
Оценка:
Tom>>Изначально не правильная идея.
КД>Просвети темных — что не правильно, и как правильно?
Самый правильный вариант это включение стрима а не наследование

КД>>>А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose

Tom>>Почему???

КД>А что — есть способ? Если есть, то я его не асилил.

Способ чего?

КД>Вообще, я решил забить на эти тонкости. Раз System.IO.Stream.Close это полный аналог его Dispose — то и черт с ними.

Ты поясни что не так с Close и Dispose???
Народная мудрось
всем все никому ничего(с).
Re[4]: Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 20.05.13 08:12
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>>>Изначально не правильная идея.

КД>>Просвети темных — что не правильно, и как правильно?
Tom>Самый правильный вариант это включение стрима а не наследование

Вот мои пользователи и будут включать объекты моего потомка от System.IO.Stream.
КД>>>>А в текущем виде этот Close — вот он даром не впился. Потому что нельзя узнать — Close был вызван явно или через Dispose
Tom>>>Почему???

КД>>А что — есть способ? Если есть, то я его не асилил.

Tom>Способ чего?

КД>>Вообще, я решил забить на эти тонкости. Раз System.IO.Stream.Close это полный аналог его Dispose — то и черт с ними.

Tom>Ты поясни что не так с Close и Dispose???

Попробуй, что ли, прочитать сообщения в этой ветке. Вроде все уже разжевано.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Close, Dispose и System.IO.Stream
От: Tom Россия http://www.RSDN.ru
Дата: 20.05.13 08:35
Оценка:
КД>Вот мои пользователи и будут включать объекты моего потомка от System.IO.Stream.
Ты сам можешь его включать, и прекрасно кстати решишь свою проблему этим...
Народная мудрось
всем все никому ничего(с).
Re[3]: Close, Dispose и System.IO.Stream
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 20.05.13 08:51
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

I>>А зачем тебе это знать ?


КД>Чтобы после Close другие методы (кроме Close, Dispose) возвращали ошибку (InvalidOperationException) "Вызов такого-то метода невозможен, потому что объект потока был закрыт.". Кстати, у меня в закрытое состояние Stream может перейти и при вызове Close у родительского подключения.


КД>А после Dispose все другие методы (кроме самого Dispose) возвращали ошибку (ObjectDisposedException) "Вызов такого-то метода невозможен, потому что объект потока был разрушен."


КД>Сдается мне Я точно знаю, что чем точнее и подробнее буду сообщать о причинах отказа в обслуживании, тем проще будет осуществлять удаленную поддержку. Поэтому так и заморачиваюсь


У тебя же Close и Dispose виртуальные, возьми да переопредели их. Но мне шота кажется что ты лишнее делаешь.
Re[4]: Close, Dispose и System.IO.Stream
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 20.05.13 09:19
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>У тебя же Close и Dispose виртуальные, возьми да переопредели их.


А вот что-то не получается Dispose переопределить. Наверное потому что System.IO.Stream.Dispose фактически реализован как "sealed"

// System.IO.Stream
/// <summary>Освобождает все ресурсы, используемые объектом <see cref="T:System.IO.Stream" />.</summary>
public /*нет тут virtual*/ void Dispose()
{
    this.Close();
}


И в результате на мой
public override void Dispose()
{
 this.Dispose(true);
}

Вылазит такая ошибка:

Ошибка 1 "xxxxxxxx.Dispose()" : невозможно переопределить наследуемый член "System.IO.Stream.Dispose()", так как он не помечен как виртуальный (virtual), абстрактный (abstract) или переопределяющий (override)


I>Но мне шота кажется что ты лишнее делаешь.


Дык. Причем очень много лишнего
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Close, Dispose и System.IO.Stream
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 20.05.13 09:38
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

I>>Но мне шота кажется что ты лишнее делаешь.


КД>Дык. Причем очень много лишнего


Тогда тебе хватит одного исключения на оба случая
Re[7]: Close, Dispose и System.IO.Stream
От: HowardLovekraft  
Дата: 20.05.13 10:43
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Зачем мне их вспоминать, если я такие уже реализовывал

Соболезную, однако речь не о вашем коде, а о чем-нибудь более распостраненном.

КД>Объекты, у которых есть Open. То есть после Close можно снова открыть.

Зачем, если можно повторно создать? Что экономим?

КД>Ну можно придумать еще класс без Open, но который после Close сохраняет некоторую статистику о проделанной работе и позволяет её получать.

Сферическое в ваккуме придумывать не надо.

КД>Это что — так сложно различать Close/Dispose?

Да. Причину я указал — в подавляющем большинстве случаев Close и Dispose — синонимы.

КД>В COM, к примеру, есть IUnknown::Release. И его почему-то не путают с Close-подобными методами в интерфейсе.

В COM-е с клиентской стороны не ковырялся лет 8, возразить ничего не могу.

КД>Да и вообще я не понял — что конкретно вы пытаетесь мне объяснить?

Не нужно делать типы, реализующие IDisposable, с различным поведением Close и Dispose.
Re[5]: Close, Dispose и System.IO.Stream
От: hardcase Пират http://nemerle.org
Дата: 20.05.13 11:35
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>А вот что-то не получается Dispose переопределить. Наверное потому что System.IO.Stream.Dispose фактически реализован как "sealed"


Ты не тот Dispose переопределяешь.
Вот нужный:
protected virtual void Dispose(bool disposing)
/* иЗвиНите зА неРовнЫй поЧерК */
Re[5]: Close, Dispose и System.IO.Stream
От: pugv Россия  
Дата: 20.05.13 12:34
Оценка: +1
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Лично я предпочитаю разделять управление ресурсом и управление объектом:

КД>1. Close — закрывает ресурс.
КД>2. Dispose — "убивает" объект.

Dispose никого не убивает, он только освобождает неуправляемые ресурсы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.