Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does C# have a way to mimic Software Transactional Memory, on a small scale?

Tags:

c#

.net

Does C# have a way to temporarily change the value of a variable in a specific scope and revert it back automatically at the end of the scope/block?

For instance (not real code):

bool UpdateOnInput = true;

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

EDIT:

The reason I want the above is because I don't want to do this:

UpdateOnInput = false

//Doing my changes without notifying anyone
Console.WriteLine (UpdateOnInput) // prints false

UpdateOnInput = true
like image 999
Joan Venge Avatar asked Feb 16 '10 18:02

Joan Venge


4 Answers

No, there's no way to do this directly. There are a few different schools of thought on how to do this sort of thing. Compare and contrast these two:

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

vs

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

Many people will tell you that the latter is "safer".

It ain't necessarily so.

The difference between the two is of course the the latter restores the state even if DoSomething() throws an exception. Is that better than keeping the state mutated in an exception scenario? What makes it better? You have an unexpected, unhandled exception reporting that something awful and unexpected has happened. Your internal state could be completely inconsistent and arbitrarily messed up; no one knows what might have been happening at the point of the exception. All we know is that DoSomething probably was trying to do something to the mutated state.

Is it really the right thing to do in the scenario where something terrible and unknown has happened to keep on stirring that particular pot and trying to mutate the state that just caused an exception again?

Sometimes that is going to be the right thing to do, and sometimes its going to make matters worse. Which scenario you're actually in depends on what exactly the code is doing, so think carefully about what the right thing to do is before just blindly choosing one or the other.

Frankly, I would rather solve the problem by not getting into the situation in the first place. Our existing compiler design uses this design pattern, and frankly, it is freakin' irritating. In the existing C# compiler the error reporting mechanism is "side effecting". That is, when part of the compiler gets an error, it calls the error reporting mechanism which then displays the error to the user.

This is a major problem for lambda binding. If you have:

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

then the way this works is we try to bind

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

and

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

and we see which one, if any, gives us an error. In this case, the former gives an error, the latter compiles without error, so this is a legal lambda conversion and overload resolution succeeds. What do we do with the error? We cannot report it to the user because this program is error free!

Therefore what we do when we bind the body of a lambda is exactly what you say: we tell the error reporter "don't report your errors to the user; save them in this buffer over here instead". Then we bind the lambda, restore the error reporter to its earlier state, and look at the contents of the error buffer.

We could avoid this problem entirely by changing the expression analyzer so that it returned the errors along with the result, rather than making errors a state-related side effect. Then the need for mutation of the error reporting state goes away entirely and we don't even have to worry about it.

So I would encourage you to revisit your design. Is there a way you can make the operation you are performing not dependent upon the state you are mutating? If so, then do that, and then you don't need to worry about how to restore the mutated state.

(And of course in our case we do want to restore the state upon an exception. If something inside the compiler throws during lambda binding, we want to be able to report that to the user! We don't want the error reporter to stay in the "suppress reporting errors" state.)

like image 63
Eric Lippert Avatar answered Sep 18 '22 23:09

Eric Lippert


No, but it is pretty simple to just do this:

bool UpdateOnTrue = true;

// ....

bool temp = UpdateOnTrue;
try
{
    UpdateOnTrue = false;
    //  do stuff
}
finally
{
    UpdateOnTrue = temp; 
}
like image 28
Ed S. Avatar answered Sep 21 '22 23:09

Ed S.


Try:

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

Now you can say:

bool flag = false;

WithAssignment(ref flag, true, () =>
{
    // flag is true during this block
});

// flag is false again
like image 23
Daniel Earwicker Avatar answered Sep 18 '22 23:09

Daniel Earwicker


No, you have to do it manually with a try/finally block. I dare say you could write an IDisposable implementation which would do something hacky in conjunction with lambda expressions, but I suspect a try/finally block is simpler (and doesn't abuse the using statement).

like image 44
Jon Skeet Avatar answered Sep 17 '22 23:09

Jon Skeet