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

时间:2010-11-03 09:51:15

标签: c# reflection delegates

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

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Test
{
    class TestClass
    {
        static Action<T, object> MakeSetterDelegate<T>(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<T,actualType> rather than Action<T,object>.
                //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<T,actualType> delegate into an Action<T,object>
                Action<T, object> 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<TestClass, object> setter = MakeSetterDelegate<TestClass>(property);
            TestClass instance = new TestClass();
            setter(instance, 5);
        }
    }
}

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

  1. DynamicInvoke真的是一个真正的问题吗?
  2. 到底有没有?

2 个答案:

答案 0 :(得分:18)

DynamicInvoke不会成为高性能的设定者。对于通用内部类型的反射是更好的选择,因为这将允许您使用类型委托。另一种选择是DynamicMethod,但是你需要担心一些IL细节。

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

static Action<T, object> MakeSetterDelegate<T>(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<Action<T, object>>(body, target, value)
            .Compile();
    }
    else
    {
        return null;
    }
}

或者使用通用类型:

    abstract class Setter<T>
    {
        public abstract void Set(T obj, object value);
    }
    class Setter<TTarget, TValue> : Setter<TTarget>
    {
        private readonly Action<TTarget, TValue> del;
        public Setter(MethodInfo method)
        {
            del = (Action<TTarget, TValue>)
                Delegate.CreateDelegate(typeof(Action<TTarget, TValue>), method);
        }
        public override void Set(TTarget obj, object value)
        {
            del(obj, (TValue)value);
        }

    }
    static Action<T, object> MakeSetterDelegate<T>(PropertyInfo property)
    {
        MethodInfo setMethod = property.GetSetMethod();
        if (setMethod != null && setMethod.GetParameters().Length == 1)
        {
            Setter<T> untyped = (Setter<T>) Activator.CreateInstance(
                typeof(Setter<,>).MakeGenericType(typeof(T),
                property.PropertyType), setMethod);
            return untyped.Set;
        }
        else
        {
            return null;
        }
    }

答案 1 :(得分:1)

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

public class GetterSetter<EntityType,propType>
{
    private readonly Func<EntityType, propType> getter;
    private readonly Action<EntityType, propType> setter;
    private readonly string propertyName;
    private readonly Expression<Func<EntityType, propType>> propertyNameExpression;

    public EntityType Entity { get; set; }

    public GetterSetter(EntityType entity, Expression<Func<EntityType, propType>> 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<EntityType, propType>)
                 Delegate.CreateDelegate(typeof(Action<EntityType, propType>), 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<OnOffElement,bool>(new OnOffElement(), item => item.IsOn);
        gs.Value = true;
        var result = gs.Value;