Search code examples
c#.netdesign-patternsdisposeidisposable

Is there a design pattern for section of code defined by a Disposable object?


Sometimes when I use a class (let's call it MyClass) I need to change its behavior locally and make sure the default behavior is restored afterwards.

I'm thinking something along the lines of creating another class (e.g. MyClassBehaviorSwitcher) implementing IDisposable. In the constructor it will set some static property and unset it in the Dispose() method. MyClass will then take the value of the static property into account. Usage example:

using (new MyClassBehaviorSwitcher()) {
    // Work with MyClass, which will behave differently
    // until the end of the block.
}

This way, I ensure the default behavior is restored afterwards. Even if the client code doesn't use using, the object will get disposed at some point.

My question: is this a pattern? Is there naming convention for such classes? Or maybe I am overlooking something and there is a better way of implementing what I want?


Solution

  • What you describe is a common scoped-behavior pattern. It has one significant weakness, however, which is that there's no way for the code in the Dispose method to know whether it is being called because an exception occurred within the using block, or whether it's being called as a result of a non-exception exit. The using pattern is not usable in cases where code needs to take different action based upon whether an exception occurred. Worse, the relative difference in convenience between employing the using pattern and one which can distinguish between the "exception" and "non-exception" cases means that a lot of code which should distinguish those cases, doesn't.

    Among other things, if the semantics of a transaction scope require that any exception which is begun must be either committed or rolled back before code leaves the scope, it would be helpful if an attempt to leave the scope without performing a commit or rollback could trigger an exception. Unfortunately, if the scoping block can't determine whether the scope is being left because of an exception, it must generally select among three ugly alternatives:

    • Silently perform a rollback, thus providing no indication of improper usage in the event that code exits non-exceptionally without having committing or rolling back a transaction.

    • Throw an exception, thus overwriting any exception that may have been pending.

    • Include code to record improper usage, thus introducing a dependency upon whatever method of logging is used.

    Having have scope departure throw an exception if there isn't one pending, or throw an exception but wrap any pending exception within it, would be cleaner than any of the above alternatives, but isn't possible except with a rather clunky try/finally pattern.