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);
}
}
If you are doing enough reflection that this is genuinely a bottleneck, then dynamic code may be worth investigating. Until then – maybe
HyperDescriptorwill reduce the cost; very similar code, but much cheaper.In .NET 4.0 and above, the
ExpressionAPI 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) isDynamicMethod. However, all of these options are fairly advanced topics. I’m happy to advise, but do you really need this?