代码之家  ›  专栏  ›  技术社区  ›  Joan Venge

C#是否有办法在小规模上模拟软件事务性内存?

  •  5
  • Joan Venge  · 技术社区  · 15 年前

    C#是否有办法临时更改特定范围内变量的值,并在范围/块结束时自动恢复?

    例如(非真实代码):

    bool UpdateOnInput = true;
    
    using (UpdateOnInput = false)
    {
        //Doing my changes without notifying anyone
        Console.WriteLine (UpdateOnInput) // prints false
    }
    //UpdateOnInput is true again.
    

    我想要上述内容的原因是因为我不想这样做:

    UpdateOnInput = false
    
    //Doing my changes without notifying anyone
    Console.WriteLine (UpdateOnInput) // prints false
    
    UpdateOnInput = true
    
    8 回复  |  直到 15 年前
        1
  •  14
  •   Eric Lippert    15 年前

    不,没有办法直接做到这一点。关于如何做这类事情,有几种不同的思想流派。比较和对比这两个方面:

    originalState = GetState();
    SetState(newState);
    DoSomething();
    SetState(originalState);
    

    vs

    originalState = GetState();
    SetState(newState);
    try
    {
        DoSomething();
    }
    finally
    {
        SetState(originalState);
    }
    

    不一定如此。

    两者之间的区别当然是后者恢复状态,即使DoSomething()引发异常。是吗 更好的 是什么让它变得更好? 您有一个未经处理的意外异常报告 想不到的 已经发生了。你的内在状态可能是完全不一致的和任意的混乱;没有人知道发生异常时可能发生了什么。我们只知道DoSomething可能是想对变异状态做点什么。

    再一次 ?

    有时候这是正确的做法,有时候这会让事情变得更糟。您实际处于哪种情况取决于代码到底在做什么,所以在盲目选择其中一种之前,请仔细考虑正确的做法。

    坦率地说,我宁愿一开始就不介入这种情况来解决问题。我们现有的编译器设计使用这种设计模式,坦率地说,这是非常令人恼火的。在现有的C#编译器中,错误报告机制是“副作用”的。也就是说,当部分编译器收到错误时,它调用错误报告机制,然后向用户显示错误。

    这是lambda绑定的一个主要问题。如果您有:

    void M(Func<int, int> f) {}
    void M(Func<string, int> f) {}
    ...
    M(x=>x.Length);
    

    这样做的方式是我们尝试绑定

    M((int x)=>{return x.Length;});
    

    M((string x)=>{return x.Length;});
    

    我们看哪一个,如果有的话,给我们一个错误。在这种情况下,前者给出一个错误,后者编译时没有错误,因此这是合法的lambda转换,重载解析成功。 我们如何处理错误?

    因此,当我们绑定lambda的主体时,我们所做的正是您所说的:我们告诉错误报告者“不要向用户报告您的错误;将它们保存在这里的缓冲区中”。然后我们绑定lambda,将error reporter恢复到其早期状态,并查看错误缓冲区的内容。

    我们可以通过更改表达式分析器来完全避免这个问题,这样它就可以随结果一起返回错误,而不是使错误成为与状态相关的副作用。然后,对错误报告状态突变的需求就完全消失了,我们甚至不必担心它。

    (当然,在我们的案例中 希望在出现异常时恢复状态。如果编译器内部在lambda绑定期间抛出了一些东西,我们希望能够向用户报告!我们不希望错误报告程序处于“抑制报告错误”状态。)

        2
  •  12
  •   Ed Swangren    15 年前

    不,但这样做很简单:

    bool UpdateOnTrue = true;
    
    // ....
    
    bool temp = UpdateOnTrue;
    try
    {
        UpdateOnTrue = false;
        //  do stuff
    }
    finally
    {
        UpdateOnTrue = temp; 
    }
    
        3
  •  6
  •   Daniel Earwicker    15 年前

    尝试:

    public void WithAssignment<T>(ref T var, T val, Action action)
    {
        T original = var;
        var = val;
        try
        {
            action();
        }
        finally
        {
            var = original;
        }
    }
    

    现在你可以说:

    bool flag = false;
    
    WithAssignment(ref flag, true, () =>
    {
        // flag is true during this block
    });
    
    // flag is false again
    
        4
  •  5
  •   Community CDub    8 年前

    不,您必须使用try/finally块手动执行此操作。我敢说你 写一篇 IDisposable abuse the using statement ).

        5
  •  4
  •   kervin    15 年前

    听起来你真的很想

    Stack<bool>
    
        6
  •  1
  •   Arsen Mkrtchyan    15 年前

    不,没有标准的方法,您应该手动实现它。 Generic implementation of IEditableObject via TypeDescriptor and Reflection 我能帮上忙吗

        7
  •  1
  •   Ian Jacobs    15 年前

    罐装。。。我对此表示怀疑。假设您的示例是临时布尔值的简单使用,我假设您脑子里有一些古怪的想法:-)您可以实现某种堆栈结构:

    1) 将旧值推送到堆栈上

    2) 加载新值

    4) 从堆栈中弹出并替换已使用的值。

    粗略(又名未测试)示例(现在无法查找堆栈语法)

    bool CurrentValue = true;
    Stack<bool> Storage= new Stack<bool>
    
    Storage.Push(CurrentValue);
    CurrentValue=false;
    DoStuff();
    CurrentValue = Storage.Pop();  
    //Continue  
    
        8
  •  1
  •   RossFabricant    15 年前

    bool b = GetSomeValue();
    DoSomething(ModifyValue(b));
    //b still has the original value. 
    

    要使其适用于引用类型,您需要在处理它之前复制它:

    ICloneable obj = GetSomeValue();
    DoSomething(ModifyValue(obj.Clone()));
    //obj still has the original value. 
    

    推荐文章