Как реализовать интерфейс 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();
}
}
Только распечатывает, что родитель был утилизирован. Поэтому, если вы используете этот метод, вам нужно быть осторожным в управлении временем жизни ваших объектов.