views:

76

answers:

2

Hi. I'm trying to optimize the reflection utilization in my code and I was wondering whether it's possible to set multiple properties of the object at once:

Sample usage:

private void SetProperties<T>(List<T> objects, List<Tuple<string, object>> propsAndValues) where T:Task
        {
            Type type = typeof(T);
            var propInfos = propsAndValues.ToDictionary(key => type.GetProperty(key.Item1, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty), elem => elem.Item2);

            objects.AsParallel().ForAll(obj =>
                {
                    obj.SetProps(propInfos);                                  
                });

        }

where

public static void SetProps<T>(this T obj, Dictionary<PropertyInfo, object> propInfos) where T : Task
        {
            foreach (var propInfo in propInfos)
            {
                propInfo.Key.SetValue(obj, propInfo.Value, null);
            }            
        }

I want SetProps extension method to be replaced by something more efficient, but haven't found anything appropriate yet. Thanks in advance ;)

I've modified the code using the links provided in comments. The regular way is still 4 times faster. The question is whether this is the limit or there is a room for improvement?

class DelegateFactory
{
    public delegate void LateBoundPropertySet(object target, object value);

    public delegate void LateBoundPropertyListSet(object target, List<object> values);

    public static LateBoundPropertySet CreateSetIL(PropertyInfo property)
    {
        var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(object), typeof(object) }, true);
        var gen = method.GetILGenerator();

        var sourceType = property.DeclaringType;
        var setter = property.GetSetMethod(true);

        gen.Emit(OpCodes.Ldarg_0); // Load input to stack
        gen.Emit(OpCodes.Castclass, sourceType); // Cast to source type
        gen.Emit(OpCodes.Ldarg_1); // Load value to stack
        gen.Emit(OpCodes.Unbox_Any, property.PropertyType); // Unbox the value to its proper value type
        gen.Emit(OpCodes.Callvirt, setter); // Call the setter method
        gen.Emit(OpCodes.Ret);

        var result = (LateBoundPropertySet)method.CreateDelegate(typeof(LateBoundPropertySet));

        return result;
    }

    public static LateBoundPropertySet CreateSet(PropertyInfo property)
    {

        var setterType = typeof(Action<,>).MakeGenericType(property.DeclaringType, property.PropertyType);

        var propertyWriter = typeof(PropertyWriter<,>).MakeGenericType(property.DeclaringType, property.PropertyType);

        var setterDelegate = Delegate.CreateDelegate(setterType, property.GetSetMethod());

        var writer = (IPropertyWriter)Activator.CreateInstance(propertyWriter, setterDelegate);

        return writer.SetValue;

    }

    private interface IPropertyWriter
    {
        void SetValue(object instance, object value);
    }

    private interface IPropertyListWriter
    {

        void SetValues(object instance, List<object> values);

    }

    private class PropertyWriter<TInstance, TProperty> : IPropertyWriter
    {

        private readonly Action<TInstance, TProperty> _setValueDelegate;

        public PropertyWriter(Action<TInstance, TProperty> setValueDelegate)
        {

            _setValueDelegate = setValueDelegate;

        }

        public void SetValue(object instance, object value)
        {

            _setValueDelegate((TInstance)instance, (TProperty)value);
        }

    }

}

public static void SetProps2<T>(this T obj, Dictionary<DelegateFactory.LateBoundPropertySet, object> propSetters) where T : Task
    {                        
        foreach (var propSet in propSetters)
        {
            propSet.Key.Invoke(propSet, propSet.Value);
        }
    }
+3  A: 

If you are doing enough reflection that this is genuinely a bottleneck, then dynamic code may be worth investigating. Until then - maybe HyperDescriptor will reduce the cost; very similar code, but much cheaper.

In .NET 4.0 and above, the Expression API allows you to set multiple properties, but this is only really viable if you cache the delegate. The other interesting option (with the same constraint: you must cache and re-use the delegate) is DynamicMethod. However, all of these options are fairly advanced topics. I'm happy to advise, but do you really need this?

Marc Gravell
Thanks for the advices.I'm gonna explore these topics.It is a bottleneck in certain cases, so I'm really trying to reduce the cost of it.
Gena Verdel
A: 

As Marc has already told you it is better to use dynamic code to improve the performance.

You can either choose between DynamicMethod approach or Delegate.CreateDelegate with GetSetMethod.

For DynamicMethod you can see an example here: Late-Bound Invocations with DynamicMethod As for CreateDelegate have a look at this question: CreateDelegate with unknown types

Giorgi
Thanks for the useful links.However I haven't found a way to develop the objects presented there into the correspondent lists and the performance improvement was insignificant...
Gena Verdel
@Gena as Marc has said you need to add caching to see a significant performance improvement.
Giorgi
How do I achieve such a thing? Never did it before...
Gena Verdel