Как удалить управляемый ресурс в методе Dispose() в С#?

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

Однако при удалении объекта он подавляет завершение сборки сборщика мусора (GC.SuppressFinalize(this), в коде ниже). Это означает, что если объект включает управляемый ресурс, нам придется позаботиться об этом, потому что сборщик мусора не будет его очищать.

В приведенном ниже примере кода (из MSDN) "Компонент" является управляемым ресурсом, и мы вызываем команду dispose() для этого ресурса (component.Dispose()). Мой вопрос в том, как мы реализуем этот метод для класса Component, который является управляемым ресурсом? Должны ли мы использовать что-то вроде Collect(), чтобы вытащить сборщик мусора для очистки этой части?

Любая идея была бы оценена. Спасибо.

Ниже приведен код, который я просматриваю из MSDN:

using System;
using System.ComponentModel;

// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.

public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
    // Pointer to an external unmanaged resource.
    private IntPtr handle;
    // Other managed resource this class uses.
    private Component component = new Component();
    // Track whether Dispose has been called.
    private bool disposed = false;

    // The class constructor.
    public MyResource(IntPtr handle)
    {
        this.handle = handle;
    }

    // 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 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.
                component.Dispose();
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            CloseHandle(handle);
            handle = IntPtr.Zero;

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

        }
    }

    // Use interop to call the method necessary
    // to clean up the unmanaged resource.
    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);

    // 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.
    ~MyResource()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}
public static void Main()
{
    // Insert code here to create
    // and use the MyResource object.
}
}

Ответ 1

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

Это неверно. Сборщик мусора по-прежнему будет очищать ваши управляемые ресурсы. Финализаторы также предназначены для очистки неуправляемых ресурсов, поэтому вызов SuppressFinalize() не повредит вам.

И так как вы новичок в шаблоне IDisposable, я предвижу вашу следующую путаницу: писать финализаторы. В С# вы должны писать только финализатор, когда имеете дело с совершенно новым видом неуправляемого ресурса. Поэтому, если у вас есть, например, класс, который обертывает тип System.Data.SqlClient.SqlConnection как часть уровня доступа к данным, вы не должны писать финализатор для этого типа, потому что вы все еще имеете дело с одним и тем же базовым неуправляемый ресурс: соединения базы данных SQL Server. Финализатор для этого ресурса позаботится уже о базовом типе SqlConnection.

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

Ответ 2

Этот одноразовый шаблон запутанный. Здесь лучший способ реализовать его:

Шаг 1. Создайте одноразовый класс, чтобы инкапсулировать каждый неуправляемый ресурс, который у вас есть. Это должно быть очень редко, у большинства людей нет неуправляемых ресурсов для очистки. Этот класс только заботится (pdf) об его неуправляемом ресурсе и должен иметь финализатор. Реализация выглядит следующим образом:

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

  // ...

  IntPtr _nativeResource;

}

Шаг 2. Создайте одноразовый класс, если класс содержит другие одноразовые классы. Это простое внедрение, для этого вам не нужен финализатор. В методе Dispose просто вызовите Dispose на другие расходные материалы. В этом случае вы не заботитесь о неуправляемых ресурсах:

public class ManagedDisposable : IDisposable {

  // ...

  public virtual void Dispose() {
    _otherDisposable.Dispose();
  }

  IDisposable _otherDisposable;

}

"Компонент" в примере будет либо одним из них, в зависимости от того, инкапсулирует ли он неуправляемый ресурс, либо просто состоит из других доступных ресурсов.

Также обратите внимание, что подавление финализации не означает, что вы подавляете сборщик мусора от очистки вашего экземпляра; это просто означает, что, когда сборщик мусора работает в вашем экземпляре, он не будет вызывать финализатор, который определен для него.

Ответ 3

Может быть, немного яснее. GC.SuppressFinalize(this) влияет только на объект, на который ссылается этот указатель, но не на каких-либо членов объекта. То есть SuppressFinalize не рекурсивно применяется к членам объекта. Когда сборщик мусора восстанавливает память для объекта Disposed, скорее всего, не будет активных ссылок на поля объектов. Поскольку вы не вызывали GC.SuppressFinalize во всех полях объекта, то сборщик мусора будет вызывать метод finalize для этих объектов, если они существуют. Когда это происходит, это полностью зависит от времени выполнения, и в целом вы должны просто позволить этому сделать свое дело.

Ответ 4

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