如何在C#中实现局部变量重置?

4

我经常看到并且在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个回答

3
在C#中实现这个的第一个挑战是处理非确定性销毁。由于C#没有析构函数,因此需要一种机制来控制范围以执行重置。IDisposable可以帮助解决这个问题,using语句将模仿C++的确定性销毁语义。
第二个挑战是获取要重置的值而不使用指针。Lambda表达式和委托可以做到这一点。
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 实际上,C#中的RAII完全依赖于IDisposableusing - Stephen Cleary
谢谢你的帮助!这也是我想到的解决方案。我只是希望有一些更简洁或开销更小的东西。我的版本跳过了getter lambda,直接通过值传递了counter,因为在这里那个lambda没有任何有用的作用。 - Collin Arnold

1

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

确实,将其从ref更改为普通值参数将实现此目标!


0

我认为你无法将ref参数捕获到本地变量中,并使其保持ref状态 - 会创建一个本地副本。

GBL.counter实际上是CallSomethingThatNeedsCounterOf1的隐式隐藏参数。如果您可以将其转换为常规声明的参数,则问题将消失。此外,如果这会导致参数过多,则解决方案将是一对方法,用于设置和重置环境,以便CallSomethingThatNeedsCounterOf1()可以运行。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接