Реализация IDisposable на запечатанном классе

StackOverflow https://stackoverflow.com/questions/1418970

  •  07-07-2019
  •  | 
  •  

Вопрос

Я не думаю, что этот вопрос задавался раньше. Я немного сбит с толку лучшим способом реализации 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 полностью запечатан и никогда не наследуется от другого класса, в котором реализация виртуальной защищенной утилизации (удаление bool) не требуется.

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

Это было полезно?

Решение

Финализатор необходим в качестве резервного механизма для в конечном итоге освобождения неуправляемых ресурсов, если вы забыли вызвать Dispose .

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

Другие советы

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

Например:

 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;
     }
 }

Из веб-журнала Джо Даффи

  

Для закрытых классов этот шаблон нужен   не следует, то есть вы должны   просто внедрите свой финализатор и   Распоряжаться с помощью простых методов (т.е.   ~ T () (Завершить) и Dispose () в C #).   При выборе последнего маршрута ваш   код должен по-прежнему придерживаться   руководящие принципы ниже относительно   осуществление доработки и   распоряжаться логикой.

Так что да, ты должен быть хорошим.

Вам нужен финализатор, как упомянул Мехрдад. Если вы хотите избежать этого, вы можете взглянуть на SafeHandle . У меня недостаточно опыта работы с P / Invoke, чтобы предложить правильное использование.

Вы не можете объявить виртуальные методы в запечатанном классе. Также объявление защищенных членов в запечатанном классе дает вам предупреждение компилятора. Таким образом, вы реализовали это правильно. Вызов GC.SuppressFinalize (this) из финализатора необязателен по понятным причинам, но не может нанести вред.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top