1

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?

Dmytro Shevchenko
  • 33,431
  • 6
  • 51
  • 67
  • In the past I have seen this implemented with `BeginXXX` and `EndXXX`. I've never come across a specific name for it though. – Simon Whitehead Sep 25 '13 at 09:45
  • 1
    A similar idea lies behind Asp.net MVC: Html.BeginForm() invokable inside a using – ilmatte Sep 25 '13 at 09:49
  • @SimonWhitehead But `BeginXXX` and `EndXXX` is another pattern. You have to call `EndXXX`, otherwise, the default behavior will not be restored. – Dmytro Shevchenko Sep 25 '13 at 09:56
  • Using `using` looks a bit more elegant, but having to create another class seems like a bit too much trouble. Why not just `try { switch = special; doStuff(); } finally { switch = normal; }`, since `using` is basically just syntactic sugar for something similar anyways. – Corak Sep 25 '13 at 09:57
  • @Corak In my case, the class I am writing is a part of API that will be used by other developers. I really want to ensure the default behavior is always restored. And `try/finally` is something people will forget to write, I'm quite sure. – Dmytro Shevchenko Sep 25 '13 at 10:00
  • @Shedal - How do you force them to use `using`? – Corak Sep 25 '13 at 10:02
  • @Corak I don't. But the object will be picked up by GC at some point anyway. – Dmytro Shevchenko Sep 25 '13 at 10:49
  • 1
    Related, almost a dupe: [Is it abusive to use IDisposable and “using” as a means for getting “scoped behavior”](http://stackoverflow.com/questions/2101524/) – H H Sep 25 '13 at 11:18
  • Thank you @HenkHolterman, it was a useful read! – Dmytro Shevchenko Sep 25 '13 at 12:37

2 Answers2

2

Yes, it's kind of a pattern, although I don't know if it has a particular name. I call it the "scope" pattern.

For example, System.Transactions uses this pattern. Transaction.Current is the thread static "ambient" transaction, and TransactionScope commits or rolles back that transaction when getting disposed.

cdoubleplusgood
  • 1,309
  • 1
  • 11
  • 12
1

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.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • In the second part you seem to be talking about transaction scoping, while my question doesn't necessarily imply transactions. In the first part, though, I like how you summed up why handling exceptions is hard in this case. Thanks for that! I think, another drawback to this pattern is that we cannot *force* the calling code to use the `using` block. And if it doesn't, it means that disposal of the object might be delayed until an undefined moment in the future. Which, at least in my case, proved to be unacceptable. – Dmytro Shevchenko Sep 25 '13 at 18:23
  • 1
    @Shedal: Transaction scopes are a common situation where different behavior would be appropriate in the exception versus non-exception case, where requiring code in the main-line to distinguish the cases would be awkward and would negate much of the purpose of having the scope-guard in the first place, and where the lack of a nice way to distinguish exception and non-exception cases often leads to code simply ignoring the distinction. They thus form an excellent example of the semantic weakness of `IDisposable` as a means of guarding a code block. – supercat Sep 25 '13 at 18:29