Когда CLR говорит, что у объекта есть финализатор?

Я знаю, что в C #, если вы пишете ~MyClass(), это в основном переводится на override System.Object.Finalize(). Итак, пишете ли вы деструктор или нет, у каждого типа в CLR будет Finalize()метод ( System.Objectпо крайней мере).

1] Значит ли это, что каждый объект по умолчанию имеет финализатор?

2] Что является основанием для CLR, чтобы решить, что объект должен быть помещен в очередь завершения?

Я спрашиваю об этом, потому что у меня был класс, скажем, ManagedResourceHolderкоторый реализован IDisposable, но не вызывал GC.SuppressFinalize(this)его IDisposable.Dispose()метод. Класс не содержал никаких неуправляемых ресурсов, и в ~ManagedResourceHolder()методе не было необходимости , что, в свою очередь, означало отсутствие необходимости в GC.SuppressFinalize(this)вызове, так как не было финализатора .

3] В контексте описанного выше сценария всегда ли необходимо предоставлять финализатор при реализации IDisposable? (даже в классе, который не содержит неуправляемых ресурсов)

Правило FxCop CA1816 давало мне нарушение по этому поводу, и ответ, который я получил здесь, когда я спросил на форуме CA по MSDN, смутил меня.

Спасибо.

11.12.2008 08:51:38
4 ОТВЕТА
РЕШЕНИЕ

Вопросы 1 и 2 : CLR в основном проверяет, переопределен ли финализатор. Если это не так, он рассматривает это как отсутствие финализатора.

Преимущество наличия финализатора в System.Object заключается в том, что компиляторы знают, что они всегда могут вызвать его base.Finalize(). Это позволяет избежать проблем с версиями. Рассмотрим мир без System.Object.Finalize():

  • System.Object (без финализации)
  • Acme.BaseClass (без финализации)
  • MyCompany.DerivedClass (Завершить)

Без Finalizeметода в объекте финализатор в MyCompany.DerivedClass не может ничего вызывать. Что приводит к проблеме, когда версия 2 Acme.BaseClass выходит с финализатором. Если вы не перекомпилируете MyCompany.DerivedClass, экземпляр DerivedClass будет завершен без вызова BaseClass.Finalize, что, безусловно, является плохой вещью.

Теперь рассмотрим ту же ситуацию с System.Object.Finalize - компилятор вставляет вызов base.Finalize автоматически в DerivedClass.Finalize, который в версии 1 просто вызывает реализацию no-op в System.Object. Когда выходит версия 2 Acme.BaseClass, вызов base.Finalizeбудет (без перекомпиляции DerivedClass) вызывать BaseClass.Finalize.

Вопрос 3 : Нет, вам не нужно иметь финализатор только потому, что вы реализуете IDisposable. Финализаторы должны использоваться только для неуправляемых ресурсов, которые больше ничего не собираются очищать - то есть тех, на которые у вас есть прямая ссылка. Например, предположим, у вас есть класс, который имеет FileStreamпеременную-член. Вы хотите реализовать, IDisposableчтобы вы могли закрыть поток как можно скорее, если вызывающий объект запомнил, но если он не запомнил вызов Dispose(), поток получит право на сборку мусора одновременно с вашим объектом. Доверьтесь тому, FileStreamу кого есть соответствующий финализатор (или ссылка на что-то еще с финализатором и т. Д.), А не пытайтесь очистить его в своем собственном финализаторе.

С .NET 2.0, с SafeHandle класса, он должен быть невероятно редким для вас нужны собственные финализаций.

16
11.12.2008 09:07:43
Правильно ли я полагаю, что нет необходимости вызывать GC.SuppressFinalize (this) в методе IDisposable.Dispose () класса ManagedResourceHolder (поскольку у него действительно нет финализатора для подавления)?
mherle 11.12.2008 10:17:39
Ага. Не нужно подавлять то, чего не существует.
Jon Skeet 11.12.2008 10:23:08
Использование финализатора в любом месте, кроме класса базового уровня, который либо запечатан, либо предназначен для финализации, мне кажется излишним, если единственная цель финализатора состоит в том, чтобы предупредить о сбое в правильной утилизации объекта. Если объект должен гарантировать, что что-то очищено, когда он выходит из области видимости, он должен инкапсулировать информацию и импульс, необходимые для проведения такой очистки, в небольшой выделенный класс. Только тот маленький класс должен будет остаться в живых во время финализации.
supercat 18.02.2011 23:08:38
  1. Нет, это не значит так. Только переопределенный Finalize()будет учитываться CLR.
  2. Наличие финализатора, как определено выше.
  3. Нет, это не всегда необходимо. Это просто хороший шаблон. Я имею в виду, никто не заставляет вас делать это. Но это хорошо, если у вас есть неуправляемые ресурсы, поскольку, если кто-то забудет их утилизировать, неуправляемый ресурс когда-нибудь будет освобожден. FxCop не применяет строгие правила. Он навязывает хорошие шаблоны, которые могут привести к неудаче в будущем, если вы не позаботитесь о них.

ОБНОВЛЕНИЕ: Каждый класс отвечает за управление своими ресурсами. Из-за особенностей абстракции и инкапсуляции объектно-ориентированных парадигм, потребитель класса не должен заботиться о том, какие ресурсы он имеет, косвенно. Следовательно, вы должны либо вручную высвободить ресурсы, которыми вы владеете (то, чем вы владеете, то, чем вы владеете непосредственно, когда вы смотрите на другие вещи как черный ящик ), или предоставить GC их освободить. Для неуправляемых ресурсов у вас нет возможности оставить его в GC, поэтому вы должны разблокировать его вручную. В этом смысле SafeHandle, о котором упоминал Джон, является управляемой абстракцией неуправляемого ресурса.поэтому его следует рассматривать как ценный управляемый ресурс (черный ящик, который управляет завершением работы самого неуправляемого ресурса).

0
11.12.2008 10:14:42
Это не очень хороший способ добавить финализатор, потому что вы IDisposable. Это дорогая ошибка. Есть много причин быть IDisposable, которые не указывают на использование финализатора.
Will Dean 11.12.2008 09:07:46
Я думаю, что упомянул «если у вас есть неуправляемые ресурсы» в ответе.
Mehrdad Afshari 11.12.2008 09:10:47
Да, но вы должны сделать это ясно , что это только тогда , когда вы непосредственно у неуправляемый ресурс. Например, если вы только что получили FileStream, дайте этому завершиться самому - вам не нужно. Вы должны очень редко, если вообще когда-либо, иметь свои собственные прямые ручки в эти дни. Используйте SafeHandle.
Jon Skeet 11.12.2008 09:23:25
Я не думаю, что управляемый класс - это неуправляемый ресурс, не так ли? Я считаю FileStream управляемым ресурсом, и мне все равно, что он содержит. Но когда я сам создаю что-то в неуправляемом мире, это определенно неуправляемый ресурс.
Mehrdad Afshari 11.12.2008 09:33:02
@ Mehrdad: Тогда я думаю, что вы должны уточнить свой ответ. Это вопрос прямого удержания неуправляемого ресурса против косвенного. Если у меня есть ссылка на FileStream, у меня косвенно есть неуправляемый ресурс, поэтому я должен вызвать для него Dispose.
Jon Skeet 11.12.2008 09:47:32

1: он действительно имеет значение (в полезном смысле), если он был переопределен

2: как определено 1, и GC.SuppressFinalize не был вызван (плюс перерегистрация и т. Д.)

3: конечно нет; на самом деле, если вы не работаете непосредственно с неуправляемым ресурсом, у вас не должно быть финализатора. Вы не должны добавлять финализатор только потому, что он является IDisposable - но вещи, которые имеют финализаторы, также обычно должны быть IDisposable.

3
11.12.2008 09:00:26

1) Да (в силу наследования)

2) Ничто не содержит ссылку на экземпляр класса (что делает его пригодным для завершения)

3) Да (почему нужно реализовывать IDisposable, если только он не требует от пользователя явного вызова dispose? Классы подключения в .net используют неуправляемый ресурс под капотом, и если вы не вызываете dispose для него, он будет висеть на нем. Поскольку время GC неизвестно, соединение будет оставаться открытым до этого времени)

Это моё понимание.

Я могу ошибаться. В этом случае эксперты исправят ситуацию для меня.

-2
11.12.2008 09:01:29
Вы ошибаетесь в третьем пункте. Если вы напрямую владеете неуправляемыми ресурсами, вам нужен финализатор, но если вы только что получили ссылку на что-то еще и у него есть финализатор, вы ничего не получите от его наличия. StreamWriter является хорошим примером этого.
Jon Skeet 11.12.2008 09:04:44
Джон, не могли бы вы объяснить выше на примере? что это означает, когда вы держите неуправляемые ресурсы напрямую (вы говорите о классе соединения в качестве примера)? Кроме того, что означает «если у вас есть ссылка на что-то ....»?
shahkalpesh 11.12.2008 09:09:22
Джон, я полностью не в своем понимании по всем пунктам?
shahkalpesh 11.12.2008 09:19:28
Посмотрите внизу моего ответа. По сути, если вы только что получили ссылку на другой объект (например, поток), вам не нужен финализатор. Если у вас есть IntPtr, который требует очистки, у вас должен быть финализатор.
Jon Skeet 11.12.2008 09:21:43
И на самом деле, для 1 и 2 ты тоже немного не в себе. У всего есть метод Finalize, но не все действительно имеют финализатор, поскольку это касается CLR. Только объекты, которые переопределяют Finalize, ставятся в очередь для завершения.
Jon Skeet 11.12.2008 09:22:27