All streams
Search
Write a publication
Pull to refresh
-22
0

Разработчик

Send message
Прежде чем бросаться добавлять финализаторы для всех классов, реализующих IDisposable, стоит подумать, а действительно ли они так нужны…

Я так написал, потому что если вы зайдёте на страницу IDisposable в MSDN, например, то там сразу и пример, в котором используется Dispose() вместе с финализатором. И некоторые программисты просто берут и копируют этот пример к себе, а когда спрашиваешь у них зачем тут финализатор, отвечают — потому что паттерн такой.
Сразу скажу, что Reactive Extensions я никогда не использовал, поэтому могу ошибаться. Я так понял что IDisposable, который возвращается из Subscribe, нужен для того чтобы потом отписаться от нотификаций через вызов Dispose(), а в RxJava аналогичный метод называется unsubscribe(). То есть если мне не нужно отписываться, то я могу и не звать Dispose(). Я не уверен что они правильно выбрали интерфейс IDisposable для этих целей, потому что IDisposable предполагает что ресурс в конце надо задиспозить, а здесь вроде как можно этого и не делать, что сбивает с толку и приводит вот к таким вопросам To Dispose or not to Dispose, that is the question?

Никто не обязан освобождать неуправляемые ресурсы в финализаторе. Если я считаю что финализатор принесёт больше проблем чем пользы, то я могу вообще его не добавлять, а возложить всё это на плечи того кто использует мой объект. В качестве примера вот вам цитата из вашей статьи: «The StreamWriter class owns a Stream object; StreamWriter.Close will flush its buffers and then call Stream.Close. However, if a StreamWriter was not closed, its finalizer cannot flush its buffers. Microsoft „solved“ this problem by not giving StreamWriter a finalizer, hoping that programmers will notice the missing data and deduce their error.»
Так я и не предлагаю писать финализаторы, и тем более обращаться там к стриму.

MemoryStream реализует IDisposable, но не держит неуправляемый ресурс только потому что он является наследником Stream. А если бы он не был его наследником, то и IDisposable у него реализовывать смысла не было бы. Если у вас есть просто Stream и вы не знаете что там, ну или в общем случае объект, реализующий IDisposable, то следует предполагать что там внутри может оказаться неуправляемый ресурс, и поэтому нужно в конце явно позвать Dispose() у этого объекта.
Я не согласен с тем, что закрыть хэндл это проблемы стрима. Это проблемы того кто использует стрим — освободить его через Dispose().
Если у вас голый IntPtr, то его нужно освободить в Dispose(), потому что это неуправляемый ресурс.
Если у вас объект типа Stream, то у него тоже нужно позвать Dispose() в своём Dispose(), потому что Stream — это фактически обёртка над неуправляемым ресурсом.
Есть там у Stream свой финализатор или нет, какая разница? В своём Dispose() освобождаем всё что нужно, вот и всё.
Согласен. Но здесь у вас фактически тоже неуправляемый ресурс, только обёрнутый в IDisposable.
Вот здесь например пишут о повторном использовании объекта таким способом. Сам я ни разу не встречал на практике
Нет такого вопроса и быть не должно — финализатор и Dispose решают принципиально разные задачи.

Понятно что не должно быть, но такие вопросы задают например на stackoverflow.

Вы скрываете (или сами не знаете) единственную цель финализатора — спасти от последствий утечек не проект (для него в момент вызова уже поздно), а систему в целом.

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

А вот здесь чистейшая деза. Финализаторы не нужны по совсем другой причине — для аварийной очистки есть более надежное место. И, разумеется, классам, не владеющим неуправляемыми ресурсами, финализаторы (и их аналоги) не нужны от слова совсем.

Где здесь деза я не понял. Классам, не владеющим неуправляемыми ресурсами, и IDisposable как правило не нужен, потому что его предназначение «Provides a mechanism for releasing unmanaged resources.»
Ну и где здесь дезинформация?
Чтобы не было утечек неуправляемых ресурсов, нужно их самому освобождать через Dispose(), а не надеяться на финализаторы.
А почему вы решили сделать его статическим? Его и в using не обернёшь
Если выдавать предупреждение на каждое деление, то будет много ложных срабатываний.
Мы сделали так: если выражение в знаменателе было как-то ограничено, например в условии блока if, или это счётчик цикла, который принимает ограниченное количество значений и ноль в него тоже входит, тогда выдаём предупреждение.
Не является, но поддержать нам его в любом случае надо, поэтому нам его нужно как-то у себя зарегистрировать.
Сначала мы зарегистрировали Enumerable.Reverse как метод IEnumerable и List.Reverse как метод List и при анализе выражения list.Reverse() пытались выбрать из двух вариантов. После этого решили зарегистрировать List.Reverse как метод List и Enumerable.Reverse как метод Enumerable.
Мы сначала так и хотели сделать, но возникли некоторые сложности. Например, List.Reverse() переставляет элементы списка, а IEnumerable.Reverse() возвращает новую коллекцию, соответственно во втором случае анализатор должен сказать что результат вызова Reverse() нужно использовать. В общем, нам просто было удобнее проаннотировать классы, чем как-то различать такие похожие случаи.
Да, мы захардкодили информацию о стандартных классах .NET Framework, которая может быть нам полезна при статическом анализе. Для наследников системных классов это тоже будет работать. Для вашего SuperList — нет, в будущем мы возможно сделаем поддержку пользовательских аннотаций для своих классов.
По поводу List.Count — мы не анализируем исходники .NET Framework и соответственно не учитываем их конткракты, то что Count — это количество элементов нам достаточно, чтобы считать его неотрицательным.
Если функция публичная, то она может быть вызвана откуда угодно, и про аргументы тогда ничего нельзя сказать.
Контракты со временем тоже поддержим и будем учитывать
Проверка там как раз не лишняя. Ошибка в первом условии, из-за чего специальная оптимизация для пробела никогда не выполняется. Это баг.
Польза есть — это видно из примеров, особенно в первом примере явный баг. В большинстве случаев находятся просто лишние проверки, но это не false positive.
В D деструкторы для структур детерминированные — вызываются при выходе из области видимости
Count имеет тип int, но на практике он никогда не будет меньше нуля. И PVS-Studio знает об этом.
Об остальных предупреждениях, на которые стоит посмотреть, я записал им в баг трекер.

Information

Rating
Does not participate
Location
Тула, Тульская обл., Россия
Date of birth
Registered
Activity