How to accomplish scoped variable reset in C#?
One common pattern I see and use frequently in C++ is to temporarily set a variable to a new value, and then reset it when I exit that scope. In C++, this is easily accomplished with references and templated scope classes, and allows for increased safety and prevention of errors where the variable is set to a new value,开发者_开发问答 then reset to an incorrect assumed initial value.
Here is a simplified example of what I mean (in C++):
void DoSomething()
{
// The following line captures GBL.counter by reference, stores its current
// value, and sets it to 1
ScopedReset<int> resetter(GBL.counter, 1);
// In this function and all below, GBL.counter will be 1
CallSomethingThatNeedsCounterOf1();
// When I hit the close brace, ~ScopedReset will be called, and it will
// reset GBL.counter to it's previous value
}
Is there any way to do this in C#? I've found the hard way that I can't capture a ref parameter inside an IEnumerator or a lambda, which were my first two thoughts. I don't want to use the unsafe keyword if possible.
The first challenge to doing this in C# is dealing with non-deterministic destruction. Since C# doesn't have destructors you need a mechanism to control scope in order to execute the reset. IDisposable
helps there and the using
statement will mimic C++ deterministic destruction semantics.
The second is getting at the value you want to reset without using pointers. Lambdas and delegates can do that.
class Program
{
class ScopedReset<T> : IDisposable
{
T originalValue = default(T);
Action<T> _setter;
public ScopedReset(Func<T> getter, Action<T> setter, T v)
{
originalValue = getter();
setter(v);
_setter = setter;
}
public void Dispose()
{
_setter(originalValue);
}
}
static int counter = 0;
static void Main(string[] args)
{
counter++;
counter++;
Console.WriteLine(counter);
using (new ScopedReset<int>(() => counter, i => counter = i, 1))
Console.WriteLine(counter);
Console.WriteLine(counter);
}
}
Can you not simply copy the reference value to a new local variable, and use this new variable throughout your method, i.e. copy value by value?
Indeed, changing it from a ref
to regular value parameter will accomplish this!
I don't think you can capture a ref paramenter to a local variable, and have it stay a ref - a local copy will be created.
GBL.counter is effectively an implicit, hidden parameter to CallSomethingThatNeedsCounterOf1. If you could convert it to a regular, declared paraemter your problem would go away. Also, if that would result in to many parameters, a solution would be a pair of methods which set up and reset the environment so that CallSomethingThatNeedsCounterOf1() can run.
You can create a class that calls the SetUp method in its constructor and the Reset method in Dispose(). You can use this class with the using statement, to aproximate the c++ behaviour. You would, however, have to create one of these classes for each scenario.
精彩评论