Microsoft User Group Винница feedback Интересно о C#: какая разница между destructor и finalizer? - Alex Krakovetskiy blog - Microsoft User Group Винница

 

Интересно о C#: какая разница между destructor и finalizer?

Продолжаем рубрику "Интересно о C#". После не шуточного обсуждения этого вопроса считаю нужным расставить все точки по данному вопросу.

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

Термин "destructor" чаще всего используется в значении детерминировано (т.е. последовательно) вызываемой очистки (deterministically-invoked cleanup), в то время как "finalizer" исполняется тогда, когда получает команду от сборщика мусора (garbadge collector).

Значит ли это, что спецификация по C# использует понятие деструктора неправильно?

Да, в такой постановке спецификация называет деструктором то, что является финалайзером, а то, что мы называем методом "Dispose()", который вызывается в конструкции using, по сути, является деструктором. CLI спецификация использует понятие "финалайзер" правильно.

Почему авторы C# спецификации используют неправильное значение?

Есть две гипотезы.

Гипотеза #1. 12 мая 1999 года не было Википедии с описанием разницы между этими двумя понятиями. Возможно, это была простая ошибка по причине того, что эти два понятия имели одно и то же значение, а разница в значениях появилась позже для различения eager/deterministic и lazy/nondeterministic методов очистки.

Гипотеза #2. 12 мая 1999 года language design committee хотел оставить открытой возможность реализовывать C# "destructor" не в таком виде, как CLR finalizer. Т.е. "destructor" был разработан как концепт языка C#, который не обязательно должен являться точной копией концепта CLR "finalizer".

Комментарии комитета:

We're going to use the term "destructor" for the member which executes when an instance is reclaimed. Classes can have destructors; structs can't. Unlike in C++, a destructor cannot be called explicitly. Destruction is non-deterministic – you can't reliably know when the destructor will execute, except to say that it executes at some point after all references to the object have been released. The destructors in an inheritance chain are called in order, from most descendant to least descendant.  There is no need (and no way) for the derived class to explicitly call the base destructor. The C# compiler compiles destructors to the appropriate CLR representation.  For this version that probably means an instance finalizer that is distinguished in metadata. 

... что косвенно подтверждает вторую гипотезу.

Ссылки:

Update.

Интерфейс IDisposable имеет вид:

public interface IDisposable
{
   void Dispose();
}

и соответствующий класс:

public class MyClass : IDisposable
{
    public void Dispose()
    {
        // Perform any object clean up here.

        // If you are inheriting from another class that
        // also implements IDisposable, don't forget to
        // call base.Dispose() as well.
    }
}

Неявная очистка (Implicit cleanup)

Неявная очистка должна быть реализована везде, где это возможно, путем защиты ресурсов с помощью SafeHandle. В .NET 1.1 этого класса не существовало, поэтому раньше необходимо было реализовывать финализатор. В последних версиях .NET необходимость реализовывать финализаторы практически отпала. Если такая необходимость все же есть, то можно реализовать protected Finalize метод используя синтаксис используемого языка. Исполняемая среда (runtime) вызовет метод Finalize как часть процесса финализации GC.

Не нужно реализовывать финализаторы кроме крайних случаев. Процесс их написания сложный и сделает использование вашего класса более дорогостоящим (expensive) даже если он ни разу не выполнится.  Все объекты, которые реализуют финализаторы должны быть включены в список объектов финализации (finalization queue), который обслуживает GC. Поэтому если вам необходимо освободить ресурсы используйте Dispose метод, а не финализаторы. Если финализатор вам нужен, используйте его в дополнение методу Dispose, а не вместо него.

Синтаксические различия финализаторов и деструкторов:

Language Destructor Syntax Finalizer Syntax
C# public void Dispose() ~T()
C++ (.NET 2.0) ~T() !T()
C++ (.NET 1.0/1.1) public void Dispose() ~T()
Visual Basic (.NET) Public Sub Dispose() Implements IDisposable.Dispose Protected Overrides Sub Finalize()

Dispose Pattern

Без использования финализатора:

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}

C использованием финализатора:

public class ComplexCleanupBase : IDisposable
{
    // some fields that require cleanup
    private bool disposed = false; // to detect redundant calls

    public ComplexCleanupBase()
    {
        // allocate resources
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // dispose-only, i.e. non-finalizable logic
            }

            // shared cleanup logic
            disposed = true;
        }
    }

    ~ComplexCleanupBase()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class ComplexCleanupExtender : ComplexCleanupBase
{
    // some new fields that require cleanup
    private bool disposed = false; // to detect redundant calls

    public ComplexCleanupExtender() : base()
    {
        // allocate more resources (in addition to base’s)
    }

    protected override void Dispose(bool disposing)
    {
       if (!disposed)
       {
            if (disposing)
            {
                // dispose-only, i.e. non-finalizable logic
            }

            // new shared cleanup logic
            disposed = true;
        }

        base.Dispose(disposing);
    }
}

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


Поділитись


Related Posts with Thumbnails Posted фев 13 2010, 07:48 by Краковецкий А. View 1 617 6

Comments

progg.ru on 02-13-2010 8:41

Thank you for submitting this cool story - Trackback from progg.ru

Stanislav on 02-14-2010 3:21

Извините, но вам как-то не удалось все по полочкам расставить. Не понятно по прежнему ничего. Приведите примеры кода, что когда вызовется. Если MS разработал язык, мне кажется, он ДОЛЖЕН стараться донести до общественности нормально изложенную статью, в которой разжеваны все аспекты

Jasper on 02-14-2010 3:26

мне тоже кажется что автор не вдавался в детали - а просто обозначил какие то опять же спорные рамки для обазночения

Краковецкий А. on 02-14-2010 5:45

Обновил пост - добавил примеры и некоторые дополнительные объяснения.

smi on 02-15-2010 2:13

это лучше не читать(

Rom on 02-15-2010 9:10

хм .. завжди думав що фіналайз це Finalize() і цеж саме що деструктор ~.

в всякому випадку якщо проглянути IL код.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Please add 3 and 4 and type the answer here:

Enter captcha:

Информация

О нас
Timeline
Спонсоры
Поддержать

Разделы

Блоги
Медиа
Форумы
Вики
Презентации

Работа

Вакансии
Компании

Проекты

TechPosters
Data Mining SDK
Численные методы на C#
iPhoner
Data Extracting SDK

Контакты

msugvn@gmail.com
krakovetsky.alex
@msugvnua
ВКонтакте
LinkedIn
Facebook
INETA

Разработка логотипа: Helen

Статистика

Powered by Community Server (Non-Commercial Edition), by Telligent Systems