5
votes

I work on a huge project in C# .NET 4.0. There is a custom class inherited from System.Net.Sockets.SocketAsyncEventArgs class. Something like the following:

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.
    */
}

So, I wanted to move the content of CleanUp() method to Dispose(bool) method.

As first, I checked the source code of the base class - SocketAsyncEventArgs (using Go To Definition so that I saw metadata as source). I found out, this class implements IDisposable interface. Nice, I just need to override the Dispose(bool) method, don't I? (See IDisposable Interface on MSDN, the "IDisposable and the inheritance hierarchy" section, for more details). Nothing new for me... Unfortunately, the SocketAsyncEventArgs class is implemented as following:

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

    //some other stuff here
}

That means, there is no way how to override Dispose(bool) method, as it's implemented as private instead of protected... What is the reason for this?

Next, I read about SocketAsyncEventArgs.Dispose() method on MSDN. The funny thing is that, it contains the following section:

Notes to Inheritors

Dispose can be called multiple times by other objects. When overriding Dispose(Boolean), be careful not to reference objects that have been previously disposed of in an earlier call to Dispose. For more information about how to implement Dispose(Boolean), see Implementing a Dispose Method.

Wait... what?

When overriding Dispose(Boolean), ...

How am I supposed to override Dispose(Boolean)?

What is the recommended way to implement IDisposable interface in this case?

1
Its not private, its not implemented at all (see referencesource.microsoft.com/System/net/System/Net/Sockets/…). It does use a finalizer but looks like it wasn't really implemented in the recommended pattern.Ron Beyer
It looks like Dispose is public, just not virtual.D Stanley
You're not supposed to inherit from that class. You probably can't change that easily in a big existing project.usr
@RonBeyer it looks like you're right and SocketAsyncEventArgs class was really not implemented in the recommended pattern... Anyway, good to know, that the .NET sour code is available. I wasn't aware of that.Marek Takac
@usr I'd think, that I'm not supposed to inherit from it. There is no such information on the MSDN page and the class is not sealed, so why not? Anyway, you are right, that I cannot do a major change to the code at the moment, as it would require a lot of testing...Marek Takac

1 Answers

4
votes

There doesn't seem to be anything stopping you from implementing IDisposable on your child class, take this example:

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

Gives the following output:

The parent was disposed.

The child was disposed.

The compiler warns about hiding the dispose of the parent class in the child, so using the new operator gets rid of that warning, just make sure to call the base class Dispose from the child class (and implement it the right way).

The dispose for the child would become something like:

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

And yes, this still works if you do something like:

using (DisposableParent p = new DisposableChild())
{

}

But something like this can break it:

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

        Console.ReadKey(true);
    }

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

Only prints out that the parent was disposed. So if you used this method you would have to be careful about controlling the lifetime of your objects.