Утилизировать, когда это называется?

Рассмотрим следующий код:

namespace DisposeTest
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Calling Test");

            Test();

            Console.WriteLine("Call to Test done");
        }

        static void Test()
        {
            DisposeImplementation di = new DisposeImplementation();
        }
    }

    internal class DisposeImplementation : IDisposable
    {
        ~DisposeImplementation()
        {
            Console.WriteLine("~ in DisposeImplementation instance called");
        }
        public void Dispose()
        {
            Console.WriteLine("Dispose in DisposeImplementation instance called");
        }
    }
}

Dispose просто никогда не вызывается, даже если я поставлю цикл ожидания после Test(); призывание. Так что это довольно отстой. Я хочу написать класс, который будет простым и очень простым в использовании, чтобы обеспечить очистку каждого возможного ресурса. Я не хочу возлагать эту ответственность на пользователя моего класса.

Возможное решение: использовать using, или позвоните Распорядиться собой (в основном то же самое). Могу ли я заставить пользователя использовать использование? Или я могу заставить называться распоряжением?

призвание GC.Collect(); после Test(); тоже не работает.

Ввод di в null не вызывает Dispose либо. Деконструктор работает, поэтому объект получает деконструированный при выходе Test()

Хорошо, ребята, теперь понятно!

Спасибо всем за ваши ответы! Я добавлю предупреждение в комментарии!

7 ответов

Решение

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

Вы не можете сделать это. Управление памятью просто не построено для размещения ресурсов, которые не являются памятью конкретно.

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

Вы можете использовать Финализатор как запасной вариант для пользователей, которые не могут правильно расположить объекты, но он не работает как основной метод очистки объектов. Для бесперебойной работы объекты должны быть расположены должным образом, чтобы не требовалось вызывать более дорогостоящий финализатор.

Для решения вопроса ФП необходимо сделать пару важных замечаний:

  1. .NET GC недетерминирован (то есть вы никогда не знаете и не должны зависеть от того, когда это произойдет)
  2. Утилизация никогда не вызывается.NET Framework; Вы должны вызвать его вручную - желательно, завернув его создание в using() блок.
  3. Явная установка одноразового объекта в null без вызова Dispose() для него - плохая вещь. Что происходит, так это то, что вы явно устанавливаете для объектов "корневая ссылка" значение null. Это фактически означает, что вы не можете вызвать Dispose позже И, что более важно, он отправляет объект в очередь завершения GC для завершения. Причинение Финализации плохой практикой программирования следует избегать любой ценой.

Финализатор: Некоторые разработчики называют его деструктором. И фактически он даже называется деструктором в спецификации языка C# 4.0 (раздел 1.6.7.6) и в предыдущих версиях текущей спецификации ECMA-334. К счастью, 4-е издание (июнь 2006 г.) правильно определяет финализаторы в разделе 8.7.9 и пытается устранить путаницу между ними в разделе 17.12. Следует отметить, что существуют важные внутренние различия (не нужно вдаваться в эти кровавые подробности) между тем, что традиционно называется деструктором и деструктором / финализатором в.NET Framework.

  1. Если присутствует финализатор, он будет вызываться.NET Framework тогда и только тогда, когда GC.SuppressFinalize() не называется.
  2. Вы никогда не должны явно вызывать финализатор. К счастью, C# не разрешит этого явно (я не знаю о других языках); хотя это можно заставить позвонив GC.Collect(2) для 2-го поколения GC.

Завершение: Завершение - это способ.NET Framework справиться с "изящной" очисткой и освобождением ресурсов.

  1. Это происходит только тогда, когда в очереди завершения есть объекты.
  2. Это происходит только тогда, когда сборка мусора происходит для Gen2 (что составляет примерно 1 на каждые 100 сборок для хорошо написанного приложения.NET).
  3. До.NET 4 включительно существует один поток Финализации. Если этот поток заблокирован по какой-либо причине, ваше приложение испорчено.
  4. Написание правильного и безопасного кода завершения не является тривиальным, и ошибки могут быть сделаны довольно легко (то есть случайно допущены исключения из Finalizer, допускающие зависимости от других объектов, которые уже могут быть завершены, и т. Д.)

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

Все ответы (более или менее) правильны, вот пример:

static void Test()
{
    using (DisposeImplementation di = new DisposeImplementation())
    {
        // Do stuff with di
    }
}

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

Вы можете добавить финализатор, который обрабатывает удаление ресурсов на случай, если кто-то "забудет" использовать интерфейс IDisposable:

public class DisposeImplementation : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    ~DisposeImplementation()
    {
        Dispose(false);
    }
}

Смотрите этот вопрос для получения дополнительной информации. Тем не менее, это просто компенсирует людей, которые не используют ваш класс правильно:) Я предлагаю вам добавить большой жир Debug.Fail() позвоните в финализатор, чтобы предупредить разработчика об их ошибке.

Если вы решите реализовать шаблон, вы увидите, что GC.Collect() вызовет утилизацию.

Используйте это как шаблон / шаблон для ваших классов

public class MyClass : IDisposable
{
    private bool disposed = false;

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    // Dispose(bool disposing) executes in two distinct scenarios.
    // If disposing equals true, the method has been called directly
    // or indirectly by a user's code. Managed and unmanaged resources
    // can be disposed.
    // If disposing equals false, the method has been called by the
    // runtime from inside the finalizer and you should not reference
    // other objects. Only unmanaged resources can be disposed.
    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if (!this.disposed)
        {
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if (disposing)
            {
                // Dispose managed resources.                
                ......
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            ...........................

            // Note disposing has been done.
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    // This destructor will run only if the Dispose method
    // does not get called.
    // It gives your base class the opportunity to finalize.
    // Do not provide destructors in types derived from this class.
    ~MyClass()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}

И, конечно же, как уже упоминали другие, не забывайте о using(...){} блок.

Вам придется позвонить Dispose явно или путем оборачивания объекта в using заявление. Пример:

using (var di = new DisposeImplementation())
{
}

Возможное решение: использовать с помощью или вызвать Dispose себя (в основном то же самое).

С помощью using так же, как звонить Dispose внутри finally блок.

Утилизация не вызывается автоматически. Вам нужно использовать using пункт, чтобы обернуть использование или вызвать его вручную.

См. http://msdn.microsoft.com/en-us/library/aa664736%28VS.71%29.aspx

И просто чтобы вытеснить другую идею, которую вы можете иметь: вы не можете позвонить dispose от деструктора... Я пытался это некоторое время назад в проекте.

Вы должны избавиться от него сами, либо позвонив Dispose метод или использование using, Помните, это не деконструктор!

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

Другие вопросы по тегам