Background:
Similar to this question, I am looking to use IDisposable for something other than what it was designed for.
The goal:
The application for this isn't terribly relevant, but just for a good example: I have a Windows Forms application where I implement an Undo design pattern. Typically that is done by intercepting "Value Changed" sorts of events from UI elements. For a DataGridView
, CellEndEdit
and so forth. However there are cases in which I programmatically change data, and I want to do the things each Undo action tracks, without tracking them.
So far:
I have a way to do all that, but I manage my "Should I Undo" logic on a count:
private int _undoOverrides = 0;
public bool ShouldUndo { get { return _undoOverrides < 1; } }
public void DoNotUndo() { ++_undoOverrides; }
public void ResumeUndo() { --_undoOverrides; }
Now, this works well but you have to remember to call ResumeUndo()
at the end of such business logic that begins with DoNotUndo()
. And I thought:
Maybe I'm not too much of an idiot to screw that up, but what if I had to expose this interface in code I pass down? I would like if the compiler could take care of this for me, if possible.
The idea:
I am considering using a class implementing IDisposable
for this. That way, a user of my code could use a using
block and never worry about housekeeping chores. This is what I have so far:
private static int _refCount = 0;
public static int ReferenceCount { get { return _refCount; } }
class HallPass : IDisposable
{
protected bool bActive;
public HallPass()
{
++Program._refCount;
bActive = true;
Console.WriteLine("Acquired hallpass!");
}
public void Dispose()
{
if (bActive)
--Program._refCount;
bActive = false;
Console.WriteLine("Hallpass expired!");
}
}
I included a boolean so that I'm sure I don't double-count back down on Dispose()
. So all you have to do to use a HallPass
is:
using (new HallPass())
{
// do things...
}
The question is:
Is this a good idea? Why might this be a bad idea? Any gotchas I should know about?
Also, and I feel stupid for this, but I'm pretty sure reference count is not the right term for it. It's like a reference count, but there's no reference to manage or memory to free. Edit: It could be, it's just not right now.
It's like a mutex or a critical section in that you're trying to make an exception (another misnomer, because I don't mean the kind you throw
) to a rule during a section of code, but it's not either of those because they're meant to be mutually exclusive within a scope--this is designed to be done in a nested fashion if you wish. That's why it's a count, not a boolean.