Внедрение IDisposable в закрытом классе
Я не думаю, что этот вопрос задавали раньше. Я немного смущен лучшим способом реализовать IDisposable
в закрытом классе - в частности, запечатанном классе, который не наследуется от базового класса. (То есть, "чистый закрытый класс", который является моим составленным термином.)
Возможно, некоторые из вас согласны со мной в том, что рекомендации по реализации IDisposable
очень сбивают с толку. Тем не менее, я хочу знать, что способ, которым я намерен реализовать IDisposable
, достаточно и безопасен.
Я делаю код P/Invoke, который выделяет IntPtr
через Marshal.AllocHGlobal
и, естественно, я хочу очистить неуправляемую память, которую я создал. Поэтому я думаю о чем-то вроде этого
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public sealed class MemBlock : IDisposable
{
IntPtr ptr;
int length;
MemBlock(int size)
{
ptr = Marshal.AllocHGlobal(size);
length = size;
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
GC.SuppressFinalize(this);
}
}
~MemBlock()
{
Dispose();
}
}
Я предполагаю, что, поскольку MemBlock
полностью запечатан и никогда не происходит из другого класса, для реализации virtual protected Dispose(bool disposing)
не требуется.
Кроме того, обязательно нужно финализатор? Все мысли приветствуются.
Ответы
Ответ 1
Финализатор необходим в качестве резервного механизма для освобождения неуправляемых ресурсов, если вы забыли позвонить Dispose
.
Нет, вы не должны объявлять метод virtual
в классе sealed
. Он вообще не компилируется. Кроме того, не рекомендуется объявлять новых членов protected
в классах sealed
.
Ответ 2
Небольшое добавление; в общем случае общий шаблон должен иметь метод Dispose(bool disposing)
, так что вы знаете, находитесь ли вы в Dispose
(где доступно больше вещей) против финализатора (где вы должны " t действительно касаются любых других связанных управляемых объектов).
Например:
public void Dispose() { Dispose(true); }
~MemBlock() { Dispose(false); }
void Dispose(bool disposing) { // would be protected virtual if not sealed
if(disposing) { // only run this logic when Dispose is called
GC.SuppressFinalize(this);
// and anything else that touches managed objects
}
if (ptr != IntPtr.Zero) {
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
}
Ответ 3
Из веб-блога Джо Даффи:
Для закрытых классов этот образец нужен не следует, то есть вы должны просто внедрите свой финализатор и Распоряжаться с помощью простых методов (т.е. ~ T() (Завершить) и Dispose() в С#). При выборе последнего маршрута ваш код должен по-прежнему придерживаться руководящие принципы ниже относительно осуществление доработки и распоряжаться логикой.
Так что да, ты должен быть хорошим.
Вам нужен финализатор, как упомянул Мехрдад. Если вы хотите избежать этого, вы можете взглянуть на SafeHandle. У меня недостаточно опыта работы с P/Invoke, чтобы предложить правильное использование.
Ответ 4
Вы не можете объявлять виртуальные методы в закрытом классе. Также объявление защищенных членов в закрытом классе дает вам предупреждение о компиляторе. Таким образом, вы внедрили его правильно.
Вызов GC.SuppressFinalize(это) из финализатора не требуется по очевидным причинам, но он не может нанести вред.
Наличие финализатора имеет важное значение при работе с неуправляемыми ресурсами, поскольку они не освобождаются автоматически, вы должны сделать это в финализаторе, который вызывается автоматически после того, как объект был собран мусором.