Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiating Immutable Objects With Reflection

I created a base class to help me reduce boilerplate code of the initialization of the immutable Objects in C#,

I'm using lazy initialization in order to try not to impact performance a lot , I was wondering how much am I affecting the performance by doing this?

This is my base class:

public class ImmutableObject<T>
{
    private readonly Func<IEnumerable<KeyValuePair<string, object>>> initContainer;

    protected ImmutableObject() {}

    protected ImmutableObject(IEnumerable<KeyValuePair<string,object>> properties)
    {
        var fields = GetType().GetFields().Where(f=> f.IsPublic);

        var fieldsAndValues =
            from fieldInfo in fields
            join keyValuePair in properties on fieldInfo.Name.ToLower() equals keyValuePair.Key.ToLower()
            select new  {fieldInfo, keyValuePair.Value};

        fieldsAndValues.ToList().ForEach(fv=> fv.fieldInfo.SetValue(this,fv.Value));

    }

    protected ImmutableObject(Func<IEnumerable<KeyValuePair<string,object>>> init)
    {
        initContainer = init;
    }

    protected T setProperty(string propertyName, object propertyValue, bool lazy = true)
    {

        Func<IEnumerable<KeyValuePair<string, object>>> mergeFunc = delegate
                                                                        {
                                                                            var propertyDict = initContainer == null ? ObjectToDictonary () : initContainer();
                                                                            return propertyDict.Select(p => p.Key == propertyName? new KeyValuePair<string, object>(propertyName, propertyValue) : p).ToList();
                                                                        };

        var containerConstructor = typeof(T).GetConstructors()
            .First( ce => ce.GetParameters().Count() == 1 && ce.GetParameters()[0].ParameterType.Name == "Func`1");

        return (T) (lazy ?  containerConstructor.Invoke(new[] {mergeFunc}) :  DictonaryToObject<T>(mergeFunc()));
    }

    private IEnumerable<KeyValuePair<string,object>> ObjectToDictonary()
    {
        var fields = GetType().GetFields().Where(f=> f.IsPublic);
        return fields.Select(f=> new KeyValuePair<string,object>(f.Name, f.GetValue(this))).ToList();
    }

    private static object DictonaryToObject<T>(IEnumerable<KeyValuePair<string,object>> objectProperties)
    {
        var mainConstructor = typeof (T).GetConstructors()
            .First(c => c.GetParameters().Count()== 1 && c.GetParameters().Any(p => p.ParameterType.Name == "IEnumerable`1") );
        return mainConstructor.Invoke(new[]{objectProperties});
    }

    public T ToObject()
    {
        var properties = initContainer == null ? ObjectToDictonary() : initContainer();
        return (T) DictonaryToObject<T>(properties);
    }
}

Can be implemented like so:

public class State:ImmutableObject<State>
{
    public State(){}
    public State(IEnumerable<KeyValuePair<string,object>> properties):base(properties) {}
    public State(Func<IEnumerable<KeyValuePair<string, object>>> func):base(func) {}

    public readonly int SomeInt;
    public State someInt(int someInt)
    {
        return setProperty("SomeInt", someInt);
    }

    public readonly string SomeString;
    public State someString(string someString)
    {
        return setProperty("SomeString", someString);
    }
}

and can be used like this:

//creating new empty object
var state = new State();

// Set fields, will return an empty object with the "chained methods".
var s2 = state.someInt(3).someString("a string");
// Resolves all the "chained methods" and initialize the object setting all the fields by reflection.
var s3 = s2.ToObject();
like image 749
CountOren Avatar asked Jun 24 '14 19:06

CountOren


2 Answers

As was already mentioned in the comments, it would make more sense, not to "conflate" the immutable instance implementation or interface with the behavior of what is essentially a builder for new instances.

You could make a much cleaner and quite type safe solution that way. So we could define some marker interfaces and type safe versions thereof:

public interface IImmutable : ICloneable { }
public interface IImmutableBuilder { }

public interface IImmutableOf<T> : IImmutable where T : class, IImmutable 
{
    IImmutableBuilderFor<T> Mutate();
}

public interface IImmutableBuilderFor<T> : IImmutableBuilder where T : class, IImmutable
{
    T Source { get; }
    IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, TFieldType value);
    IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, Func<T, TFieldType> valueProvider);
    IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, TFieldType value);
    IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, Func<TFieldType, TFieldType> valueProvider);
    T Build();
}

And provide all the required basic builder behavior in a class like below. Note that most error checking/compiled delegate creation is omitted for the sake of brevity/simplicity. A cleaner, performance optimized version with a reasonable level of error checking can be found in this gist.

public class DefaultBuilderFor<T> : IImmutableBuilderFor<T> where T : class, IImmutableOf<T>
{
    private static readonly IDictionary<string, Tuple<Type, Action<T, object>>> _setters;
    private List<Action<T>> _mutations = new List<Action<T>>();

    static DefaultBuilderFor()
    {
        _setters = GetFieldSetters();
    }

    public DefaultBuilderFor(T instance)
    {
        Source = instance;
    }

    public T Source { get; private set; }

    public IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, TFieldType value)
    {
        // Notes: error checking omitted & add what to do if `TFieldType` is not "correct".
        _mutations.Add(inst => _setters[fieldName].Item2(inst, value));
        return this;
    }

    public IImmutableBuilderFor<T> Set<TFieldType>(string fieldName, Func<T, TFieldType> valueProvider)
    {
        // Notes: error checking omitted & add what to do if `TFieldType` is not "correct".
        _mutations.Add(inst => _setters[fieldName].Item2(inst, valueProvider(inst)));
        return this;
    }

    public IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, TFieldType value)
    {
        // Error checking omitted.
        var memberExpression = fieldExpression.Body as MemberExpression;
        return Set<TFieldType>(memberExpression.Member.Name, value);
    }

    public IImmutableBuilderFor<T> Set<TFieldType>(Expression<Func<T, TFieldType>> fieldExpression, Func<TFieldType, TFieldType> valueProvider)
    {
        // Error checking omitted.
        var memberExpression = fieldExpression.Body as MemberExpression;
        var getter = fieldExpression.Compile();
        return Set<TFieldType>(memberExpression.Member.Name, inst => valueProvider(getter(inst)));
    }

    public T Build()
    {
        var result = (T)Source.Clone();
        _mutations.ForEach(x => x(result));
        return result;
    }

    private static IDictionary<string, Tuple<Type, Action<T, object>>> GetFieldSetters()
    {
        // Note: can be optimized using delegate setter creation (IL). 
        return typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)
            .Where(x => !x.IsLiteral)
            .ToDictionary(
                x => x.Name,
                x => SetterEntry(x.FieldType, (inst, val) => x.SetValue(inst, val)));
    }

    private static Tuple<Type, Action<T, object>> SetterEntry(Type type, Action<T, object> setter)
    {
        return Tuple.Create(type, setter);
    }
}

Example usage

This could then be used like this, using your example class of State:

public static class Example
{
    public class State : IImmutableOf<State>
    {
        public State(int someInt, string someString)
        {
            SomeInt = someInt;
            SomeString = someString;
        }

        public readonly int SomeInt;
        public readonly string SomeString;

        public IImmutableBuilderFor<State> Mutate()
        {
            return new DefaultBuilderFor<State>(this);
        }

        public object Clone()
        {
            return base.MemberwiseClone();
        }

        public override string ToString()
        {
            return string.Format("{0}, {1}", SomeInt, SomeString);
        }
    }

    public static void Run()
    {
        var original = new State(10, "initial");

        var mutatedInstance = original.Mutate()
            .Set("SomeInt", 45)
            .Set(x => x.SomeString, "Hello SO")
            .Build();
        Console.WriteLine(mutatedInstance);

        mutatedInstance = original.Mutate()
            .Set(x => x.SomeInt, val => val + 10)
            .Build();
        Console.WriteLine(mutatedInstance);
    }
}

With the following output:

45, Hello SO
20, initial
like image 164
Alex Avatar answered Sep 19 '22 03:09

Alex


Well to answer your question about performance, reflection is very expensive (relatively speaking). I would not use your design if it's in performance critical code.

When it comes to generics and reflection the performance hit can often be surprisingly large. Consider even something as simple as this:

public class Builder<T> where T : new()
{
    public T Build()
    {
        return new T();
    }
}

What this is actually doing is calling Activator.CreateInstance which uses reflection and it's extremely expensive.

If I wanted to optimize code like the above case I would use dynamic methods. And the performance difference between the two would be drastic.

Of course, keep in mind we're entering the zone of advanced code that's more complex and harder to read for the sake of performance. You could consider this overly optimized and overkill in code that isn't performance critical.

But in code that I write I avoid reflection like the plague.

like image 27
Zer0 Avatar answered Sep 17 '22 03:09

Zer0