为属性setter或getter创建一个高性能的开放委托

开放委托是没有目标的实例方法的委托。 要调用它,您需要提供目标作为其第一个参数。 它们是优化代码的一种聪明方法,否则会使用reflection并且性能较差。 有关开放代表的介绍,请参阅此内容 。 你在实践中使用它的方法是使用昂贵的reflection代码来构建这些开放的委托,但是你可以像一个简单的委托调用那样非常便宜地调用它们。

我正在尝试编写将任意PropertyInfo转换为其setter的委托的代码。 到目前为止,我想出了这个:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace Test { class TestClass { static Action MakeSetterDelegate(PropertyInfo property) { MethodInfo setMethod = property.GetSetMethod(); if (setMethod != null && setMethod.GetParameters().Length == 1) //skips over nasty index properties { //To be able to bind to the delegate we have to create a delegate //type like: Action rather than Action. //We use reflection to do that Type setterGenericType = typeof(Action); Type delegateType = setterGenericType.MakeGenericType(new Type[] { typeof(T), property.PropertyType }); var untypedDelegate = Delegate.CreateDelegate(delegateType, setMethod); //we wrap the Action delegate into an Action Action setter = (instance, value) => { untypedDelegate.DynamicInvoke(new object[] { instance, value }); }; return setter; } else { return null; } } int TestProp { set { System.Diagnostics.Debug.WriteLine("Called set_TestProp"); } } static void Test() { PropertyInfo property = typeof(TestClass).GetProperty("TestProp"); Action setter = MakeSetterDelegate(property); TestClass instance = new TestClass(); setter(instance, 5); } } } 

将为getter编写类似的代码。 它可以工作,但是setter委托使用DynamicInvoke从Action <derivedType >转换为Action <object >,我怀疑它正在吃掉我所追求的优化的很大一部分。 所以问题是:

  1. DynamicInvoke真的是一个真正的问题吗?
  2. 不管怎么说呢?

DynamicInvoke不会创建一个高性能的setter。 反对generics内部类型的reflection在这里是更好的选择,因为这将允许您使用类型化的委托。 另一个选项是DynamicMethod ,但是你需要担心一些IL细节。

可能希望查看HyperDescriptor ,它将IL工作包装到PropertyDescriptor实现中。 另一个选项是Expression API(如果您使用的是.NET 3.5或更高版本):

 static Action MakeSetterDelegate(PropertyInfo property) { MethodInfo setMethod = property.GetSetMethod(); if (setMethod != null && setMethod.GetParameters().Length == 1) { var target = Expression.Parameter(typeof(T)); var value = Expression.Parameter(typeof(object)); var body = Expression.Call(target, setMethod, Expression.Convert(value, property.PropertyType)); return Expression.Lambda>(body, target, value) .Compile(); } else { return null; } } 

或者使用通用类型:

  abstract class Setter { public abstract void Set(T obj, object value); } class Setter : Setter { private readonly Action del; public Setter(MethodInfo method) { del = (Action) Delegate.CreateDelegate(typeof(Action), method); } public override void Set(TTarget obj, object value) { del(obj, (TValue)value); } } static Action MakeSetterDelegate(PropertyInfo property) { MethodInfo setMethod = property.GetSetMethod(); if (setMethod != null && setMethod.GetParameters().Length == 1) { Setter untyped = (Setter) Activator.CreateInstance( typeof(Setter<,>).MakeGenericType(typeof(T), property.PropertyType), setMethod); return untyped.Set; } else { return null; } } 

我曾经上过这堂课。 也许它有帮助:

 public class GetterSetter { private readonly Func getter; private readonly Action setter; private readonly string propertyName; private readonly Expression> propertyNameExpression; public EntityType Entity { get; set; } public GetterSetter(EntityType entity, Expression> property_NameExpression) { Entity = entity; propertyName = GetPropertyName(property_NameExpression); propertyNameExpression = property_NameExpression; //Create Getter getter = propertyNameExpression.Compile(); // Create Setter() MethodInfo method = typeof (EntityType).GetProperty(propertyName).GetSetMethod(); setter = (Action) Delegate.CreateDelegate(typeof(Action), method); } public propType Value { get { return getter(Entity); } set { setter(Entity, value); } } protected string GetPropertyName(LambdaExpression _propertyNameExpression) { var lambda = _propertyNameExpression as LambdaExpression; MemberExpression memberExpression; if (lambda.Body is UnaryExpression) { var unaryExpression = lambda.Body as UnaryExpression; memberExpression = unaryExpression.Operand as MemberExpression; } else { memberExpression = lambda.Body as MemberExpression; } var propertyInfo = memberExpression.Member as PropertyInfo; return propertyInfo.Name; } 

测试:

 var gs = new GetterSetter(new OnOffElement(), item => item.IsOn); gs.Value = true; var result = gs.Value;