Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

set multiple properties simultaneously via reflection

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);
        }
    }
like image 535
Gena Verdel Avatar asked Jul 14 '26 12:07

Gena Verdel


2 Answers

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?

like image 143
Marc Gravell Avatar answered Jul 18 '26 14:07

Marc Gravell


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

like image 29
Giorgi Avatar answered Jul 18 '26 15:07

Giorgi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!