Temporarily change a variable's value

蹲街弑〆低调 提交于 2019-12-05 00:11:05

问题


In an API implementation I'm currently hacking on, there's the need to repeatedly change some variables temporarily, before doing some task, and change them back after the task is done to whatever they were before.

The current code looks like this:

var _oldValue = _variable;
_variable = tempValue;
try
{
  doIt();
}
finally
{
  _variable = oldValue;
}

Doing this a lot is annoying, ugly, hard to maintain, and buries the actual algorithms under a lot of clutter that's just implementation artifacts.

In C++, I would make a class which stores the old value somewhere during construction, and restores it in its destructor:

{
  temp_value tmp(variable_, temp_val);
  do_it();
}

When trying to do something similar in C# I failed because apparently C# can't store references to other objects in classes.

So what would I have to do in C# to remove that clutter?

P.S.: Feel free to add any other tags you see fit. I failed to come up with any.


回答1:


Why not create a method that does it for you, and then pass a Lamda to it?

private void SaveGlobalsAndDoSomething(Action doit)
{
    var _oldValue = _variable;
    _variable = tempValue;
    try
    {
        doit();
    }
    finally
    {
        _variable = _oldValue;
    }
}

And to use it:

SaveGlobalsAndDoSomething(() => { DoSomething(); });

Edit in response to comment:

That doit sometimes returns a value isn't a problem. We're not passing DoSomething to the method. We're passing { DoSomething(); } to the method. So you can easily write:

int returnValue;
SaveGlobalsAndDoSomething(() => { returnValue = DoSomething(); });



回答2:


While I agree with Eric Lippert on ideal solution there are cases when on forced to temporay change state of varable and execute some actions. I.e. there are several examples of such requirement in SharePoint object model, so it is not possible to redesign code to avoid it.

Below is code that can be used to temporary cahnge value and restor it with using statement. Use of using for such non-release-unmanged-resources purposes is contentios, so use you judgment if such approach works for you:

Usage sample:

using(TemporaryChange(true, myValue, v => myValue = v))
{
 // code to run while "myValue" is changed to "true"
}

Class:

class TemporaryChange<V> : IDisposable
{
    private V original;
    private Action<V> setValue;

    internal TemporaryChange(V value, V currentValue, Action<V> setValue)
    {
        this.setValue = setValue;
        this.original = currentValue;
        this.setValue(value);
    }

    void IDisposable.Dispose()
    {
        this.setValue(this.original);
    }
}



回答3:


The fact that you are considering doing a variety of horrible, horrible things to solve this problem is indicative that you shouldn't be in this situation in the first place. If you're in a situation where code depends on mutating and then unmutating state then you have a bad design. Fix the real design problem rather than trying to come up with a clever way to continue using the bad architecture.

What I do when I'm in this situation is clone my state. Suppose you are doing this:

class Frobber
{
    State state;
    ...
    void M()
    {
         ...
         try
         {
             oldstate = state;
             state = newstate;
             this.DoIt();
         }
         finally
         {
             state = oldstate;
         }
    }

Instead do this:

class Frobber
{
    State state;
    ...
    void M()
    {
         ...
         Frobber newFrobber = new Frobber(newstate);
         newFrobber.DoIt();
         ...

Instead of mutating a variable and changing it back, make a whole new variable. Throw away the new variable when you're done with it. The old variable doesn't need to be mutated back because it never changed.



来源:https://stackoverflow.com/questions/5464838/temporarily-change-a-variables-value

标签

工具导航Map