Tuesday, March 12, 2013

PropertyWrapper

One way to fake indexed properties in C# 

In .NET framework indexed properties is allowed, but not in the C# language.
Whether that's good or bad is debatable, but sometimes they would be very convinient.

When converting a large VB.NET application I encountered a massive amount of indexed properties that I had to fix in some way.

The solution I came up with was to create PropertyWrapper classes.
public class PropertyWrapper<TK, T>
{
    private readonly Func<TK, T> _getMethod;
    private readonly Action<TK, T> _setMethod;

    public PropertyWrapper(Func<TK, T> getMethod,
                           Action<TK, T> setMethod)
    {
        _getMethod = getMethod;
        _setMethod = setMethod;
    }

    public T this[TK key]
    {
        get
        {
            if (_getMethod == null) return default(T);
            return _getMethod(key);
        }
        set
        {
            if (_setMethod == null) return;
            _setMethod(key, value);
        }
    }
}
public class PropertyWrapperRead<TK, T>
{
    private readonly Func<TK, T> _getMethod;

    public PropertyWrapperRead(Func<TK, T> getMethod)
    {
        _getMethod = getMethod;
    }

    public T this[TK key]
    {
        get
        {
            if (_getMethod == null) return default(T);
            return _getMethod(key);
        }
    }
}
And this is how to use it.
public class ClassWithIndexedProperty
{
    private List<Person> _persons = new List<Person>()
        {
            new Person(1,"First"),
            new Person(2,"Second"),
            new Person(4,"Fourth"),
        };

    public PropertyWrapper<int, string> Name
    {
        get
        {
            return new PropertyWrapper<int, string>(
                (key) =>
                    {
                        var person = _persons.FirstOrDefault(p => p.Number == key);
                        return person != null ? person.Name : null;
                    },
                (key, value) =>
                    {
                        var person = _persons.FirstOrDefault(p => p.Number == key);
                        if (person == null)
                        {
                            _persons.Add(new Person(key, value));
                        }
                        else
                        {
                            person.Name = value;
                        }
                    });
        }
    }
}
If properties with more indexes is needed just create more classes with signatures like this...
public class PropertyWrapper<TKey1, TKey2, TResult>
public class PropertyWrapper<TKey1, TKey2, TKey3, TResult>
public class PropertyWrapper<TKey1, TKey2, TKey3, TKey4, TResult>