johnny 5
johnny 5

Reputation: 20995

C# Initialize Disposable in inherited constructor

Hey so I have a base class coming from a 3rd party dll, which is dependent on a disposable. Context: IDisposable

public class BaseValidator
{
    public BaseValidator(Context context) {}
}

We're trying to move away from tying our classes to these dependencies. So we started relying on providers instead

public interface IContextProvider 
{
    Context Create();
}

I have a new validator that I'm writing which inherits from the BaseValidator, but I would like it to be dependent on the IContextProvider instead. So I'd like to create the context in the inherited constructor, but I would like to dispose of it in the destructor to prevent memory leaks, However I'm not sure if this is possible.

public class EntityValidator: BaseValidator 
{
    public EntityValidator(IContextProvider provider) : base(provider.Create()) 
    {
    }

    ~EntityValidator()
    {
        //I'm not how I can dispose the entity I've passed into it.
    }
}

My question is, is there a trick I can use to Capture the variable passed into the base?

Note: I know I can make a work around with an external helper class, but I'm interested if anyone knows how to do this in a more savvy way.

Upvotes: 2

Views: 216

Answers (2)

Astyan
Astyan

Reputation: 66

Before, in other language like C++, developpers used to rely on destructors to do a lot of cleaning. It was guaranteed by compiler so it was a strong behavior. Smart pointer was a good pattern that used this behavior (to implement something like very basic but automatic garbage collection system). Code was elegant but people used it for a lot of other need. With time a lot of code used to happen in destructor making the debug and readability hard.

IDisposable has been made to force developpers to write explicitely the call to Dispose... This is also useful when you want to Dispose internal resource without your object being destructed. For example in some "Close" method of Stream, where the stream is definitively closed but you can still have reference on steam... and use IsOpen property...

So for me you should not try to hide it. If you depends on code that needs to be Disposed, embrace this dependency... or chose another third party library. You can simply make the class that need to manipulate Disposable object (BaseValidator) IDisposable too and delegate the need to call to the user.

Usually people that write class implementing IDisposable, in open source project, also write destructor (just in case someone forgot to call Dispose) This is true for a lot of class of .Net framework too (for example Pen() { where nobody thinks to call Dispose on it in some Control drawing events...)

So I would recommend :

  1. Go get the information about if they did it with their classes, if yes they are strong probability they will keep this behavior/code forever. So you can just make your class to inherit IDisposable too, call context.Dispose in your own Dispose implement and that's enough... no need to worry because if your user forgot to call Dispose, the third party do the cleaning. Add a destructor/finalizer on your class only if you have other unmanaged resource to clean too...

  2. Now if they did not, you can just wrap third party "Context" class in your own Context class. Your Context class will have to implement destructor that call Dispose on the instance of third party Context class. And that's it. You can even make your Context class sealed. Your Context class is here to make the behavior of your app as close as if 1) was true and implemented by third party. So this class will be easy to remove later because third party will probably implement finalizer one day (if they are serious). Doing just a sealed wrapper around just one class will avoid some complexity/issue related to how destructors (finallizers) works in .Net : They are all called in any order. Because of this underterministic behavior it makes your code hard to maintain later. For example if third party Context class's finalizer is called before your wrapping class => exception can occured and at a bad time (when gc is doing its stuff) which can make your app crash. Because of all these problems, you better go back to 1)

Upvotes: 0

Eric J.
Eric J.

Reputation: 150108

If the BaseValidator class does not expose Context in a public manner, your current design would require you use reflection and knowledge of the internal implementation of BaseValidator to dispose of it, which is of course fragile.

I would instead capture the context using an intermediate constructor:

Context _context;
private EntityValidator(Context context) : base(context) 
{ 
    _context = context;
}
public EntityValidator(IContextProvider provider) : this(provider.Create())
{
    
}

Note, disposing via a finalizer (a.k.a. destructor) is not ideal due to constraints it places on the garbage collector. I'd instead have EntityValidator implement IDisposable

Upvotes: 7

Related Questions