Подтвердить что ты не робот

Внедрение 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) не требуется.

Кроме того, обязательно нужно финализатор? Все мысли приветствуются.

4b9b3361

Ответ 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(это) из финализатора не требуется по очевидным причинам, но он не может нанести вред.

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