Установить несколько свойств одновременно с помощью отражения
Я пытаюсь оптимизировать использование отражения в своем коде, и мне было интересно, можно ли установить несколько свойств объекта одновременно:
Пример использования:
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);
});
}
где
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);
}
}
Я хочу, чтобы метод расширения SetProps был заменен чем-то более эффективным, но пока не нашел ничего подходящего. Заранее спасибо;)
Я изменил код, используя ссылки в комментариях. Обычный способ все еще в 4 раза быстрее. Вопрос в том, является ли это пределом или есть место для улучшения?
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);
}
}
2 ответа
Если вы достаточно размышляете о том, что это действительно узкое место, динамический код, возможно, стоит изучить. До тех пор - возможно HyperDescriptor
снизит стоимость; очень похожий код, но намного дешевле.
В.NET 4.0 и выше Expression
API позволяет устанавливать несколько свойств, но это действительно реально, только если вы кэшируете делегата. Другой интересный вариант (с тем же ограничением: вы должны кэшировать и повторно использовать делегат) DynamicMethod
, Тем не менее, все эти варианты довольно сложные темы. Я с удовольствием посоветую, но тебе это действительно нужно?
Как уже сказал Марк, для повышения производительности лучше использовать динамический код.
Вы можете выбрать между подходом DynamicMethod или Delegate.CreateDelegate с помощью GetSetMethod.
Для DynamicMethod вы можете увидеть пример здесь: Последние вызовы с DynamicMethod Как и для CreateDelegate, взгляните на этот вопрос: CreateDelegate с неизвестными типами