我在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关键字。
答案 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 ++行为近似。但是,您必须为每个方案创建其中一个类。