如何在从SocketAsyncEventArgs继承的类中实现IDisposable接口?

5

我在一个使用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)方法,对吧?(请参见MSDN上的IDisposable接口,“IDisposable和继承层次结构”部分,获取更多详细信息)。对我来说没有什么新东西...不幸的是,SocketAsyncEventArgs类的实现如下:

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

    //some other stuff here
}

这意味着,无法覆盖 Dispose(bool) 方法,因为它是作为 private 实现的而不是 protected... 这是什么原因?
接下来,我在 MSDN 上了解了 SocketAsyncEventArgs.Dispose() 方法。有趣的是,它包含以下部分:
“继承者说明”
Dispose 可以被其他对象多次调用。当重写 Dispose(Boolean) 时,请注意不要引用先前在早期调用 Dispose 中已处理的对象。有关如何实现 Dispose(Boolean) 的详细信息,请参阅实现 Dispose 方法。”
等等...什么?
“当重写 Dispose(Boolean) 时,...”
我该如何重写 Dispose(Boolean)?

在这种情况下,实现IDisposable接口的推荐方式是什么?


1
它不是私有的,根本没有实现(请参见http://referencesource.microsoft.com/System/net/System/Net/Sockets/Socket.cs.html#8877)。它确实使用了终结器,但看起来并没有按照推荐的模式实现。 - Ron Beyer
看起来 Dispose 是公开的,只是不是 virtual - D Stanley
你不应该继承那个类。在一个大型的现有项目中,你可能无法轻松地更改它。 - usr
@RonBeyer 看起来你是对的,SocketAsyncEventArgs 类确实没有按照推荐的模式实现... 不管怎样,很高兴知道 .NET 源代码是可用的。我之前不知道这一点。 - Marek Takac
@usr 我认为,我不应该从中继承。MSDN页面上没有这样的信息,而且该类也没有被密封,那么为什么不呢?无论如何,你是对的,目前我不能对代码进行重大更改,因为这将需要大量测试... - Marek Takac
通常你会进行包装。滥用继承(大多数情况下)。通常继承是用于替换不同的行为。例如Stream或IEnumerable。 - usr
1个回答

4

您似乎可以在子类上实现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);
    }
}

给出以下输出:

父对象已被释放。

子对象已被释放。

编译器警告在子类中隐藏父类的dispose,因此使用new运算符可以消除该警告,只需确保从子类调用基类Dispose(并正确实现它)即可。
子类的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();
    }
}

仅打印出父对象已被处理的信息。因此,如果您使用此方法,则需要小心控制对象的生命周期。


谢谢你的回答。我同意你的观点,我可以在我的继承类上实现IDisposable接口。这实际上是我首先想到的事情。我只是好奇是否会有人提出另一个可能更好的想法。如果在不久的将来没有更好的解决方案,我会将你的答案标记为已接受。 - Marek Takac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接