Johnv2020
Johnv2020

Reputation: 2146

Dispose of resources used by 3rd party class which does not implement IDisposable

I'm using a 3rd party assembly to provide sftp functionality to my project as shown below. Multiple threads need to implement this functionality so the 3rd party assembly is consumed in a static helper class.

The 3rd party assembly works well unless an unusual error condition is encountered, e.g. the sftp server exists but the directory I'm attempting to put files into gets deleted. When this happens a valid exception is thrown however it appears that intermittently the 3rd part assembly isn't cleaning up after itself correctly as no new instances of the client will work.

static readonly object _locker = new object();

    static void WorkerMethodCalledByThread()
    {
        lock(_locker)
        {
            var client = new ThirdPartyAssembly();
            bool hasConnection = false;
            try
            {
                client.Connect("some connection details");
                hasConnection = true;
                // do some work - 
                //but if an issue happens here then no new instances of ThirdPartAssembly work
            }
            catch(Exception e)
            {
                Logger.LogError(e);
            }
            finally
            {
                if (hasConnection) { client.Close(); }
            }
        }
    }

I have no means to get the 3rd party assembly changed so what I'm looking for is a way to dispose of any resources it may have taken. However the 3rd party class being called does not implement IDisposable itself.

So.. I'm trying to figure out if its possible to release the resources used by the 3rd party ?

The two possible approaches I can think of are either

1: add a wrapper class which has the 3rd party class as its base class & which also implements IDisposable (shown below) - however I'm pretty sure this won't clean up the base class

2: use a WeakReference to the 3rd party class (shown below) - but I don't know if this will work

Wrapper Class

public class WrapperClass : ThirdPartyClass, IDisposable
{
    ~WrapperClass()
    {
        Dispose();
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}

WeakReference

static readonly object _locker = new object();

    static void WorkerMethodCalledByThread()
    {
        lock(_locker)
        {
            var client = new WeakReference(new ThirdPartyAssembly());
            bool hasConnection = false;
            try
            {
                ((ThirdPartyAssembly)client.Target).Connect("some connection details");
                hasConnection = true;
                // do some work - 
                //but if an issue happens here then no new instances of ThirdPartAssembly work
            }
            catch(Exception e)
            {
                Logger.LogError(e);
            }
            finally
            {
                if (hasConnection) { ((ThirdPartyAssembly)client.Target).Close(); }
            }
        }
    }

Upvotes: 1

Views: 362

Answers (1)

Mike Perrenoud
Mike Perrenoud

Reputation: 67898

So the problem with the WeakReference is:

while still allowing that object to be reclaimed by garbage collection.

it doesn't guarantee that GC will get to it because it clearly isn't managing itself well internally.

The better approach is to base the class and implement IDisposable. This will allow you to leverage your wrapper in a using statement. However, the caveat is this, and there's really no way around it, you're going to have to use Reflection to dig into this class and clean it up.

It's going to take a lot of breakpoints, and a lot of digging in the Watch windows, but you can get it done!

Upvotes: 1

Related Questions