如何在C#中完成范围变量重置?

时间:2010-08-08 17:32:13

标签: c#

我在C ++中经常看到并经常使用的一种常见模式是暂时将变量设置为新值,然后在退出该范围时重置它。在C ++中,可以使用引用和模板化范围类轻松完成,并且可以提高安全性并防止错误,其中变量设置为新值,然后重置为不正确的假定初始值。

这是我的意思(在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
}

有没有办法在C#中这样做?我发现很难在IEnumerator或lambda中捕获ref参数,这是我的前两个想法。如果可能,我不想使用unsafe关键字。

3 个答案:

答案 0 :(得分:3)

在C#中执行此操作的第一个挑战是处理非确定性破坏。由于C#没有析构函数,因此您需要一种机制来控制作用域以执行重置。 IDisposable帮助那里,using语句将模仿C ++确定性破坏语义。

第二个是在不使用指针的情况下获得要重置的值。 Lambdas和代表可以做到这一点。

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);
    }
}

答案 1 :(得分:1)

你能不能简单地将参考值复制到一个新的局部变量,并在整个方法中使用这个新变量,即按值复制值?

确实,将它从ref更改为常规值参数将实现此目的!

答案 2 :(得分:0)

我认为你不能将ref参数捕获到局部变量,并让它保持ref - 将创建本地副本。

GBL.counter实际上是CallSomethingThatNeedsCounterOf1的隐式隐藏参数。如果你可以把它转换为常规的,宣布的paraemter,你的问题就会消失。此外,如果这会导致许多参数,解决方案将是一对设置和重置环境的方法,以便CallSomethingThatNeedsCounterOf1()可以运行。

您可以创建一个在其构造函数中调用SetUp方法的类,并在Dispose()中创建Reset方法。您可以将此类与using语句一起使用,以使c ++行为近似。但是,您必须为每个方案创建其中一个类。