Могут ли компиляторы, нацеленные на CLR, генерировать вызов метода Dispose, когда для объекта установлено значение null? - PullRequest
3 голосов
/ 23 августа 2010

Могут ли компиляторы (например, C #) автоматически генерировать вызов метода Dispose для объекта, когда для него установлено значение null (конечно, объект должен поддерживать метод Dispose в первую очередь).Например, если мы напишем

cnSqlConnection = null;

и cnSqlConnection является экземпляром типа SqlConnection, может ли компилятор C # внедрить вызов метода Dispose прямо перед обновлением ссылки на null?

Кроме того, поскольку классы инфраструктуры поддерживают сценарий, в котором метод Dispose может вызываться несколько раз, не будет никакого вреда, если вызов будет дублирован.

Ответы [ 7 ]

7 голосов
/ 23 августа 2010

(a) Правильно реализованный объект должен выполнить ту же логику очистки в своем финализаторе.Если вы пропустите вызов Dispose, логика финализатора, вероятно, будет работать в любом случае.

(b) Раймонд Чен объясняет здесь сложность автоматического удаления .Подводя итоги: только действительно безопасно заставить человека-программиста вызывать Dispose в нужной точке.Если вы возьмете автоматическое удаление до логического завершения, то в итоге получите счетчик ссылок, чего и избегает модель памяти CLR.

4 голосов
/ 23 августа 2010

Это было бы весьма противоречиво, я приведу несколько примеров:
Данный класс:

class A : IDisposable { public void Dispose() { } }

Пример 1:

IDisposable a = new A();
IDisposable b = a;
a = null; // The object is still alive in b, should it really be disposed?

Пример 2:

IDisposable a = new A();
IDisposable b = new A();
a = b; // a is not accessible anymore, but not set to null, 
       //shouldn't it be disposed here?

Пример 3:

private void Foo()
{
    IDisposable a = new A();
    // a is not used any more, but not set to null, 
    //shouldn't it be disposed here?
}

В c # есть блок using, который решает проблему, к которой вы идете:

using (IDisposable a = new A())
{
    // Do stuff
}   // Here a.Dispose() is automatically called.
2 голосов
/ 23 августа 2010

Не может, это просто ссылка на объект. Может быть много ссылок на один и тот же объект, все они будут указывать на мертвый объект, если компилятор сделает это автоматически. Это гарантированный кабум.

Основным свойством сборщика мусора является то, что ни компилятор, ни вы не можете узнать, сколько ссылок существует на объект. Это может сделать только сборщик мусора. И это уже происходит, он автоматически завершает объект, если все ссылки исчезли.

Смысл функции IDisposable () - не ждать, пока это не произойдет. Поскольку компилятор не может этого сделать и не может быть автоматизирован, вы можете пообещать , что других живых ссылок на объект не существует. Если вы не можете дать такое обещание, вам не следует вызывать Dispose () и оставлять его на усмотрение сборщика. Стандарт в коде взаимодействия COM, например.

Другие схемы автоматического управления памятью могут сделать это. Они используют подсчет ссылок, распространенный до того, как сборщики мусора стали мейнстримом. Дорого и ненадежно, потому что не может справиться с циклами. Microsoft финансировала исследовательский проект по добавлению подсчета ссылок в CLR. Это был большой бюст . После этого GC получил гораздо больше уважения.

2 голосов
/ 23 августа 2010

Технически, да, компилятор может сделать это, но в идеале он не будет .

Причина в том, что у вас возникнут проблемы с определением, есть ли что-либо еще, удерживающее ссылку. Компилятор не может определить, кто хранит ссылку во время компиляции (возможно, с помощью статического анализа, но это не гарантировано).

Теперь, это может быть возможно сделать во время выполнения, но это все еще не идеально. Требуется эквивалент GC каждый раз, когда для ссылки задано значение NULL (метка и развертка). Затем для всего, что является GCed, если есть реализация IDisposable, вызовите Dispose. Это затянет CLR в грязь и заставит его работать ужасно.

Конечно, всегда есть подсчет ссылок (как упомянул nonnb в своем комментарии к вопросу), но это просто возвращение к COM, и тоже не идеально. Сложности подсчета ссылок - это то, что породило аспект GC CLR.

Что следует учесть: если у вас есть ситуация, когда вы не знаете, кто на самом деле владеет вашей реализацией IDisposable, тогда это представляет собой недостаток дизайна в вашей системе. Если у вас есть функция, которая работает с такими экземплярами, она должна либо сделать явное заявление о том, что она будет распоряжаться такими экземплярами, либо оставить это на усмотрение вызывающей стороны (последний является предпочтительным методом).

1 голос
/ 23 августа 2010

Да, компилятор может автоматически генерировать вызов Dispose. Реальный вопрос в том, хорошая это идея или нет. И ответ на этот вопрос, безусловно, нет.

Рассмотрим следующий пример.

void DoSomething(IDisposable disposable)
{
  DoSomethingElse(disposable);
  disposable = null;
}

В вышеприведенном примере, как вы DoSomethingElse не храните свою собственную отдельную ссылку на экземпляр disposable или что вызывающий DoSomething делает что-то похожее? Это действительно испортило бы все, если бы объект был удален, в то время как другие части кода предполагают, что объект «живой».

Более интересный вопрос - почему структуры, которые IDisposable действительно вызывают Dispose, вызываются автоматически, когда заканчивается их область действия. Должно быть совершенно очевидно, почему выполнение этого для ссылочных типов не будет работать, но как насчет структур? Рассмотрим следующие осложнения.

  • Разрешить бокс или нет?
  • Стоит ли ограничивать их передачей только по ссылке?
  • Должны ли вы разрешить присваивания переменной?

Я уверен, что список не является исчерпывающим, но вы поняли. Эти проблемы разрешимы, но стоит ли это того? Компилятору, возможно, придется наложить на них столько ограничений, что их варианты использования станут весьма ограниченными. Хотя это интересная тема.

Относительно этой темы вы можете найти этот блог интересным.

0 голосов
/ 23 августа 2010

Установка ссылки на объект на ноль пометит объект как освобожденный для сборки мусора, но не вызовет явное удаление.Если объект реализует IDisposable, вы должны явно вызывать Dispose (), когда закончите работу с объектом, или заключить его в оператор using.

Вот несколько полезных сведений об IDisposable и сборке мусора:

http://www.xtremedotnettalk.com/showthread.php?t=97910

0 голосов
/ 23 августа 2010
public static class MissingCompilerFeatures
{
    public static void SetToNullAndDispose(ref IDisposable obj)
    {
        if (obj != null) { obj.Dispose(); obj = null; }
    }
}
...