Saturday, May 24, 2008

Using IDisposable and Lambdas for temporary values

What follows is some code I wrote last week that I couldn't find anywhere else and I thought was useful. First, is a test method demonstrating my use case, followed by the implementation of the TemporaryValue classes to fulfil the requirement.

The property get lambda expression can be an instance or static property as long as it is read-write. In this implementation there is no exception handling and no multithreading considerations.

[TestMethod]
public void Original_property_value_should_be_restored_after_temporary_value_block()
{
    const string OriginalValue = "_original_value_";
    const string NewValue = "_new_value_";
    var f = new Foo {Bar = OriginalValue};

    Assert.AreSame(OriginalValue, f.Bar);

    using (TemporaryValue.For(() => f.Bar, NewValue))
    {
        Assert.AreSame(NewValue, f.Bar);
    }

    Assert.AreSame(OriginalValue, f.Bar);
}

public static class TemporaryValue 

    public static TemporaryValue For(Expression<Func> propertyGet, TValue newValue) 
    { 
        return new TemporaryValue(propertyGet, newValue); 
    } 

 
public class TemporaryValue : IDisposable 

    private PropertyInfo _propertyInfo; 
    private object _target; 
    private TValue _original; 
 
    public TemporaryValue(Expression<Func> propertyGet, TValue newValue) 
    { 
        var memberExpression = (MemberExpression)propertyGet.Body; 
        _propertyInfo = (PropertyInfo)memberExpression.Member; 
 
        var targetExpression = memberExpression.Expression
        if (null != targetExpression) 
        { 
            _target = Expression.Lambda<Func<object>>(targetExpression).Compile().Invoke(); 
        } 
 
        _original = propertyGet.Compile().Invoke(); 
        Set(newValue); 
    } 
 
    private void Set(TValue value) 
    { 
        _propertyInfo.SetValue(_target, value, null); 
    } 
 
    public void Dispose() 
    { 
        if (null != _propertyInfo) 
        { 
            Set(_original); 
            _propertyInfo = null
            _target = null
            _original = default(TValue); 
        } 
    } 
}

After learning to manipulate lambda expressions in this way I decided it would be a good idea to use a similar approach to remove the need for string literals in an INotifyPropertyChanged implementation. Unfortunately, while I was offline for a few days, Paul got a head start.