Реализация метода Dispose
Метод Dispose в основном реализуется для освобождения неуправляемых ресурсов. При работе с членами экземпляра, которые являются реализациями IDisposable, обычно применяются каскадные вызовы Dispose. Существуют и другие причины для реализации Dispose, например освобождение выделенной памяти, удаление элемента, добавленного в коллекцию, или сигнал о снятии блокировки, которая была получена.
Сборщик мусора .NET не выделяет и не освобождает неуправляемую память. Шаблон освобождения объекта налагает определенные правила на время существования объекта. Шаблон удаления используется для объектов, реализующих IDisposable интерфейс . Этот шаблон распространен при взаимодействии с дескрипторами файлов и конвейеров, дескрипторами реестра, дескрипторами ожидания или указателями на блоки неуправляемой памяти, так как сборщик мусора не может освободить неуправляемые объекты.
Чтобы обеспечить надлежащую очистку ресурсов, Dispose метод должен быть идемпотентным, чтобы его можно было вызывать несколько раз, не вызывая исключение. Кроме того, последующие вызовы Dispose не должны выполнять никаких действий.
В примере кода, предоставленном для метода , GC.KeepAlive показано, как сборка мусора может привести к запуску метода завершения, пока еще используется неуправляемая ссылка на объект или его члены. Возможно, имеет смысл использовать GC.KeepAlive, чтобы запретить сборку мусора для объекта с момента начала текущей процедуры до вызова этого метода.
Что касается внедрения зависимостей, то при регистрации служб в IServiceCollectionслужбах время существования службы управляется неявно от вашего имени. И IServiceProvider соответствующие IHost функции оркестрации очистки ресурсов. В частности, реализации IDisposable и IAsyncDisposable должным образом удаляются в конце указанного времени существования.
Дополнительные сведения см. в статье Внедрение зависимостей в .NET.
Безопасные дескрипторы
Написание кода для метода завершения объекта является сложной задачей, которая может вызвать проблемы при неправильном выполнении. Поэтому вместо реализации метода завершения рекомендуется создавать объекты System.Runtime.InteropServices.SafeHandle.
System.Runtime.InteropServices.SafeHandle — это абстрактный управляемый тип, выполняющий роль оболочки для System.IntPtr, который идентифицирует неуправляемый ресурс. В Windows он может идентифицировать дескриптор, а в Unix — дескриптор файла. предоставляет SafeHandle всю логику, необходимую для обеспечения того, чтобы этот ресурс был освобожден один раз и только один раз, когда SafeHandle удаляется или когда все ссылки на SafeHandle были удалены и SafeHandle экземпляр завершен.
System.Runtime.InteropServices.SafeHandle — это абстрактный базовый класс. Производные классы предоставляют определенные экземпляры для различных видов дескрипторов. Эти производные классы проверяют, какие значения System.IntPtr считаются недопустимыми и как фактически освободить дескриптор. Например, класс SafeFileHandle является производным от SafeHandle , выступает оболочкой для структур IntPtrs , которые определяют открытые дескрипторы файлов, а также переопределяет свой метод SafeHandle.ReleaseHandle() для его закрытия (через функцию close в UNIX или CloseHandle в Windows). Большинство API-интерфейсов в библиотеках .NET, создающих неуправляемый SafeHandle ресурс, заключает его в и при необходимости возвращает SafeHandle его вам, а не возвращает необработанный указатель. В ситуациях, когда вы взаимодействуете с неуправляемым компонентом и получаете структуру IntPtr для неуправляемого ресурса, можно создать собственный тип SafeHandle в качестве оболочки структуры. В результате для реализации методов завершения требуется несколько типов, отличных SafeHandle от типов. Большинство реализаций одноразовых шаблонов в конечном итоге только упаковывают другие управляемые ресурсы, некоторые из которых могут быть объектами SafeHandle .
Следующие производные Microsoft.Win32.SafeHandles классы в пространстве имен предоставляют безопасные дескрипторы.
| Класс | Ресурсы, которые она хранит |
|---|---|
| SafeFileHandle SafeMemoryMappedFileHandle SafePipeHandle |
Файлы, сопоставленные в памяти файлы и каналы |
| SafeMemoryMappedViewHandle | представления памяти; |
| SafeNCryptKeyHandle SafeNCryptProviderHandle SafeNCryptSecretHandle |
конструкции шифрования; |
| SafeRegistryHandle | Разделы реестра |
| SafeWaitHandle | дескрипторы ожидания. |
Dispose() и Dispose(bool)
Интерфейс IDisposable требует реализации одного метода Dispose без параметров. Кроме того, любой непечатанный класс должен иметь метод перегрузки Dispose(bool) .
- public non-virtual ( NotOverridable в Visual Basic) (IDisposable.Dispose реализация).
- protected virtual ( Overridable в Visual Basic) Dispose(bool) .
Метод Dispose()
public Так как метод , не виртуальный ( NotOverridable в Visual Basic), метод без Dispose параметров вызывается, когда он больше не нужен (потребителю типа), его целью является освобождение неуправляемых ресурсов, выполнение общей очистки и указание на то, что метод завершения, если он существует, не должен выполняться. Освобождение физической памяти, связанной с управляемым объектом, всегда оставляется сборщику мусора. Он имеет стандартную реализацию:
public void Dispose() < // Dispose of unmanaged resources. Dispose(true); // Suppress finalization. GC.SuppressFinalize(this); >
Public Sub Dispose() _ Implements IDisposable.Dispose ' Dispose of unmanaged resources. Dispose(True) ' Suppress finalization. GC.SuppressFinalize(Me) End Sub
Метод Dispose полностью выполняет очистку объектов, поэтому сборщику мусора не требуется вызывать переопределенный метод Object.Finalize. Таким образом, вызов метода SuppressFinalize не позволит сборщику мусора запустить метод завершения. Если тип не имеет метода завершения, вызов метода GC.SuppressFinalize не производит эффекта. Фактическая очистка выполняется перегрузкой Dispose(bool) метода.
Перегрузка метода Dispose(Boolean)
В этой перегрузке параметр disposing типа Boolean указывает, откуда осуществляется вызов метода: из метода Dispose (значение true ) или из метода завершения (значение false ).
protected virtual void Dispose(bool disposing) < if (_disposed) < return; >if (disposing) < // TODO: dispose managed state (managed objects). >// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. // TODO: set large fields to null. _disposed = true; >
Protected Overridable Sub Dispose(disposing As Boolean) If disposed Then Exit Sub ' A block that frees unmanaged resources. If disposing Then ' Deterministic call… ' A conditional block that frees managed resources. End If disposed = True End Sub
Параметр disposing при вызове из метода завершения должен иметь значение false , а при вызове из метода IDisposable.Dispose — значение true . Иными словами, при детерминированном вызове он будет иметь значение true , а при недетерминированном вызове — false .
Тело метода состоит из трех блоков кода:
- Блок для условного возврата, если объект уже удален.
- Блок, который освобождает неуправляемые ресурсы. Этот блок выполняется вне зависимости от значения параметра disposing .
- Условный блок, который освобождает управляемые ресурсы. Этот блок выполняется, если параметр disposing имеет значение true . К управляемым ресурсам, которые он освобождает, могут относиться:
- Управляемые объекты, реализующие IDisposable. Условный блок может использоваться для вызова реализации Dispose (каскадное удаление). При использовании класса, производного от System.Runtime.InteropServices.SafeHandle, в качестве оболочки для неуправляемого ресурса необходимо вызвать реализацию SafeHandle.Dispose().
- Управляемые объекты, которые используют большие объемы памяти или дефицитные ресурсы. Назначайте ссылки на большие управляемые объекты в null , чтобы они чаще оказывались недоступными. Это освобождает их быстрее, чем если бы они были освобождены недетерминированным образом.
Если метод вызывается из метода завершения, должен выполняться только тот код, который освобождает неуправляемые ресурсы. Разработчик отвечает за то, чтобы ложный путь не взаимодействовал с управляемыми объектами, которые могли быть удалены. Это важно, так как порядок, в котором сборщик мусора удаляет управляемые объекты во время завершения, не детерминирован.
Каскадные вызовы Dispose
Если класс владеет полем или свойством и его тип реализует IDisposable, содержащий класс должен также реализовывать IDisposable. Класс, который создает IDisposable экземпляр реализации и сохраняет ее в качестве члена экземпляра, также отвечает за ее очистку. Это помогает гарантировать, что ссылочные удаляемые типы получают возможность детерминированного выполнения очистки Dispose с помощью метода . В следующем примере используется sealed класс (или NotInheritable в Visual Basic).
using System; public sealed class Foo : IDisposable < private readonly IDisposable _bar; public Foo() < _bar = new Bar(); >public void Dispose() => _bar.Dispose(); >Public NotInheritable Class Foo Implements IDisposable Private ReadOnly _bar As IDisposable Public Sub New() _bar = New Bar() End Sub Public Sub Dispose() Implements IDisposable.Dispose _bar.Dispose() End Sub End Class- Если класс имеет IDisposable поле или свойство, но не владеет им, то есть класс не создает объект , то классу не нужно реализовывать IDisposable.
- Бывают случаи, когда может потребоваться выполнить null проверку в методе завершения (который включает Dispose(false) метод, вызываемый методом завершения). Одна из основных причин заключается в том, что вы не уверены, был ли экземпляр полностью инициализирован (например, в конструкторе может быть вызвано исключение).
Реализация шаблона освобождения
Все непечатанные классы (или классы Visual Basic, не измененные как ) следует рассматривать как NotInheritable потенциальные базовые классы, так как они могут быть унаследованы. При реализации шаблона освобождения для любого класса, который может быть базовым, необходимо обеспечить следующее:
- Реализация Dispose, которая вызывает метод Dispose(bool) .
- Метод Dispose(bool) , который выполняет фактическую очистку.
- Любой класс, производный от класса SafeHandle, который создает оболочку для неуправляемого ресурс (рекомендуется), или переопределенный метод Object.Finalize. Класс SafeHandle предоставляет метод завершения, поэтому вам не нужно писать его самостоятельно.
Базовый класс может ссылаться только на управляемые объекты и реализовывать шаблон удаления. В таких случаях метод завершения не нужен. Метод завершения нужен только в том случае, если используются прямые ссылки на неуправляемые ресурсы.
Ниже приведен общий пример реализации шаблона удаления для базового класса, использующего безопасный дескриптор.
using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; public class BaseClassWithSafeHandle : IDisposable < // To detect redundant calls private bool _disposedValue; // Instantiate a SafeHandle instance. private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true); // Public implementation of Dispose pattern callable by consumers. public void Dispose() < Dispose(true); GC.SuppressFinalize(this); >// Protected implementation of Dispose pattern. protected virtual void Dispose(bool disposing) < if (!_disposedValue) < if (disposing) < _safeHandle?.Dispose(); _safeHandle = null; >_disposedValue = true; > > >Imports Microsoft.Win32.SafeHandles Imports System.Runtime.InteropServices Public Class BaseClassWithSafeHandle Implements IDisposable ' To detect redundant calls Private _disposedValue As Boolean ' Instantiate a SafeHandle instance. Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True) ' Public implementation of Dispose pattern callable by consumers. Public Sub Dispose() _ Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub ' Protected implementation of Dispose pattern. Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not _disposedValue Then If disposing Then _safeHandle?.Dispose() _safeHandle = Nothing End If _disposedValue = True End If End Sub End ClassВ предыдущем примере используется объект SafeFileHandle для иллюстрации шаблона. Вместо него может использоваться любой объект, производный от SafeHandle. Обратите внимание, что в этом примере неправильно создаются экземпляры объекта SafeFileHandle.
Вот общий шаблон реализации шаблона удаления для базового класса, который переопределяет метод Object.Finalize.
using System; public class BaseClassWithFinalizer : IDisposable < // To detect redundant calls private bool _disposedValue; ~BaseClassWithFinalizer() =>Dispose(false); // Public implementation of Dispose pattern callable by consumers. public void Dispose() < Dispose(true); GC.SuppressFinalize(this); >// Protected implementation of Dispose pattern. protected virtual void Dispose(bool disposing) < if (!_disposedValue) < if (disposing) < // TODO: dispose managed state (managed objects) >// TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: set large fields to null _disposedValue = true; > > >Public Class BaseClassWithFinalizer Implements IDisposable ' To detect redundant calls Private _disposedValue As Boolean Protected Overrides Sub Finalize() Dispose(False) End Sub ' Public implementation of Dispose pattern callable by consumers. Public Sub Dispose() _ Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub ' Protected implementation of Dispose pattern. Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not _disposedValue Then If disposing Then ' TODO: dispose managed state (managed objects) End If ' TODO free unmanaged resources (unmanaged objects) And override finalizer ' TODO: set large fields to null _disposedValue = True End If End Sub End ClassВ C# вы реализуете завершение путем предоставления метода завершения, а не путем переопределения Object.Finalize. В Visual Basic вы создаете метод завершения с Protected Overrides Sub Finalize() помощью .
Реализация шаблона освобождения для производного класса
Класс, производный от класса, реализующего интерфейс IDisposable, не должен реализовывать интерфейс IDisposable, поскольку реализация метода IDisposable.Dispose базового класса наследуется производными классами. Вместо этого для очистки производного класса необходимо указать следующее:
- Метод protected override void Dispose(bool) , который переопределяет метод базового класса и выполняет фактическую очистку производного класса. Этот метод также должен вызывать base.Dispose(bool) метод ( MyBase.Dispose(bool) в Visual Basic), передавая ему состояние удаления ( bool disposing параметр) в качестве аргумента.
- Любой класс, производный от класса SafeHandle, который создает оболочку для неуправляемого ресурс (рекомендуется), или переопределенный метод Object.Finalize. Класс SafeHandle содержит метод завершения, что освобождает разработчика от необходимости создавать его вручную. Если вы предоставляете метод завершения, он должен вызвать перегрузку Dispose(bool) с false аргументом .
Ниже приведен пример общего шаблона реализации шаблона удаления для производного класса, использующего безопасный дескриптор:
using Microsoft.Win32.SafeHandles; using System; using System.Runtime.InteropServices; public class DerivedClassWithSafeHandle : BaseClassWithSafeHandle < // To detect redundant calls private bool _disposedValue; // Instantiate a SafeHandle instance. private SafeHandle? _safeHandle = new SafeFileHandle(IntPtr.Zero, true); // Protected implementation of Dispose pattern. protected override void Dispose(bool disposing) < if (!_disposedValue) < if (disposing) < _safeHandle?.Dispose(); _safeHandle = null; >_disposedValue = true; > // Call base class implementation. base.Dispose(disposing); > >Imports Microsoft.Win32.SafeHandles Imports System.Runtime.InteropServices Public Class DerivedClassWithSafeHandle Inherits BaseClassWithSafeHandle ' To detect redundant calls Private _disposedValue As Boolean ' Instantiate a SafeHandle instance. Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True) Protected Overrides Sub Dispose(ByVal disposing As Boolean) If Not _disposedValue Then If disposing Then _safeHandle?.Dispose() _safeHandle = Nothing End If _disposedValue = True End If ' Call base class implementation. MyBase.Dispose(disposing) End Sub End ClassВ предыдущем примере используется объект SafeFileHandle для иллюстрации шаблона. Вместо него может использоваться любой объект, производный от SafeHandle. Обратите внимание, что в этом примере неправильно создаются экземпляры объекта SafeFileHandle.
Вот общий шаблон реализации шаблона удаления для производного класса, который переопределяет метод Object.Finalize:
public class DerivedClassWithFinalizer : BaseClassWithFinalizer < // To detect redundant calls private bool _disposedValue; ~DerivedClassWithFinalizer() =>Dispose(false); // Protected implementation of Dispose pattern. protected override void Dispose(bool disposing) < if (!_disposedValue) < if (disposing) < // TODO: dispose managed state (managed objects). >// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. // TODO: set large fields to null. _disposedValue = true; > // Call the base class implementation. base.Dispose(disposing); > >Public Class DerivedClassWithFinalizer Inherits BaseClassWithFinalizer ' To detect redundant calls Private _disposedValue As Boolean Protected Overrides Sub Finalize() Dispose(False) End Sub ' Protected implementation of Dispose pattern. Protected Overrides Sub Dispose(ByVal disposing As Boolean) If Not _disposedValue Then If disposing Then ' TODO: dispose managed state (managed objects). End If ' TODO free unmanaged resources (unmanaged objects) And override a finalizer below. ' TODO: set large fields to null. _disposedValue = True End If ' Call the base class implementation. MyBase.Dispose(disposing) End Sub End ClassСм. также раздел
- Удаление служб
- SuppressFinalize
- IDisposable
- IDisposable.Dispose
- Microsoft.Win32.SafeHandles
- System.Runtime.InteropServices.SafeHandle
- Object.Finalize
- Определение и использование классов и структур (C++/CLI)
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Dispose c что делает
Большинство объектов, используемых в программах на C#, относятся к управляемым или managed-коду. Такие объекты управляются CLR и легко очищаются сборщиком мусора. Однако вместе с тем встречаются также и такие объекты, которые задействуют неуправляемые объекты (подключения к файлам, базам данных, сетевые подключения и т.д.). Такие неуправляемые объекты обращаются к API операционной системы. Сборщик мусора может справиться с управляемыми объектами, однако он не знает, как удалять неуправляемые объекты. В этом случае разработчик должен сам реализовывать механизмы очистки на уровне программного кода.
Освобождение неуправляемых ресурсов подразумевает реализацию одного из двух механизмов:
- Создание деструктора
- Реализация классом интерфейса System.IDisposable
Создание деструкторов
Если вы вдруг программировали на языке C++, то наверное уже знакомы с концепцией деструкторов. Метод деструктора носит имя класса (как и конструктор), перед которым стоит знак тильды ( ~ ).
Деструкторы можно определить только в классах. Деструктор в отличие от конструктора не может иметь модификаторов доступа и параметры. При этом каждый класс может иметь только один деструктор.
Например, определим в классе Person простейший деструктор:
class Person < public string Name < get;>public Person(string name) => Name = name; ~Person() < Console.WriteLine($"has deleted"); > >
В данном случае в деструкторе в целях демонстрации просто выводится строка на консоль, которая уведомляет, что объект удален. Но в реальных программах в деструктор вкладывается логика освобождения неуправляемых ресурсов.
Однако на деле при очистке сборщик мусора вызывает не деструктор, а метод Finalize . Все потому, что компилятор C# компилирует деструктор в конструкцию, которая эквивалентна следующей:
protected override void Finalize() < try < // здесь идут инструкции деструктора >finally < base.Finalize(); >>
Метод Finalize уже определен в базовом для всех типов классе Object, однако данный метод нельзя так просто переопределить. И фактическая его реализация происходит через создание деструктора.
Используя в программе класс Person, после ее завершения можно будет увидеть на консоли сообщение об удалении объекта tom:
Test(); GC.Collect(); // очистка памяти под объект tom Console.Read(); // ставим задержку void Test() < Person tom = new Person("Tom"); >public class Person < public string Name < get;>public Person(string name) => Name = name; ~Person() < Console.WriteLine($"has been deleted"); > >Обратите внимание, что даже после завершения метода Test и соответственно удаления из стека ссылки на объект Person в куче, может не последовать немедленного вызова деструктора. Лишь при завершении всей программы гарантировано произойдет очистка памяти. Однако с .NET 5 и в последующих версиях при завершении программы деструкторы не вызываются. Поэтому в программе выше для более быстрой очистки памяти применяется метод GC.Collect и для гарантированного вызова деструктора устанавливается задержка с помощью вызова Console.Read() , который ожидает от пользователя ввода.
На уровне памяти это выглядит так: сборщик мусора при размещении объекта в куче определяет, поддерживает ли данный объект метод Finalize . И если объект имеет метод Finalize, то указатель на него сохраняется в специальной таблице, которая называется очередь финализации. Когда наступает момент сборки мусора, сборщик видит, что данный объект должен быть уничтожен, и если он имеет метод Finalize, то он копируется в еще одну таблицу и окончательно уничтожается лишь при следующем проходе сборщика мусора.
Стоит отметить, что точное время вызова деструктора не определено. Кроме того, при финализации двух связанных объектов порядок вызова деструкторов не гарантируется. То есть если объект A хранит ссылку на объект B, и при этом оба эти объекта имеют деструкторы, то для объекта B деструктор моет уже отработать в то время, как для объекта A деструктор только начнет работу.
И здесь мы можем столкнуться со следующей проблемой: а что если нам немедленно надо вызвать деструктор и освободить все связанные с объектом неуправляемые ресурсы? В этом случае мы можем использовать второй подход — реализацию интерфейса IDisposable.
Интерфейс IDisposable
Интерфейс IDisposable объявляет один единственный метод Dispose , в котором при реализации интерфейса в классе должно происходить освобождение неуправляемых ресурсов. Например:
Test(); void Test() < Person? tom = null; try < tom = new Person("Tom"); >finally < tom?.Dispose(); >> public class Person : IDisposable < public string Name < get;>public Person(string name) => Name = name; public void Dispose() < Console.WriteLine($"has been disposed"); > >В данном коде используется конструкция try. finally. По сути эта конструкция по функционалу в общем эквивалентна следующим двум строкам кода:
Person tom = new Person("Tom"); tom.Dispose();Но конструкцию try. finally предпочтительнее использовать при вызове метода Dispose, так как она гарантирует, что даже в случае возникновения исключения произойдет освобождение ресурсов в методе Dispose.
Комбинирование подходов
Мы рассмотрели два подхода. Какой же из них лучше? С одной стороны, метод Dispose позволяет в любой момент времени вызвать освобождение связанных ресурсов, а с другой — программист, использующий наш класс, может забыть поставить в коде вызов метода Dispose. В общем бывают различные ситуации. И чтобы сочетать плюсы обоих подходов мы можем использовать комбинированный подход. Microsoft предлагает нам использовать следующий формализованный шаблон:
public class SomeClass: IDisposable < private bool disposed = false; // реализация интерфейса IDisposable. public void Dispose() < // освобождаем неуправляемые ресурсы Dispose(true); // подавляем финализацию GC.SuppressFinalize(this); >protected virtual void Dispose(bool disposing) < if (disposed) return; if (disposing) < // Освобождаем управляемые ресурсы >// освобождаем неуправляемые объекты disposed = true; > // Деструктор ~SomeClass() < Dispose (false); >>
Логика очистки реализуется перегруженной версией метода Dispose(bool disposing) . Если параметр disposing имеет значение true, то данный метод вызывается из публичного метода Dispose, если false — то из деструктора.
При вызове деструктора в качестве параметра disposing передается значение false, чтобы избежать очистки управляемых ресурсов, так как мы не можем быть уверенными в их состоянии, что они до сих пор находятся в памяти. И в этом случае остается полагаться на деструкторы этих ресурсов. Ну и в обоих случаях освобождаются неуправляемые ресурсы.
Еще один важный момент — вызов в методе Dispose метода GC.SuppressFinalize(this) . GC.SuppressFinalize не позволяет системе выполнить метод Finalize для данного объекта. Если же в классе деструктор не определен, то вызов этого метода не будет иметь никакого эффекта.
Таким образом, даже если разработчик не использует в программе метод Dispose, все равно произойдет очистка и освобождение ресурсов.
Общие рекомендации по использованию Finalize и Dispose
- Деструктор следует реализовывать только у тех объектов, которым он действительно необходим, так как метод Finalize оказывает сильное влияние на производительность
- После вызова метода Dispose необходимо блокировать у объекта вызов метода Finalize с помощью GC.SuppressFinalize
- При создании производных классов от базовых, которые реализуют интерфейс IDisposable, следует также вызывать метод Dispose базового класса:
public class Derived: Base < private bool IsDisposed = false; protected override void Dispose(bool disposing) < if (IsDisposed) return; if (disposing) < // Освобождение управляемых ресурсов >IsDisposed = true; // Обращение к методу Dispose базового класса base.Dispose(disposing); > >
IDisposable. Dispose Метод
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Выполняет определяемые приложением задачи, связанные с удалением, высвобождением или сбросом неуправляемых ресурсов.
public: void Dispose();public void Dispose ();abstract member Dispose : unit -> unitPublic Sub Dispose ()Примеры
В следующем примере показано, как можно реализовать Dispose метод .
#using #using using namespace System; using namespace System::ComponentModel; using namespace System::Windows::Forms; // The following example demonstrates how to create a class that // implements the IDisposable interface and the IDisposable.Dispose // method with finalization to clean up unmanaged resources. // public ref class MyResource: public IDisposable < private: // Pointer to an external unmanaged resource. IntPtr handle; // A managed resource this class uses. Component^ component; // Track whether Dispose has been called. bool disposed; public: // The class constructor. MyResource( IntPtr handle, Component^ component ) < this->handle = handle; this->component = component; disposed = false; > // This method is called if the user explicitly disposes of the // object (by calling the Dispose method in other managed languages, // or the destructor in C++). The compiler emits as a call to // GC::SuppressFinalize( this ) for you, so there is no need to // call it here. ~MyResource() < // Dispose of managed resources. component->~Component(); // Call C++ finalizer to clean up unmanaged resources. this->!MyResource(); // Mark the class as disposed. This flag allows you to throw an // exception if a disposed object is accessed. disposed = true; > // Use interop to call the method necessary to clean up the // unmanaged resource. // [System::Runtime::InteropServices::DllImport("Kernel32")] static Boolean CloseHandle( IntPtr handle ); // The C++ finalizer destructor ensures that unmanaged resources get // released if the user releases the object without explicitly // disposing of it. // !MyResource() < // Call the appropriate methods to clean up unmanaged // resources here. If disposing is false when Dispose(bool, // disposing) is called, only the following code is executed. CloseHandle( handle ); handle = IntPtr::Zero; >>; void main() < // Insert code here to create and use the MyResource object. MyResource^ mr = gcnew MyResource((IntPtr) 42, (Component^) gcnew Button()); mr->~MyResource(); >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(disposing: true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize 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. protected virtual 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# finalizer syntax for finalization code. // This finalizer will run only if the Dispose method // does not get called. // It gives your base class the opportunity to finalize. // Do not provide finalizer in types derived from this class. ~MyResource() < // Do not re-create Dispose clean-up code here. // Calling Dispose(disposing: false) is optimal in terms of // readability and maintainability. Dispose(disposing: false); >> public static void Main() < // Insert code here to create // and use the MyResource object. >>// The following example demonstrates how to create // a resource class that implements the IDisposable interface // and the IDisposable.Dispose method. open System open System.ComponentModel open System.Runtime.InteropServices // Use interop to call the method necessary // to clean up the unmanaged resource. [] extern Boolean CloseHandle(nativeint handle) // A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. type MyResource(handle: nativeint) = // Pointer to an external unmanaged resource. let mutable handle = handle // Other managed resource this class uses. let comp = new Component() // Track whether Dispose has been called. let mutable disposed = false // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. interface IDisposable with member this.Dispose() = this.Dispose true // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize 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. abstract Dispose: bool -> unit override _.Dispose(disposing) = // Check to see if Dispose has already been called. if not disposed then // If disposing equals true, dispose all managed // and unmanaged resources. if disposing then // Dispose managed resources. comp.Dispose() // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. CloseHandle handle |> ignore handleImports 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 class that implements IDisposable. ' By implementing IDisposable, you are announcing that ' instances of this type allocate scarce resources. Public Class MyResource Implements IDisposable ' Pointer to an external unmanaged resource. Private handle As IntPtr ' Other managed resource this class uses. Private component As component ' Track whether Dispose has been called. Private disposed As Boolean = False ' The class constructor. Public Sub New(ByVal handle As IntPtr) Me.handle = handle End Sub ' Implement IDisposable. ' Do not make this method virtual. ' A derived class should not be able to override this method. Public Overloads Sub Dispose() Implements IDisposable.Dispose Dispose(disposing:=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(Me) End Sub ' 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. Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean) ' Check to see if Dispose has already been called. If Not Me.disposed Then ' If disposing equals true, dispose all managed ' and unmanaged resources. If disposing Then ' Dispose managed resources. component.Dispose() End If ' 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 End If End Sub ' Use interop to call the method necessary ' to clean up the unmanaged resource. _ Private Shared Function CloseHandle(ByVal handle As IntPtr) As [Boolean] End Function ' This finalizer will run only if the Dispose method ' does not get called. ' It gives your base class the opportunity to finalize. ' Do not provide finalize methods in types derived from this class. Protected Overrides Sub Finalize() ' Do not re-create Dispose clean-up code here. ' Calling Dispose(disposing:=False) is optimal in terms of ' readability and maintainability. Dispose(disposing:=False) MyBase.Finalize() End Sub End Class Public Shared Sub Main() ' Insert code here to create ' and use the MyResource object. End Sub End ClassКомментарии
Используйте этот метод для закрытия или освобождения неуправляемых ресурсов, таких как файлы, потоки и дескрипторы, хранящиеся в экземпляре класса , реализующего этот интерфейс. По соглашению этот метод используется для всех задач, связанных с освобождением ресурсов, удерживаемых объектом, или подготовкой объекта к повторному использованию.
Если вы используете класс, реализующий IDisposable интерфейс , следует вызвать его Dispose реализацию по завершении работы с классом . Дополнительные сведения см. в разделе "Использование объекта, реализующего IDisposable" этой статьи IDisposable .
При реализации этого метода убедитесь, что все удерживаемые ресурсы освобождены, распространяя вызов через иерархию вложенности. Например, если объект A выделяет объект B, а объект B выделяет объект C, то реализация A Dispose должна вызывать Dispose объект B, который, в свою очередь, должен вызывать Dispose для C.
Компилятор C++ поддерживает детерминированное удаление ресурсов и не допускает прямую реализацию Dispose метода .
Объект также должен вызывать Dispose метод своего базового класса, если базовый класс реализует IDisposable. Дополнительные сведения о реализации IDisposable в базовом классе и его подклассах см. в разделе "IDisposable и иерархия наследования" этой статьи IDisposable .
Если метод объекта Dispose вызывается несколько раз, объект должен игнорировать все вызовы после первого. Объект не должен вызывать исключение, если его Dispose метод вызывается несколько раз. Методы экземпляра, отличные от Dispose , могут вызывать исключение , ObjectDisposedException если ресурсы уже удалены.
Пользователи могут ожидать, что тип ресурса будет использовать определенное соглашение для обозначения выделенного и освобожденного состояния. Примером этого являются классы потоков, которые традиционно считаются открытыми или закрытыми. Разработчик класса с таким соглашением может реализовать открытый метод с настраиваемым именем, например Close , который вызывает Dispose метод .
Dispose Так как метод должен вызываться явным образом, всегда существует опасность того, что неуправляемые ресурсы не будут освобождены, так как потребитель объекта не может вызвать его Dispose метод. Этого можно избежать двумя способами:
- Заключите управляемый ресурс в объект, производный от System.Runtime.InteropServices.SafeHandle. Затем Dispose реализация вызывает Dispose метод System.Runtime.InteropServices.SafeHandle экземпляров . Дополнительные сведения см. в разделе "Альтернатива SafeHandle" этой статьи Object.Finalize .
- Реализуйте метод завершения для освобождения ресурсов, если Dispose не вызывается . По умолчанию сборщик мусора автоматически вызывает метод завершения объекта, прежде чем освободить его память. Однако если Dispose был вызван метод , сборщик мусора обычно не требуется вызывать метод завершения удаленного объекта. Чтобы предотвратить автоматическое завершение, Dispose реализации могут вызывать GC.SuppressFinalize метод .
При использовании объекта, который обращается к неуправляемым ресурсам, например StreamWriter, рекомендуется создать экземпляр с помощью инструкции using . Оператор using автоматически закрывает поток и вызывает Dispose объект после завершения кода, который ее использует. Пример см. в StreamWriter разделе класс .
Применяется к
См. также раздел
Правильная реализация метода Dispose
Есть класс А, у него 4 текстовых поля А1,А2,А3,А4. Я хочу реализовать метод Dispose для класса, т.к. в его полях находится большой текст, а используется он всего 1 раз. Скажите, будет ли достаточно в моем случае, если сделаю так?
Dispose()
Или может нужно просто сделать А1 = ""?
Отслеживать
68k 221 221 золотой знак 79 79 серебряных знаков 221 221 бронзовый знак
задан 18 янв 2016 в 14:47
Leonard Bertone Leonard Bertone
2,066 4 4 золотых знака 28 28 серебряных знаков 40 40 бронзовых знаковНе надо. Dispose предназначен не для помощи сборщику мусора, а для освобождения внешних (unmanaged) ресурсов.
– user176262
18 янв 2016 в 14:48
Т.е. мне вообще никак освобождать ресурсы самостоятельно?
18 янв 2016 в 14:49
Какие ресурсы? "Давайте договоримся об определениях и спор разрешится сам собой."
– user176262
18 янв 2016 в 14:52@LeonardBertone в С# есть сборщик мусора. Он сам все делает. Освобождать руками нужно внешние ресурсы и, например, низкоуровневые соединения с базой данных или файловые дескрипторы
18 янв 2016 в 14:52
@Stack интернированы по умолчанию только строковые литералы. В вашем ответе, кстати, об этом тоже было написано.
28 янв 2016 в 13:40
4 ответа 4
Сортировка: Сброс на вариант по умолчанию
Отвечая на ваш вопрос об обнулении ссылок -- нет, обнулять их не нужно, поскольку это не поможет. Более того, в вашем случае метод Dispose вам совсем не нужен. Почему, читайте ниже.
Существует только две ситуации, когда необходимо реализовывать IDisposable :
- В классе есть управляемые ( IDisposable ) ресурсы
- В классе есть неуправляемые ресурсы
В классе есть управляемые (IDisposable) ресурсы
У всех таких ресурсов нужно вызвать метод Dispose() или его аналог (например, Close() ). Зануллять такие ресурсы в 99.9% случаев нет необходимости, поскольку они в любом случае будут собраны сборщиком мусора. (Зануллять можно, если эти ресурсы занимают большой объем памяти и вы хотите "посигнализировать" сборщику мусора чтобы он побыстрее их собрал. Но никаких гарантий это не дает.)
public sealed class SingleApplicationInstance : IDisposable < private Mutex namedMutex; private bool namedMutexCreatedNew; public SingleApplicationInstance(string applicationName) < this.namedMutex = new Mutex(false, applicationName, out namedMutexCreatedNew); >public bool AlreadyExisted < get < return !this.namedMutexCreatedNew; >> public void Dispose() < namedMutex.Close(); >>В классе есть неуправляемые ресурсы
Реализация Dispose() для таких классов должна "закрыть" ресурс (как -- зависит от самого ресурса) и вызвать GC.SuppressFinalize(this); . Плюс обязательно должен быть реализован финализатор, в котором должно вызываться "закрытие" ресурса. Т.о. гарантируется, что ресурс будет закрыт в любом случае -- либо программистом (при этом финализатор вызва не будет), либо сборщиком мусора (путем вызова финализатора).
public sealed class WindowStationHandle : IDisposable < public WindowStationHandle(IntPtr handle) < this.Handle = handle; >public WindowStationHandle() : this(IntPtr.Zero) < >public bool IsInvalid < get < return (this.Handle == IntPtr.Zero); >> public IntPtr Handle < get; set; >private void CloseHandle() < if (this.IsInvalid) < return; >if (!NativeMethods.CloseWindowStation(this.Handle)) < Trace.WriteLine("CloseWindowStation: " + new Win32Exception().Message); >this.Handle = IntPtr.Zero; > public void Dispose() < this.CloseHandle(); GC.SuppressFinalize(this); >~WindowStationHandle() < this.CloseHandle(); >> internal static partial class NativeMethodsПолная версия о правильном применении IDisposable -- в моем переводе на Хабре.