Реализация IDisposable на запечатанном классе - PullRequest
20 голосов
/ 14 сентября 2009

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

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

Ответы [ 4 ]

13 голосов
/ 14 сентября 2009

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

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

8 голосов
/ 14 сентября 2009

незначительное дополнение; в случае general , обычно используется метод Dispose(bool disposing), чтобы вы знали, находитесь ли вы в 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;
     }
 }
7 голосов
/ 14 сентября 2009

Из Интернет-журнал Джо Даффи :

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

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

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

1 голос
/ 14 сентября 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...