среда, 15 декабря 2010 г.

Object Reflection Helper

Please, be polite to this article, because it's only example and experiment, for production ready solution we have to measure different velocity and productivity values.

I have created helper, that cache object properties retrieved with reflection. Also helper provide wrapped methods for setting and getting values of properties, that arranged in dictionary with property name as key.
Let's look on ReflectionUtil class, it really do caching job on static level:

public static class ReflectionUtil<T>
{
    static ReflectionUtil()
    {
        Properties = typeof (T).GetProperties();
        GetProperties = Properties.Select(_ => new
                                                         {
                                                             Func = GetGetFunc(_), 
                                                             _.Name,
                                                         })
            .Where(_ => _.Func != null)
            .ToDictionary(_ => _.Name, _ => _.Func);
        
        SetProperties = Properties.Select(_ => new
                                                         {
                                                             Func = GetSetFunc(_), 
                                                             _.Name,
                                                         })
            .Where(_ => _.Func != null)
            .ToDictionary(_ => _.Name, _ => _.Func);
    }


    public static Func<T, object> GetGetFunc(PropertyInfo propertyInfo)
    {
        MethodInfo methodInfo = propertyInfo.GetGetMethod();
        if (methodInfo != null)
        {
            return  _ => methodInfo.Invoke(_, ArrayUtil.Empty<object>());
        }

        return null;
    }

    public static Func<T, object, T> GetSetFunc(PropertyInfo propertyInfo)
    {
        MethodInfo methodInfo = propertyInfo.GetSetMethod();
        if (methodInfo != null)
        {
            return delegate(T _, object val)
                       {
                           methodInfo.Invoke(_, new[] {val});
                           return _;
                       };
        }

        return null;
    }

    public static Dictionary<string, Func<T, object>> GetProperties { get; private set; }

    public static Dictionary<string, Func<T, object, T>> SetProperties { get; private set; }

    public static PropertyInfo[] Properties { get; private set; }
}

One note about code, I extensively use great OSS Lokad Shared Library and class ArrayUtil is from it.
Using this caching can give us some small worth of speeding up iterating over properties of common object and give the ability to lookup through properties using dictionary.

This code snippet help to integrate our reflection utility helper to object pipeline using extension methods syntax.
public static class ObjectReflectionExtension
{
    public static Dictionary<string, Func<T, object>> GetProperties<T>(this T value)
    {
        return ReflectionUtil<T>.GetProperties;
    }

    public static Dictionary<string, Func<T, object, T>> SetProperties<T>(this T value)
    {
        return ReflectionUtil<T>.SetProperties;
    }

    public static PropertyInfo[] Properties<T>(this T value)
    {
        return ReflectionUtil<T>.Properties;
    }
}

This helper I will use in the next article with real world code examples. We can extend ReflectionUtil class for caching another .NET members of object, e.g. methods, events, etc...
Quick example to show how typically we can use helper class and get properties from cache storage. First, we retrieve getters and setters from source object fromObj and destination object toObj:

var getProperties = fromObj.GetProperties();
var setProperties = toObj.SetProperties();

Then, we join different types of accessors by name using simple LINQ expression:

var map = from getProp in getProperties
          join setProp in setProperties on getProp.Key equals setProp.Key
          select new { From = getProp.Value, To = setProp.Value };

In the last expression we simply copied all properties values from source object to destination object, of course those objects have to have properties with the same names, if you would like to see any result. In such manner we can implement very simple shallow clone pattern, because of his reflection nature it can be used for any object type.

map.ForEach(
            _ => _.To(toObj, _.From(fromObj) )
            );

As usually when we using reflection, it is a big question about performance of that type operations, right now we left benchmark out of are of this article, in the next article I will show how to try to accomplish simple object mapper with the help of ReflectionUtil helper, and how to control object mapping in narrow places.
It will be a set of articles, where I will experiment around different aspects of technical implementation CQRS concept in enterprise software. I find interesting article by David Ebbo about interesting solution using the latest .NET 4.0 dynamic object features.
Find enough clever things to say, and you're a Prime Minister; write them down and you're a Shakespeare.
George Bernard Shaw

0 коммент.:

Отправить комментарий