2015-07-29 8 views
4

Мы все знаем шаблон System.IDisposable. Это было описано время Zillion, также здесь, на StackOverflow:Почему Dispose() не должен распоряжаться управляемыми ресурсами и финализатором?

ссылка: Dispose() for cleaning up managed resources?

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

Вы можете видеть, что это происходит потому, что следующий код рекомендуется:

protected void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     // Code to dispose the managed resources of the class 
    } 
    // Code to dispose the un-managed resources of the class 
} 

I что мой класс должен внедрять System.IDisposable всякий раз, когда у моего класса есть (частный) член, который реализует System.IDisposable. Dispose (bool) должен вызвать Dispose() частного члена, если логическое распоряжение является истинным.

Почему это проблема, если Dispose будет вызываться во время финализации? Итак, почему следующий Dispose будет проблемой, если он вызывается во время финализации?

protected void Dispose(bool disposing) 
{ 
    if (myDisposableObject != null) 
    { 
     myDisposableObject.Dispose(); 
     myDisposableObject = null; 
    } 
} 
+3

Финализаторы [не гарантируются выполнение в детерминированном порядке] (http://stackoverflow.com/questions/4163603/why-garbage-collector-takes-objects-in-the-wrong-order). В частности, любая из ваших управляемых ссылок, возможно, уже была доработана до того, как будет вызываться ваш финализатор. –

+0

@DanBryant Не только финализировано либо - ссылки, которые ваш экземпляр имеет на другие объекты, больше не являются обязательными для GC, поэтому управляемые объекты, на которые вы ссылаетесь, могли быть уже собраны *. Даже проверку 'myDisposableObject! = Null' недостаточно, так как вы находитесь в многопоточной среде - эта ссылка может быть отменена между проверкой и вызовом' Dispose'. И, наконец, вполне вероятно, что финализатор на этом объекте также был запланирован (и, возможно, даже закончен) уже, так что его утилизация, вероятно, выполняется как на управляемой, так и на неуправляемой стороне. – Luaan

+0

По правде говоря, вам вообще не нужно будет реализовывать финализатор. Все необходимые вам финализаторы уже написаны - используйте безопасные ручки, и вы должны быть в порядке почти все время. Они могут быть полезны для целей отладки («забыл избавиться от этого объекта»), но вам редко нужны они для того, для чего они предназначены - освобождение неуправляемых ресурсов управляемого объекта. В вашем случае вопрос, который вы задаете, уже предает это - вам нужно только заботиться о неуправляемых ресурсах; вызов 'Dispose' на другом управляемом объекте является вмешательством, где вы не должны. Не делай этого. – Luaan

ответ

3

Вообще говоря, вы должны избавиться от ресурсов как можно скорее. Если вам не нужен ресурс, почему бы вам сохранить его впустую?

Кроме того, для вызова Dispose() во время финализации вам необходимо создать финализатор для вашего объекта, то есть - деструктор на C#. Однако точное время вызова финализатора объекта не является детерминированным, что означает, что ваши управляемые объекты могут быть недоступны и недоступны в данный момент. Даже поток, который выполняет ваш финализатор, не является детерминированным, что также может привести к проблемам, которые трудно предвидеть.

Для этого необходимо создать финализаторы для освобождения неуправляемых ресурсов.

Очень мало программистов понимают «полностью», как работает доработка. Например, сборщик мусора распознает, что ваш тип имеет финализатор во время создания объекта и помещает ваш экземпляр в специальную внутреннюю структуру данных, называемую finalizequeue. Действительно, когда вы отлаживаете свое приложение с помощью sos.dll (windbg), вы можете использовать команду! FinalizeQueue, чтобы отображать объекты, которые имеют финализаторы, и еще не завершены в какой-то более поздний момент времени.

+0

Неверно, что ваши управляемые объекты могут быть недоступны и недоступны во время финализации. Просто их финализаторы можно было бы назвать уже, если они существуют. –

2

При выполнении финализации объекта, один из следующих будет справедливо практически для любого IDisposable объекта может держать:

  1. Он держал только ссылку на этот другой объект, и финализации этого другого объекта имеет уже поэтому нет необходимости ничего с этим делать.

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

  3. Другие объекты по-прежнему используют объект IDisposable, и в этом случае финализатор не должен звонить Dispose.

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

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

+0

Правильно ли я вас понимаю: если я использую одноразовые объекты .NET, я все равно должен реализовать System.IDisposable и использовать метод Dispose (bool) из Dispose() и destructor? И в методе Dispose (bool) я должен проверить булевское распоряжение и ТОЛЬКО ПРИЗЫВАЮТ УТИЛИЗИРОВАТЬ ДРУГИЕ ОБЪЕКТЫ, ЕСЛИ Я УДАЛЯЮТСЯ? –

+1

@HaraldDutch: очень мало объектов должны иметь финализаторы/деструкторы C#, и если базовый класс не имеет финализатора, но производному классу потребуется неуправляемая очистка, производный класс не должен реализовывать сам финализатор, а вместо этого хранить ссылку на частный объект, который делает. Объекты, которые * сами * объекты 'IDisposable' должны реализовать' IDisposable' сами, и имеют 'Dispose (true)' call 'IDisposable.Dispose' на принадлежащих им объектах, но ничто никогда не должно вызывать 'Dispose (false)'. – supercat