Здравствуйте, _FRED_, Вы писали:
_FR>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
Да про такое даже прочитать где-то сложно, так что в практической надобности такого вопроса я сомневаюсь. Лично я бы конечно заподозрил и ответил, что нет, но не был бы точно уверен почему.
Сейчас специально открыл Рихтера, Албахари и спецификацию C# и нигде не нашел описания такого случая. Так что если надо кого-то завалить на собеседовании и почувствовать своё превосходство, то да, вопрос хороший.
Здравствуйте, _FRED_, Вы писали:
R>>Тут вот в процессе работы родился простенький вопросик на понимание конструкции using.
_FR>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал). _FR>Ну нафик. Лучше уж про виртуальный деструктор спросить.
В пользу безполезности вопроса высказывается и компилятор:
warning CS0728: Possibly incorrect assignment to local 'file' which is the argument to a using or lock statement. The Dispose call or unlocking will happen on the original value of the local.
А спрашивать о том, что произойдёт, когда компилятор это сам скажет, не должно.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, MozgC, Вы писали:
MC>Здравствуйте, _FRED_, Вы писали:
_FR>>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
MC>Да про такое даже прочитать где-то сложно, так что в практической надобности такого вопроса я сомневаюсь. Лично я бы конечно заподозрил и ответил, что нет, но не был бы точно уверен почему. MC>Сейчас специально открыл Рихтера, Албахари и спецификацию C# и нигде не нашел описания такого случая. Так что если надо кого-то завалить на собеседовании и почувствовать своё превосходство, то да, вопрос хороший.
Ну зря вы так в штыки. Если вдуматься, то это вполне логичное осмысленное поведение. И вот небольшой пример, иллюстрирующий полезность такого поведения:
class InboundMessageBuffer : IDisposable
{
public int Read(byte[] data, int offset, int count)
{
return _stream.Read(data, offset, count);
}
public void PushInboundMessage(byte[] bytes)
{
// Вот так элегантно освобождается старый буфер и создается новый using (_stream)
_stream = new MemoryStream(bytes);
}
public void Dispose()
{
using (_stream)
{ }
}
#region Implementation
MemoryStream _stream;
#endregion
};
И знать это нужно не для того чтоб кого-то завалить и показать свое превосходство, а в первую очередь для того, чтоб правильно и эффективно использовать предоставляемые платформой и языком возможности.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, _FRED_, Вы писали:
R>>>Тут вот в процессе работы родился простенький вопросик на понимание конструкции using.
_FR>>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал). _FR>>Ну нафик. Лучше уж про виртуальный деструктор спросить.
_FR>В пользу безполезности вопроса высказывается и компилятор: _FR>
_FR>warning CS0728: Possibly incorrect assignment to local 'file' which is the argument to a using or lock statement. The Dispose call or unlocking will happen on the original value of the local.
_FR>А спрашивать о том, что произойдёт, когда компилятор это сам скажет, не должно.
Да, это результат того, что пример был максимально упрощен. Если заменить локальную переменную на поле класса, то компилятор уже не выдает такого предупреждения (пример здесь
R> // Вот так элегантно освобождается старый буфер и создается новый
R> using (_stream)
R> _stream = new MemoryStream(bytes);
R> ...
R> using (_stream)
R> { }
R>
Здравствуйте, rg45, Вы писали:
R>Да, это результат того, что пример был максимально упрощен. Если заменить локальную переменную на поле класса, то компилятор уже не выдает такого предупреждения (пример здесь
идея не ясна. потрудитесь донести ее более доходчиво. зачем сие, для чего такое знание, и где такое используется? приведенный пример не прошел бы code review в большинстве компаний
Здравствуйте, rg45, Вы писали:
R>Ну зря вы так в штыки. Если вдуматься, то это вполне логичное осмысленное поведение. И вот небольшой пример, иллюстрирующий полезность такого поведения:
R> // Вот так элегантно освобождается старый буфер и создается новый
R> using (_stream)
R> _stream = new MemoryStream(bytes);
Прикольно. Но без соответствующего коментария (того самого, который имеется) этот код очень подозрителен.
R>И знать это нужно не для того чтоб кого-то завалить и показать свое превосходство, а в первую очередь для того, чтоб правильно и эффективно использовать предоставляемые платформой и языком возможности.
Вопрос не в том, "зачем это нужно знать", а в том, "зачем это спрашивать на собеседовании". Знать это, несомненно, полезно, но спрашивать: Ну разве что только в том случае, что вы уже положительно решили взять человека.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, rg45, Вы писали:
R>Ну зря вы так в штыки. Если вдуматься, то это вполне логичное осмысленное поведение. И вот небольшой пример, иллюстрирующий полезность такого поведения: R>
...
Отвечать по отдельности каждому затруднительно, постараюсь ответить одним постом на все вопросы.
Соглашусь, пример не очень удачный и возникшие вопросы закономерны. Что скажете на счет такого: допустим, есть фасадный класс, инкапсулируюший в себе несколько объектов, реализующих интерфейс IDisposable. Сам фасадный класс также реализует интерфейс IDisposable и должен при своем разрушении освободить все агрегированные объекты. Поскольку агрегированные объекты так или иначе создаются последовательно, то существует вероятность возникновения исключения, когда часть объектов уже созданы, а часть еще нет. В этой ситуации необходимо обеспечить корректное освобождение всех ресурсов, захваченных на момент возникновения исключения. При этом хочется, чтоб код был по возможности компактным и читабельным. Я в этих случаях обычно поступаю так:
class SomeFacade : IDisposable
{
public SomeFacade()
{
try
{
_disposable1 = CreateDisposable1();
_disposable2 = CreateDisposable2();
_disposable3 = CreateDisposable3();
}
catch
{
using (this)
throw;
}
}
public void Dispose()
{
using (_disposable1)
using (_disposable1)
using (_disposable1)
{ }
}
private IDisposable _disposable1;
private IDisposable _disposable2;
private IDisposable _disposable3;
}
Преимущество конструкции using вместо явного вызова Dispose становится более очевидным, когда разрушаемых объектов много. Сравните:
public void Dispose()
{
if (_disposable1 != null)
_disposable1.Dispose();
if (_disposable2 != null)
_disposable2.Dispose();
if (_disposable3 != null)
_disposable3.Dispose();
}
Что же касается полезности того, что конструкция using лочит состояние ссылки на момент входа, и не отслеживает изменение переменной внутри блока, то, ИМХО, все становится достаточно очевидно, если вспомнить что аргументами конструкции using могут быть не только идентификаторы переменных, но и произвольные выражения. В этом плане запись: using (someDisposable) ничем не отличается от записи using (SomeDisposable.CreateInstance()). Т.е. в первом случае, так же как и во втором, идентификатор someDisposable играет роль выражения, значение которго и будет "защелкнуто" конструкцией using. И я не согласен с теми, кто утверждал, что это невозможно сообразить, а можно только прочесть или обнаружить эмпирически. И уж тем более не согласен с теми, кто утверждает, что знать это не обязательно.
Я на все вопросы ответил?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Простенький вопрос к собеседованию
От:
Аноним
Дата:
17.09.10 13:49
Оценка:
Здравствуйте, rg45, Вы писали:
R>Соглашусь, пример не очень удачный и возникшие вопросы закономерны. Что скажете на счет такого: допустим, есть фасадный класс, инкапсулируюший в себе несколько объектов, реализующих интерфейс IDisposable. Сам фасадный класс также реализует интерфейс IDisposable и должен при своем разрушении освободить все агрегированные объекты...
но конструктор не отработал, значит экземпляр класса не создан. должен ли он заниматся саморазрушением? имхо вопрос тут в архитектуре...
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, rg45, Вы писали:
R>>Соглашусь, пример не очень удачный и возникшие вопросы закономерны. Что скажете на счет такого: допустим, есть фасадный класс, инкапсулируюший в себе несколько объектов, реализующих интерфейс IDisposable. Сам фасадный класс также реализует интерфейс IDisposable и должен при своем разрушении освободить все агрегированные объекты...
А>но конструктор не отработал, значит экземпляр класса не создан. должен ли он заниматся саморазрушением? имхо вопрос тут в архитектуре...
Он успел создать некоторые объекты, реализующие интерфейс IDisposable. И если он их не разрушит то это чревато утечками системных ресурсов — неуправляемой памяти, объектами ядра, подвисшими потоками и даже процессами. Ведь понятие системных ресурсов несколько шире, выделение и освобождение памяти и освобождение далеко не всех ресурсов по зубам сборщику мусора.
--
Не можешь достичь желаемого — пожелай достигнутого.
class Util{
public static void CheckAndDispose(IDisposable obj){
if (obj != null)
obj.Dispose();
}
}
...
public Dispose(){
Util.CheckAndDispose(_disposable1);
Util.CheckAndDispose(_disposable2);
Util.CheckAndDispose(_disposable3);
}
Сразу понятно что, кто и зачем.
А использование using для этих целей похоже на хак с недокументированными возможностями.
Не для того using придуман был.
Здравствуйте, _FRED_, Вы писали:
_FR>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
Вот тут не соглашусь. Как-то проблем с ответом не возникло ни у меня, ни у коллег — хотя и не пробовал никто такого. Наверное каждый кто пытался представить во что будет разворачиваться using конструкция ответит без проблем.
Однако зачем такое спрашивать на собеседовании не пойму. Что покажет ответ на этот вопрос? Тогда уже лучше сразу попросить написать эквивалент using — по крайней мере покажет что человек не угадывал, а понимает как оно работает.
LF>Сразу понятно что, кто и зачем. LF>А использование using для этих целей похоже на хак с недокументированными возможностями. LF>Не для того using придуман был.
Не согласен. О каких недокументированных возможностях речь? О том, что оператор using может быть использован с нулевым аргументом? В MSDN это недвусмысленно зафиксировано: http://msdn.microsoft.com/en-us/library/yh598w02.aspx. Внимание на секцию Remarks и пример.
А что касается 'CheckAndDispose' — можно, конечно. Но зачем повсеместно делать то, чего можно не делать? Лишние сущности, лишний код, лишние нагромождения, дополнительная возможность допускать ошибки...
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Преимущество конструкции using вместо явного вызова Dispose становится более очевидным, когда разрушаемых объектов много. Сравните: R>
R> public void Dispose()
R> {
R> if (_disposable1 != null)
R> _disposable1.Dispose();
R> if (_disposable2 != null)
R> _disposable2.Dispose();
R> if (_disposable3 != null)
R> _disposable3.Dispose();
R> }
R>
Ну тогда и вы сравните:
...
public static class DisposableExtension
{
public static void MyDispose(this IDisposable d)
{
if (d != null)
d.Dispose();
}
}
...
public void Dispose()
{
_disposable1.MyDispose();
_disposable1.MyDispose();
_disposable1.MyDispose();
}
...
Меня постоянно преследуют умные мысли. Но я быстрее.
Здравствуйте, Jesmus, Вы писали:
_FR>>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
J>Вот тут не соглашусь. Как-то проблем с ответом не возникло ни у меня, ни у коллег — хотя и не пробовал никто такого. Наверное каждый кто пытался представить во что будет разворачиваться using конструкция ответит без проблем.
J>Однако зачем такое спрашивать на собеседовании не пойму. Что покажет ответ на этот вопрос? Тогда уже лучше сразу попросить написать эквивалент using — по крайней мере покажет что человек не угадывал, а понимает как оно работает.
Дело в том, что когда задаётся такой вот вопрос, то, если не знаешь точный ответ, варианта поиска решения два:
Компилятор поступает так, как я предполагаю
В компиляторе есть некий специальный кейс на данный случай
Как оно окажется по-правде — соображалки определить не удастся. То есть получаются угадалочки.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, rg45, Вы писали:
R>Вот так вот! Аргументы кончились, поэтому претендуем на абсолютную истину
я не верю что вам самому нравится ваш не_очевидный код после всего здесь услышанного.
советую применить то что предложено выше. сообщать о данном действии не обязательно =)
Меня постоянно преследуют умные мысли. Но я быстрее.
Здравствуйте, rg45, Вы писали:
R>Соглашусь, пример не очень удачный и возникшие вопросы закономерны. Что скажете на счет такого: допустим, есть фасадный класс, инкапсулируюший в себе несколько объектов, реализующих интерфейс IDisposable. Сам фасадный класс также реализует интерфейс IDisposable и должен при своем разрушении освободить все агрегированные объекты. Поскольку агрегированные объекты так или иначе создаются последовательно, то существует вероятность возникновения исключения, когда часть объектов уже созданы, а часть еще нет. В этой ситуации необходимо обеспечить корректное освобождение всех ресурсов, захваченных на момент возникновения исключения. При этом хочется, чтоб код был по возможности компактным и читабельным. Я в этих случаях обычно поступаю так: R>
Все таки, хочется дойти в этом вопросе до какого-то логического завершения. Поймите меня правильно, я вовсе не горю желанием тупо спорить. Для меня очень важно сформировать правильную точку зрения по этому вопросу. Конечно, не только по этому, но по этому тоже. Возможно, я в чем-то заблуждаюсь, тогда я буду благодарен, если вы объясните мне мою ошибку. Давайте конструктивно, что по вашему мнению есть плохого или не правильного в этом примере? Только просьба, давайте временно (подчеркиваю — временно) вопрос привычности и понятности кода оставим за скобками, и рассмотрим сугубо техническую часть проблемы.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали: R>Я на все вопросы ответил?
Пытаемся судорожно изобрести новый паттерн ? Вам надо на с++ форум, там любят обсудить как бы так извратится что бы никто кроме автора кода не понял что написано. Да и сам автор через полгода уже не факт что вспомнит зачем он написал эту фигню .
Здравствуйте, rg45, Вы писали:
R>Все таки, хочется дойти в этом вопросе до какого-то логического завершения. Поймите меня правильно, я вовсе не горю желанием тупо спорить. Для меня очень важно сформировать правильную точку зрения по этому вопросу. Конечно, не только по этому, но по этому тоже. Возможно, я в чем-то заблуждаюсь, тогда я буду благодарен, если вы объясните мне мою ошибку. Давайте конструктивно, что по вашему мнению есть плохого или не правильного в этом примере? Только просьба, давайте временно (подчеркиваю — временно) вопрос привычности и понятности кода оставим за скобками, и рассмотрим сугубо техническую часть проблемы.
Только то, что вы за замысловатыми конструкциями прячете простой по сути код. Зачем делать сложно, когда можно сделать просто?
Здравствуйте, Aviator, Вы писали:
A>Здравствуйте, rg45, Вы писали: R>>Я на все вопросы ответил? A>Пытаемся судорожно изобрести новый паттерн ? Вам надо на с++ форум, там любят обсудить как бы так извратится что бы никто кроме автора кода не понял что написано. Да и сам автор через полгода уже не факт что вспомнит зачем он написал эту фигню .
Зло и неконструктивно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, Jesmus, Вы писали:
_FR>>>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
J>>Вот тут не соглашусь. Как-то проблем с ответом не возникло ни у меня, ни у коллег — хотя и не пробовал никто такого. Наверное каждый кто пытался представить во что будет разворачиваться using конструкция ответит без проблем.
J>>Однако зачем такое спрашивать на собеседовании не пойму. Что покажет ответ на этот вопрос? Тогда уже лучше сразу попросить написать эквивалент using — по крайней мере покажет что человек не угадывал, а понимает как оно работает.
_FR>Дело в том, что когда задаётся такой вот вопрос, то, если не знаешь точный ответ, варианта поиска решения два: _FR>
_FR>Компилятор поступает так, как я предполагаю _FR>В компиляторе есть некий специальный кейс на данный случай _FR>_FR>Как оно окажется по-правде — соображалки определить не удастся. То есть получаются угадалочки.
Почему же не знаешь? В спецификации пункт 8.13 достаточно популярно расписывает, как раскладывается using statement. Так что тут никаких гаданий.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, rg45, Вы писали:
R>>Все таки, хочется дойти в этом вопросе до какого-то логического завершения. Поймите меня правильно, я вовсе не горю желанием тупо спорить. Для меня очень важно сформировать правильную точку зрения по этому вопросу. Конечно, не только по этому, но по этому тоже. Возможно, я в чем-то заблуждаюсь, тогда я буду благодарен, если вы объясните мне мою ошибку. Давайте конструктивно, что по вашему мнению есть плохого или не правильного в этом примере? Только просьба, давайте временно (подчеркиваю — временно) вопрос привычности и понятности кода оставим за скобками, и рассмотрим сугубо техническую часть проблемы.
L>Только то, что вы за замысловатыми конструкциями прячете простой по сути код. Зачем делать сложно, когда можно сделать просто?
Правильно ли я понял мысль, "простой по сути код" должен выглядеть так:
public class Util
{
public static void CheckAndDispose(IDisposable obj)
{
if (obj != null)
obj.Dispose();
}
}
class SomeFacade : IDisposable
{
public SomeFacade()
{
try
{
_disposable1 = CreateDisposable1();
_disposable2 = CreateDisposable2();
_disposable3 = CreateDisposable3();
}
catch
{
Dispose();
throw;
}
}
public void Dispose()
{
Util.CheckAndDispose(_disposable1);
Util.CheckAndDispose(_disposable2);
Util.CheckAndDispose(_disposable3);
}
private IDisposable _disposable1;
private IDisposable _disposable2;
private IDisposable _disposable3;
}
???
И какие конструкции в моем коде кажутся замысловатыми, эти что ли:
using (disposable1) { }
???
Потрясающе!!! Неужели действительно непонятно какой эффект произведет эта конструкция? А вот у меня гораздо большее недоверие вызовет конструкция:
Util.CheckAndDispose(_disposable1);
Потому что это еще надо проверять, нет ли в ней ошибок, утечек и т.д.
Извините, не убедили, мой код мне кажется более простым, более надежным и более читабельным.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
L>>Только то, что вы за замысловатыми конструкциями прячете простой по сути код. Зачем делать сложно, когда можно сделать просто?
R>Правильно ли я понял мысль, "простой по сути код" должен выглядеть так: R>
Да я бы и Util.CheckAndDispose убрал бы. Экономии — ноль.
R>И какие конструкции в моем коде кажутся замысловатыми, эти что ли: R>
R>using (disposable1) { }
R>
R>???
Да. Если у вас есть возражение, предлагаю почитать, для чего был придуман using и убедиться, что не для этого.
R>Потрясающе!!! Неужели действительно непонятно какой эффект произведет эта конструкция? А вот у меня гораздо большее недоверие вызовет конструкция: R>
R>Util.CheckAndDispose(_disposable1);
R>
R>Потому что это еще надо проверять, нет ли в ней ошибок, утечек и т.д.
Зачем?
R>Извините, не убедили, мой код мне кажется более простым, более надежным и более читабельным.
Если вы код пишете исключаительно для себя, то без проблем. Если же вы работаете в команде, то стоит прислушиваться к мнению окружающих.
Здравствуйте, rg45, Вы писали:
R>Зло и неконструктивно.
хотите конструктивно? ок. using добавлен в язык для детерминированного уничтожения объектов. его использование подразумевает некий scope за пределами которого происходит разрушение объекта. ни один из ваших примеров НЕ вписывается в предназначение using. то, что вы делаете — это действительно сродни хаку. разработчик не ждет такого его использования.
Здравствуйте, Uzzy, Вы писали:
_FR>>>>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
J>>>Вот тут не соглашусь. Как-то проблем с ответом не возникло ни у меня, ни у коллег — хотя и не пробовал никто такого. Наверное каждый кто пытался представить во что будет разворачиваться using конструкция ответит без проблем.
J>>>Однако зачем такое спрашивать на собеседовании не пойму. Что покажет ответ на этот вопрос? Тогда уже лучше сразу попросить написать эквивалент using — по крайней мере покажет что человек не угадывал, а понимает как оно работает.
_FR>>Дело в том, что когда задаётся такой вот вопрос, то, если не знаешь точный ответ, варианта поиска решения два: _FR>>
_FR>>Компилятор поступает так, как я предполагаю _FR>>В компиляторе есть некий специальный кейс на данный случай _FR>>_FR>>Как оно окажется по-правде — соображалки определить не удастся. То есть получаются угадалочки.
U>Почему же не знаешь? В спецификации пункт 8.13 достаточно популярно расписывает, как раскладывается using statement. Так что тут никаких гаданий.
Так почему бы тогда просто не спросить (если именно это и интересует) собеседуемого: "спеку много думали?"
В том-то и паранойя данного вопроса: что он на "понимание" (слово топикстартера), в то время, как умение понимать никак на вопрос ответить не поможет: сгодится лишь "знание" ("пробовал") или "память" ("читал" [спецификацию, имеется в виду, если кто не понял]).
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
_FR>В том-то и паранойя данного вопроса: что он на "понимание" (слово топикстартера), в то время, как умение понимать никак на вопрос ответить не поможет: сгодится лишь "знание" ("пробовал") или "память" ("читал" [спецификацию, имеется в виду, если кто не понял]).
Есть один момент, который мне кажется странным в твоих рассуждениях. Почему ты отождествляешь категории "понимать" и "додуматься самому"? Это ведь не одно и тоже. Понимание чего-либо может быть результатом: а) логических рассуждений (додумался сам), б) анализа практического опыта и в) результатом вдумчивого изучения. Большинство из нас прекрасно понимают смысл интеграла и производной, но это совсем не означает, что все мы сами до этого додумались. Можно конечно же знать что либо, но при этом не понимать (как теорию относительности, например, или принцип наименьшего действия). Но я счел (это мое ИМХО), что в данном вопросе знание без понимания не имеет особой ценности, поэтому и написал — что вопрос на понимание. Знание также подразумевается, потому что понимать, не зная, вряд ли возможно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, rg45, Вы писали:
R>>Зло и неконструктивно.
А>хотите конструктивно? ок. using добавлен в язык для детерминированного уничтожения объектов. его использование подразумевает некий scope за пределами которого происходит разрушение объекта. ни один из ваших примеров НЕ вписывается в предназначение using. то, что вы делаете — это действительно сродни хаку. разработчик не ждет такого его использования.
Конструктивно, лаконично и явно не без здравого смысла. Спасибо. Беру тайм-аут на обмозговывание.
--
Не можешь достичь желаемого — пожелай достигнутого.
public Dispose(){
using(_disposable1)
using(_disposable2)
using(_disposable3)
{ }
}
и такой:
public Dispose(){
Util.CheckAndDispose(_disposable1);
Util.CheckAndDispose(_disposable2);
Util.CheckAndDispose(_disposable3);
}
А теперь допустим, что во время вызова _disposable1.Dispose() возникает исключение. В первом случае, не смотря на это исключение, метод Dispose для объектов _disposable2 и _disposable3 будет вызван. А во втором не будет. И если в верхних слоях программы это исключение перехватывается, то весь этот сценарий может повториться снова и снова. Со всеми вытекающими последствиями. Как быть? Поместим CheckAndDispose во вложенные блоки try-catch? И во что после этого превратится Ваше "просто и понятно"?
Конечно же вопрос о том должны ли из методов Dispose лететь исключения или не должны — вопрос сам по себе не тривиальный. Напрмер, в C++ есть заповедь (если можно так выразиться), что деструкторы не должны бросать исключений. Но, во-первых, исключения могут лететь из вызываемых внутри деструкторов функций, и вряд ли будет хорошо, если деструктор будет тупо давить все подряд исключения. А во-вторых, Dispose существенно отличается от деструкторов C++ и многие из обоснований, справедливых для деструкторов не применимы для Dispose.
Ну и наконец, хорошо ли плохо ли, но факт остается фактом: исключения могут лететь из Dispose и сбрасывать со счетов этот факт нельзя.
Что скажете? (Вопрос не только к LF).
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, rg45, Вы писали:
R>>Зло и неконструктивно.
А>хотите конструктивно? ок. using добавлен в язык для детерминированного уничтожения объектов. его использование подразумевает некий scope за пределами которого происходит разрушение объекта. ни один из ваших примеров НЕ вписывается в предназначение using. то, что вы делаете — это действительно сродни хаку. разработчик не ждет такого его использования.
Хорошо, допустим, убедили. Как предлагаете решать проблему, с исключениями, описанную мной здесь: Dispose и исключения
Здравствуйте, Аноним, Вы писали:
А>но конструктор не отработал, значит экземпляр класса не создан.
Раз было выполнение конструктора, то экземпляр был создан. Когда-то здесь на форуме уже обсуждалась разница восприятия конструктора в дотнете и в си.
А> должен ли он заниматся саморазрушением? имхо вопрос тут в архитектуре...
Саморазрушение — неудачное слово. Это больше относится к деструктору. Здесь идет речь о освобождении захваченных ресурсов, а их освободить может только класс-хозяин. А Dispose метод должен всегда учитывать случай, что инстанс может быть в неопределенном состоянии.
ЗЫ: Вот только дизайн класса выше мне совсем не нравится.
R> class InboundMessageBuffer : IDisposable
R> {
R> public void PushInboundMessage(byte[] bytes)
R> {
R> // Вот так элегантно освобождается старый буфер и создается новый R> using (_stream)
R> _stream = new MemoryStream(bytes);
R> }
R> MemoryStream _stream;
R> };
R>
Ваш старый буфер элегантно не освобождается. Освобождается новый после использования.
Здравствуйте, Pro100Oleh, Вы писали:
PO>Здравствуйте, rg45, Вы писали:
R>>
R>> class InboundMessageBuffer : IDisposable
R>> {
R>> public void PushInboundMessage(byte[] bytes)
R>> {
R>> // Вот так элегантно освобождается старый буфер и создается новый
PO>R> using (_stream)
R>> _stream = new MemoryStream(bytes);
R>> }
R>> MemoryStream _stream;
R>> };
R>>
PO>Ваш старый буфер элегантно не освобождается. Освобождается новый после использования. PO>
Здравствуйте, rg45, Вы писали:
R>Хорошо, допустим, убедили. Как предлагаете решать проблему, с исключениями, описанную мной здесь: Dispose и исключения
А она разве есть?
Контракт IDisposable предполагает то, что метод Dispose не выбрасывает исключений.
А знаете почему? Потому что Dispose может быть вызван в потоке финализатора.
Двойка Вам. Дизасеммблировать дизасемблировали, а прочесть правильно не можете. Метод Dispose() будет вызван для того, объекта, на который ссылалась переменная this._stream перед входом в блок try-catch, внимание на выделенные строчки.
А если это не достаточно наглядно, то не поленюсь написать пример:
using System;
class SomeDisposable : IDisposable
{
public string Name;
public SomeDisposable(string name) { Name = name; }
public void Dispose() { Console.WriteLine(Name + " is disposed"); }
}
class Program
{
static void Main(string[] args)
{
SomeDisposable someDisposable = new SomeDisposable("First");
using(someDisposable)
someDisposable = new SomeDisposable("Second");
}
}
Output:
First is disposed
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, rg45, Вы писали:
R>>Хорошо, допустим, убедили. Как предлагаете решать проблему, с исключениями, описанную мной здесь: Dispose и исключения
?
H>А она разве есть? H>Контракт IDisposable предполагает то, что метод Dispose не выбрасывает исключений. H>А знаете почему? Потому что Dispose может быть вызван в потоке финализатора.
Я как бы в курсе, что контракт предполагает. Прочитайте внимательно мой пост, я писал об этом. То, что Dispose не должен сам бросать исключений — это и ежу понятно. А как быть с исключениями, летящими из функций, вызываемых внутри Dispose? Вы предлагаете намертво их глушить и все? А если нет, значит проблема есть, нравится нам это или нет.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, Pro100Oleh, Вы писали:
PO>>Вот решение для случая, когда ресурсы класса создаются в конструкторе:
H>Финализатор в данном конкретном случае совсем не обязателен, так как из ресурсов у нас только единственный управляемый объект Stream.
Stream рассматривайте только как пример (видимо неудачный).
Pro
Re[6]: Простенький вопрос к собеседованию
От:
Аноним
Дата:
18.09.10 08:27
Оценка:
Здравствуйте, Pro100Oleh, Вы писали:
PO>Вот решение для случая, когда ресурсы класса создаются в конструкторе:
решение чего? До вызова финализатора FileStream файл не закроется. MyStreamWriter финализтор, кстати, не нужен, тем более, что он неправильно работает.
Здравствуйте, rg45, Вы писали:
R>Хорошо, допустим, убедили. Как предлагаете решать проблему, с исключениями, описанную мной здесь: Dispose и исключения
?
я считаю, что если один из Dispose кидает исключение, то это как бы совсем плохо и нада падать. не должен Dispose кидать исключения и все тут. ну на мой взгляд это таки да — сродни исключениям в деструкторе С++.
в любом случае в случае исключения в одном из Dispose, а потом в одном из следующих (в вашем примере) произойдет утеря исходного исключения (я о исключении в первом Dispose). вот еще реальный пример:
sealed class Disposable : IDisposable
{
public void Dispose()
{
throw newNotImplementedException();
}
}
...
try
{
using (var disposable = new Disposable())
{
throw new OutOfMemoryException();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Результат: System.NotImplementedException: The method or operation is not implemented.
что будет если в Disaposable.Dispose кинет исключение? будет утеряно рабочее исключение. совсем не гуд. ошибка как бы есть, но стерта новой (возможно абсолютно неинтересной юзеру типа ArgumentNullException. а что с ним делать? логировать и падать).
R>Кстати, почему анонимно?
так быстрее )
Здравствуйте, Pro100Oleh, Вы писали:
А>> должен ли он заниматся саморазрушением? имхо вопрос тут в архитектуре... PO>Саморазрушение — неудачное слово. Это больше относится к деструктору. Здесь идет речь о освобождении захваченных ресурсов, а их освободить может только класс-хозяин. А Dispose метод должен всегда учитывать случай, что инстанс может быть в неопределенном состоянии.
+1
PO>ЗЫ: Вот только дизайн класса выше мне совсем не нравится.
Я уже согласился, что пример неудачный — он был придуман налету, не подумайте, что из живого проекта. Пример, по которому хотелось бы получить критику находится здесь
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, rg45, Вы писали:
R>>Хорошо, допустим, убедили. Как предлагаете решать проблему, с исключениями, описанную мной здесь: Dispose и исключения
? А>я считаю, что если один из Dispose кидает исключение, то это как бы совсем плохо и нада падать. не должен Dispose кидать исключения и все тут. ну на мой взгляд это таки да — сродни исключениям в деструкторе С++. А>в любом случае в случае исключения в одном из Dispose, а потом в одном из следующих (в вашем примере) произойдет утеря исходного исключения (я о исключении в первом Dispose). вот еще реальный пример: А>
...
А>
А>Результат: System.NotImplementedException: The method or operation is not implemented.
А>что будет если в Disaposable.Dispose кинет исключение? будет утеряно рабочее исключение. совсем не гуд. ошибка как бы есть, но стерта новой (возможно абсолютно неинтересной юзеру типа ArgumentNullException. а что с ним делать? логировать и падать).
Короче говоря, на уровне нашего класса мы эту проблему решать не пытаемся. Правильно я понял мысль? Уповаем на то, что логика приложения верхнего уровня так и реализована — логировать и падать. А что если приложение ведет себя по-другому? Я так понимаю, эту проблему мы тоже на уровне нашего класса не решаем. Ну что ж, не могу сказать что я в восторге, но похоже другого выхода просто нет. Проблему "затирания" одного исключения другим я как-то даже выпустил из виду.
Еще раз спасибо.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[11]: Простенький вопрос к собеседованию
От:
Аноним
Дата:
18.09.10 09:05
Оценка:
Здравствуйте, rg45, Вы писали:
R>Короче говоря, на уровне нашего класса мы эту проблему решать не пытаемся. Правильно я понял мысль? Уповаем на то, что логика приложения верхнего уровня так и реализована — логировать и падать. А что если приложение ведет себя по-другому? Я так понимаю, эту проблему мы тоже на уровне нашего класса не решаем. Ну что ж, не могу сказать что я в восторге, но похоже другого выхода просто нет. Проблему "затирания" одного исключения другим я как-то даже выпустил из виду.
а это все вопрос архитектуры и дизайна. тут нарыл тему
была еще одна интересная на форуме, большая, основательный срач там был ( ), не нашел к сожалению .
в любом случае, разрабатывая класс, мы должны следовать принципу "Dispose не кидает исключений", либо не юзать FCL и писать все руками подразумевая такую возможность. только все равно не ясно как быть с потерянными исключениями
R>Еще раз спасибо.
да не за что
Здравствуйте, Аноним, Вы писали:
А>... А>что будет если в Disaposable.Dispose кинет исключение? будет утеряно рабочее исключение. совсем не гуд. ошибка как бы есть, но стерта новой (возможно абсолютно неинтересной юзеру типа ArgumentNullException. а что с ним делать? логировать и падать).
Ну и если попытаться подытожить, то типовая реализация метода Dispose должна выглядеть схематично так:
public void Dispose()
{
try
{
disposable1.Dispose();
disposable2.Dispose();
disposable3.Dispose();
}
catch(Exception innerException)
{
throw new FatalError_DoNotCatchThis(innerException);
}
}
По необходимости можно использовать предложенную в этом топике технологию CheckedDispose. Так?
--
Не можешь достичь желаемого — пожелай достигнутого.
имхо по идее так, НО как мне думается таких проверок делать не нужно, тк писать нужно Dispose так чтобы он не кидал исключений, т.е. не писать там кода который планово может кинуть исключение. в этом случае вызывающий код не ждет исключения того типа, что неожиданно произошло в Dispose вследствии чего его не обрабатывает попадая в AppDomain.UnhandledException ну а дальше логаем и FailFast.
R>По необходимости можно использовать предложенную в этом топике технологию CheckedDispose. Так?
мне она не нравится.
Здравствуйте, rg45, Вы писали:
_FR>>Сомневаюсь, что вопрос "на понимание". Скорее "на знание" (пробовал\не пробовал) или "на память" (читал\не читал).
_FR>>В том-то и паранойя данного вопроса: что он на "понимание" (слово топикстартера), в то время, как умение понимать никак на вопрос ответить не поможет: сгодится лишь "знание" ("пробовал") или "память" ("читал" [спецификацию, имеется в виду, если кто не понял]).
R>Есть один момент, который мне кажется странным в твоих рассуждениях. Почему ты отождествляешь категории "понимать" и "додуматься самому"?
"Понимать" — это термин топикстартера, а не мой
R>Это ведь не одно и тоже.
Не одно.
R>Понимание чего-либо может быть результатом: а) логических рассуждений (додумался сам), б) анализа практического опыта и в) результатом вдумчивого изучения.
Может быть. Но в контексте данного обсуждения поведения using "логические рассуждения" не имеют смысла, ибо в данном случае что бы правильно ответить нужно или "знать" или "угадать". "Угадать" к "понимать" отношения не имеет, а вот "знать" можно или в следствии "практического опыта" (пробовал, как я говорил) или после "вдумчивого изучения" (спецификации языка, как я говорил). Надеюсь, так менее странно звучит.
R>Большинство из нас прекрасно понимают смысл интеграла и производной, но это совсем не означает, что все мы сами до этого додумались. Можно конечно же знать что либо, но при этом не понимать (как теорию относительности, например, или принцип наименьшего действия). Но я счел (это мое ИМХО), что в данном вопросе знание без понимания не имеет особой ценности, поэтому и написал — что вопрос на понимание. Знание также подразумевается, потому что понимать, не зная, вряд ли возможно.
Отличие в том, что в вопросах правил языка различных исключений (то есть "специальных кейсов") достаточно много, чуть ли не столько же, сколько и самих правил. Поэтому полагаться на рассуждение "по аналогии" в вопросе поведения компилятора более чем не надёжно.
Help will always be given at Hogwarts to those who ask for it.