Something I often used back in C++ was letting a class A
handle a state entry and exit condition for another class B
, via the A
constructor and destructor, to make sure that if something in that scope threw an exception, then B would have a known state when the scope was exited. This isn't pure RAII as far as the acronym goes, but it's an established pattern nevertheless.
In C#, I often want to do
class FrobbleManager
{
...
private void FiddleTheFrobble()
{
this.Frobble.Unlock();
Foo(); // Can throw
this.Frobble.Fiddle(); // Can throw
Bar(); // Can throw
this.Frobble.Lock();
}
}
Which needs to be done like this
private void FiddleTheFrobble()
{
this.Frobble.Unlock();
try
{
Foo(); // Can throw
this.Frobble.Fiddle(); // Can throw
Bar(); // Can throw
}
finally
{
this.Frobble.Lock();
}
}
if I want to guarantee the Frobble
state when FiddleTheFrobble
returns. The code would be nicer with
private void FiddleTheFrobble()
{
using (var janitor = new FrobbleJanitor(this.Frobble))
{
Foo(); // Can throw
this.Frobble.Fiddle(); // Can throw
Bar(); // Can throw
}
}
where FrobbleJanitor
looks roughly like
class FrobbleJanitor : IDisposable
{
private Frobble frobble;
public FrobbleJanitor(Frobble frobble)
{
this.frobble = frobble;
this.frobble.Unlock();
}
public void Dispose()
{
this.frobble.Lock();
}
}
And that's how I want to do it. Now reality catches up, since what I want to use requires that the FrobbleJanitor
is used with using
. I could consider this a code review issue, but something is nagging me.
Question: Would the above be considered as abusive use of using
and IDisposable
?
Eric Gunnerson, who was on the C# language design team, gave this answer to pretty much the same question:
Note: My viewpoint is probably biased from my C++ background, so my answer's value should be evaluated against that possible bias...
What Says the C# Language Specification?
Quoting C# Language Specification:
The Code that is using a resource is, of course, the code starting by the
using
keyword and going until the scope attached to theusing
.So I guess this is Ok because the Lock is a resource.
Perhaps the keyword
using
was badly chosen. Perhaps it should have been calledscoped
.Then, we can consider almost anything as a resource. A file handle. A network connection... A thread?
A thread???
Using (or abusing) the
using
keyword?Would it be shiny to (ab)use the
using
keyword to make sure the thread's work is ended before exiting the scope?Herb Sutter seems to think it's shiny, as he offers an interesting use of the IDispose pattern to wait for a thread's work to end:
http://www.drdobbs.com/go-parallel/article/showArticle.jhtml?articleID=225700095
Here is the code, copy-pasted from the article:
While the C# code for the Active object is not provided, it is written the C# uses the IDispose pattern for the code whose C++ version is contained in the destructor. And by looking at the C++ version, we see a destructor which waits for the internal thread to end before exiting, as shown in this other extract of the article:
So, as far as Herb is concerned, it's shiny.
We have plenty of use of this pattern in our code base, and I've seen it all over the place before - I'm sure it must have been discussed here as well. In general I don't see what's wrong with doing this, it provides a useful pattern and causes no real harm.
I don't think so, necessarily. IDisposable technically is meant to be used for things that have non-managed resources, but then the using directive is just a neat way of implementing a common pattern of
try .. finally { dispose }
.A purist would argue 'yes - it's abusive', and in the purist sense it is; but most of us do not code from a purist perspective, but from a semi-artistic one. Using the 'using' construct in this way is quite artistic indeed, in my opinion.
You should probably stick another interface on top of IDisposable to push it a bit further away, explaining to other developers why that interface implies IDisposable.
There are lots of other alternatives to doing this but, ultimately, I can't think of any that will be as neat as this, so go for it!
I believe the answer to your question is no, this would not be an abuse of
IDisposable
.The way I understand the
IDisposable
interface is that you are not supposed to work with an object once it has been disposed (except that you're allowed to call itsDispose
method as often as you want).Since you create a new
FrobbleJanitor
explicitly each time you get to theusing
statement, you never use the sameFrobbeJanitor
object twice. And since it's purpose is to manage another object,Dispose
seems appropriate to the task of freeing this ("managed") resource.(Btw., the standard sample code demonstrating the proper implementation of
Dispose
almost always suggests that managed resources should be freed, too, not just unmanaged resources such as file system handles.)The only thing where I'd personally worry is that it's less clear what's happening with
using (var janitor = new FrobbleJanitor())
than with a more explicittry..finally
block where theLock
&Unlock
operations are directly visible. But which approach is taken probably comes down to a matter of personal preferences.A real world example is the BeginForm of ASP.net MVC. Basically you can write:
or
Html.EndForm calls Dispose, and Dispose just outputs the
</form>
tag. The good thing about this is that the { } brackets create a visible "scope" which makes it easier to see what's within the form and what not.I wouldn't overuse it, but essentially IDisposable is just a way to say "You HAVE to call this function when you're done with it". MvcForm uses it to make sure the form is closed, Stream uses it to make sure the stream is closed, you could use it to make sure the object is unlocked.
Personally I would only use it if the following two rules are true, but thet are set arbitarily by me:
At the end, it's all about expectations. If an object implements IDisposable, I assume it needs to do some cleanup, so I call it. I think it usually beats having a "Shutdown" function.
That being said, I don't like this line:
As the FrobbleJanitor "owns" the Frobble now, I wonder if it wouldn't be better to instead call Fiddle on he Frobble in the Janitor?