开发者

How to accomplish scoped variable reset in C#?

开发者 https://www.devze.com 2023-01-10 21:59 出处:网络
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 tem

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消