views:

79

answers:

4
  • We have an interface that is passed to the constructor using an IoC container
  • We have multiple classes that implement this interface.

The problem is, some (not all) of the implementations need to be cleaned up, preferably using the IDisposable interface.

Since not all of the implementations will need the "Dispose" method, do I include it anyway (I realize I'm not actually implementing IDisposable, this is just an example)? To me, it seems like the class that has knowledge about the actual implementations of the interface should know how to clean up those objects. However, this is the IoC container, and I see no elegant way of signalling it to call cleanup on a particular class it has created.

interface IBar
{
  void DoWork();
  void Dispose(); //Not all implementations need this
}

class Foo : IBar
{
  public void DoWork()
  {
    //implementation 1
  }

  public void Dispose()
  {
    //cleanup, which needs to be called
  }
}

class Foo2 : IBar
{
  public void DoWork()
  {
    //implementation 2
  }

  public void Dispose()
  {
    //No cleanup needed in this implementation
  }
}
A: 

When does Dispose need to be called? After performing work or during application shutdown? If the latter, doesn't your IoC container support such cleanup? In Java and with the Spring IoC container, your implementation would implement Spring's disposable interface, and the container would invoke the dispose method during shutdown.

meriton
I'm specifically cleaning up some event handlers when an error occurs. I basically want to shut down a couple of classes that run in the background. I suppose it may be possible to use a custom lifecycle that lets me manually shutdown a class on-demand. I'm using Structuremap.
Jason Young
+1  A: 

What's wrong with the following?

interface IBar { void DoWork(); }
class Foo : IBar, IDisposable { // details, implements Dispose }
class Foo2 : IBar { // details, no Dispose }
Jason
+3  A: 

You can implement IDisposable on the ones that need it and then do this:

interface IBar
{
    void Bar();
}

class Foo : IBar
{
    public void Bar() { }
}

class Foo2 : IBar, IDisposable
{
    public void Bar() {}
    public void Dispose() {}
}

class Program
{
    static void Main(string[] args)
    {
        foreach (IBar bar in new IBar[] { new Foo(), new Foo2() })
        {
            bar.Bar();

            IDisposable disp = bar as IDisposable;

            if (disp != null)
            {
                disp.Dispose();
            }
        }
    }
}
João Angelo
+1  A: 

Odd question. If your concern is specifically IDisposable, most containers will inspect the implementation, and if it contains an implementation of IDisposable the container will invoke Dispose () auto-magically when it releases the instance.

So,

// pure clean business interface. no dispose member
public interface ISomeBusinessInterface { }

// an implementation that does not require any 'disposing'
// and implements *only* business interface
public class SomeBusinessImplementation : 
    ISomeBusinessInterface
{
}

// an implementation that contains heavy resources and 
// requires 'disposing'
public class SomeDisposableBusinessImplementation : 
    ISomeBusinessInterface,
    IDisposable
{
}

// a typical consumer. no knowledge of instance life-cycle
public class SomeConsumer 
{
    // injector constructor
    public SomeConsumer (ISomeBusinessInterface business) { }
}

Your consumers should have no knowledge of an injected component's lifecycle, and is not in any shape way or form responsible for it. From this perspective, it makes sense that ISomeBusinessInterface does not inherit or expose any such Dispose method - I mean, consider if you disposed of an important singleton service! How embarassing would that be?

So who is responsible for the life-cycle of injected components? Well the container is of course! As previously mentioned, most containers will inspect component implementations for implementations of IDisposable [also another reason to use standard CLR-defined interfaces], and will invoke Dispose when it determines the component has reached the end of its life. So SomeDisposableBusinessImplementation will be properly disposed of in the end. :)


Castle Windsor

Castle Windsor IoC container tracks and maintains references to all instances it creates. It uses reflection to determine if the instance is "disposable" or not, and then invokes Dispose () when the instance's lifecycle completes. For long-lived objects, this is when the container is disposed. For transient short-lived objects, this is when Release (object) is explicitly invoked by consumer *.

*=which raises an important point, transient objects should be acquired via an explicit call to Resolve<T> () and released via an explicit call to Release (object). Implicitly injecting transient classes may lead to memory leaks!


StructureMap

StructureMap IoC container does not track and maintain references to transient instances. Which pretty much means object lifecycle management is a little complex +. This is great for transient short-lived objects, as there is no additional book-keeping required except the usual. However, for long-lived objects, you will have to extend the framework, reflect, and dispose of instances yourself.

+=complex imo.

johnny g
How do you normally tell your IoC container that you're "done" with a class?Suppose the application had a long running file write interface that was used by many classes. If I wanted to have a button that closed the file, how would I do that? I need to tell the IoC container somehow, but I'm not sure how to do that.Custom lifecycle/lifestyle?
Jason Young
I see a way to do it with Windsor, but not structuremap.
Jason Young
processing query ... [ie googling structuremap] ... will update once compiled.
johnny g
The only thing I found was a comment from Jeremy Miller stating that he wasn't adding IDisposable support.
Jason Young
compiled! see revised solution. embedded some links, including one to Msdn finalize/dispose pattern [which you will require as you cannot rely on explicit disposal] and another stack question regarding explicit disposal of StructureMap components.
johnny g