Паттерн Dispose
, как известно, сложно понять, особенно когда у нас есть иерархия классов, которая должна располагать вещи на разных уровнях. Ниже представлена рекомендуемая реализация, взятая из Реализация метода Dispose - Microsoft Docs .
using System;
class BaseClass : IDisposable
{
// To detect redundant calls
private bool _disposed = false;
~BaseClass() => 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 (_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;
}
}
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
class DerivedClass : BaseClass
{
// To detect redundant calls
private bool _disposed = false;
// Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// Dispose managed state (managed objects).
_safeHandle?.Dispose();
}
_disposed = true;
// Call base class implementation.
base.Dispose(disposing);
}
}
В этой реализации я не получаю того преимущества, которое мы имеем при добавлении _disposed
поле на каждом уровне иерархии? Вместо этого мы могли бы позаботиться о _disposed
только на верхнем уровне иерархии (тот, который непосредственно реализует IDisposable
и не заботиться об этом в производных классах.
Примерно так:
using System;
class BaseClass : IDisposable
{
// To detect redundant calls
private bool _disposed = false;
~BaseClass()
{
if (_disposed)
{
return;
}
Dispose(false);
_disposed = true;
}
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
if (_disposed)
{
return;
}
Dispose(true);
GC.SuppressFinalize(this);
_disposed = true;
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
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.
}
}
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
class DerivedClass : BaseClass
{
// Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Dispose managed state (managed objects).
_safeHandle?.Dispose();
}
// Call base class implementation.
base.Dispose(disposing);
}
}
Это настолько широко используемый пример кода, что, безусловно, должна быть веская причина для его реализации с повторением _disposed
на каждом уровне, но я просто не могу найти ни одного.
Это немного больше кода в базовом классе, но меньше беспокойства в производном классе, и некоторая повторяющаяся информация удалена.
Что еще мне не хватает?
Изменить:
Поскольку @InBetween правильно говорит, что одним из недостатков моей реализации является то, что если вам нужно будет проверить, размещен ли ваш объект в одном методе производного класса, вы не сможете это проверить. Давайте исправим это проблема, сделав его защищенным свойством с частным набором.
using System;
class BaseClass : IDisposable
{
// To detect redundant calls
protected bool Disposed { get; private set; } = false;
~BaseClass()
{
if (Disposed)
{
return;
}
Dispose(false);
Disposed = true;
}
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
if (Disposed)
{
return;
}
Dispose(true);
GC.SuppressFinalize(this);
Disposed = true;
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
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.
}
}
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
class DerivedClass : BaseClass
{
// Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
public void DoSomething()
{
if(Disposed)
{
throw new ObjectDisposedException("Cannot access disposed resource");
}
}
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Dispose managed state (managed objects).
_safeHandle?.Dispose();
}
// Call base class implementation.
base.Dispose(disposing);
}
}