Как реализовать интерфейс IDisposable в классе, унаследованном от SocketAsyncEventArgs

Я работаю над огромным проектом в C# .NET 4.0. Существует специальный класс, унаследованный от System.Net.Sockets. Класс SocketAsyncEventArgs. Что-то вроде следующего:

public class SocketTaskArgs : SocketAsyncEventArgs
{
    public SocketTaskArgs()
    {
        Completed += someEventhHandler;
    }

    public void CleanUp()
    {
        Completed -= someEventhHandler;
    }

    /* 
        There is a lot of code here that is unimportant at the moment.
    */
}

Итак, я хотел переместить содержимое метода CleanUp() в метод Dispose(bool).

Сначала я проверил исходный код базового класса - SocketAsyncEventArgs (используя " Перейти к определению", чтобы метаданные рассматривались в качестве источника). Я обнаружил, что этот класс реализует интерфейс IDisposable. Хорошо, мне просто нужно переопределить метод Dispose(bool), не так ли? (Дополнительные сведения см. В разделе " Интерфейс IDisposable в MSDN ", раздел " IDisposable и иерархия наследования "). Ничего нового для меня... К сожалению, класс SocketAsyncEventArgs реализован следующим образом:

public class SocketAsyncEventArgs : EventArgs, IDisposable
{
    public void Dispose();

    //some other stuff here
}

Это означает, что нет способа переопределить метод Dispose(bool), поскольку он реализован как закрытый, а не защищенный... В чем причина?

Далее я прочитал о методе SocketAsyncEventArgs.Dispose() в MSDN. Самое смешное, что он содержит следующий раздел:

Примечания для наследников

Dispose может вызываться несколько раз другими объектами. При переопределении Dispose (Boolean), будьте осторожны, чтобы не ссылаться на объекты, которые были ранее удалены в предыдущем вызове Dispose. Для получения дополнительной информации о том, как реализовать Dispose (Boolean), см. Реализация метода Dispose.

Чего ждать?

При переопределении Dispose (Boolean),...

Как я должен переопределить Dispose(Boolean)?

Каков рекомендуемый способ реализации интерфейса IDisposable в этом случае?

1 ответ

Решение

Кажется, ничто не мешает вам реализовать IDisposable на вашем дочернем классе, возьмите этот пример:

public class DisposableParent : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("The parent was disposed.");
    }
}

public class DisposableChild : DisposableParent, IDisposable
{
    public new void Dispose()
    {
        base.Dispose();
        Console.WriteLine("The child was disposed.");
    }
}

public class Program
{
    public static void Main()
    {
         using (DisposableChild c = new DisposableChild()) { }
         Console.ReadKey(true);
    }
}

Дает следующий вывод:

Родитель был распущен.

Ребенок был настроен.

Компилятор предупреждает о сокрытии расположения родительского класса в дочернем, поэтому с помощью new оператор избавляется от этого предупреждения, просто обязательно вызовите базовый класс Dispose от дочернего класса (и реализовать его правильно).

Распоряжение для ребенка станет что-то вроде:

public class DisposableChild : DisposableParent, IDisposable
{
    private bool _disposed = false;

    public new void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (!_disposed)
            {
                base.Dispose();
                Console.WriteLine("The child was disposed.");
                _disposed = true;
            }
        }
    }
}

И да, это все еще работает, если вы делаете что-то вроде:

using (DisposableParent p = new DisposableChild())
{

}

Но что-то вроде этого может сломать это:

public class Program
{
    public static void Main()
    {
        DisposableChild c = new DisposableChild();
        DisposeOfIt(c);

        Console.ReadKey(true);
    }

    public static void DisposeOfIt(DisposableParent p)
    {
        p.Dispose();
    }
}

Только распечатывает, что родитель был утилизирован. Поэтому, если вы используете этот метод, вам нужно быть осторожным в управлении временем жизни ваших объектов.

Другие вопросы по тегам