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 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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With