4

The MSDN documentation for IDisposable states that:

It is a version-breaking change to add the IDisposable interface to an existing class, because it changes the semantics of the class.

What exactly does this mean?

I can see how removing IDisposable would be a big deal. Instantiating a class inside of a using statement would no longer be possible, for example. But what else makes IDisposable special, especially in the context of adding the interface?

3 Answers 3

7

When you implement IDisposable, you're basically adding semantics of "you should dispose this when you're done with it". That means that existing code which doesn't dispose of it is violating that implicit contract... so there's no way of going from "not implementing IDisposable" to "implementing IDisposable" while keeping clients using your type correctly, unless you also have access to all that client code.

To give another example - suppose the documentation for GetHashCode was updated to say "This should always return an even number" - that would be a breaking change because existing code could easily return an odd number. Again, it wouldn't break in source code - everything would still build - but you'd be changing the contract to make it more restrictive.

If you view the implementation of IDisposable as a contract on the client code, hopefully it's clearer why it's a breaking change.

Sign up to request clarification or add additional context in comments.

1 Comment

Ah, of course. Yep, you're causing the client code to be no longer using the class correctly. For some reason I was thinking this changed some sort of under-the-hood compiler-ism (like using, for example). Your explanation makes perfect sense.
1

Implementing IDisposable tells clients that they always need to dispose the class.

That is a breaking change.

1 Comment

Yep, it's obvious now :-) This "breaks" clients that were never coded to Dispose().
1

If a class does not implement IDisposable, code which uses the class will likely create and abandon instances without examining them to see if they implement IDisposable. Even if a later version of the class does implement IDisposable, that change will not magically cause code which never checks for IDisposable to magically start calling IDisposable.Dispose; client code which explicitly checks for IDisposable and attempts to call Dispose if possible (e.g. the compiler-generated code associated with foreach blocks) will start calling IDisposable.Dispose once that becomes possible, but most code won't bother.

Note that not all of the code which uses an object will have to worry about its implementing IDisposable. The only time something should call IDisposable on an object is if it is the last thing to hold a "useful" reference to it. If code gives a method a reference to an object and the method won't do anything with the object after it returns, the caller will easily be able to know when the method is done with the object, and so the caller will just as able to call Dispose as would be the method. Consequently, there would be no reason for the method to call Dispose. On the other hand, if code calls an object's implementation of IEnumerable<T>.GetEnumerator and that method needs the services of an object that will require cleanup once enumeration is complete, the only way the object which implemented GetEnumerator could possibly when the services are no longer required will be if the code that called GetEnumerator calls Dispose on the returned enumerator once it is no longer required.

Incidentally, whether it's necessary to call IDisposable.Dispose on the object returned by a factory method like GetEnumerator doesn't necessarily depend on the factory method's return type. If one calls the non-generic IEnumerable.GetEnumerator() on an object, semantic correctness requires that one check whether the returned object instance implements IDisposable (it might do so, even though the declared type of IEnumerable.GetEnumerator() does not) and call Dispose on it if so. From that standpoint, having a later version of an abstract base class implement IDisposable when earlier versions didn't do so might not really be a breaking change. If code which called factory methods returning the abstract class were required to call Disposed on the returned items whenever it was possible to do so, having the base class implement IDisposable would not add any new responsibilities to the client code--it would merely make it easier for client code to actually care out the responsibilities it was going to have anyway (checking whether an object instance implements IDisposable is slower and more cumbersome than would be calling IDisposable.Dispose unconditionally on a class which implements it as a do-nothing method).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.