9

尝试创建两个已发出委托的字典,以便在动态获取/设置属性值时提高性能。

代码:

  Properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where(p => p.CanRead && !p.GetIndexParameters().Any())
                    .AsEnumerable();
  PropertyGetters = Properties.ToDictionary(p => p.Name, p => (Func<object, object>)Delegate.CreateDelegate(typeof(Func<object, object>), p.GetGetMethod()));
  PropertySetters = Properties.Where(p => p.GetSetMethod() != null)
                    .ToDictionary(p => p.Name, p => (Action<object, object>)Delegate.CreateDelegate(typeof(Action<object, object>), p.GetSetMethod()));

但是我得到以下异常:

无法绑定到目标方法,因为其签名或安全透明度与委托类型的不兼容。

根据我的阅读,这将是由静态/索引/值类型属性引起的,该Properties集合不包含静态或索引属性,但我确实需要它来处理值类型属性,例如intand double

如何在保持代码抽象并避免泛型的同时创建所需的 getter/setter?

4

2 回答 2

10

好的,最终从这个问题中找到了我的答案: MethodInfo.Invoke 性能问题

更具体地说,这篇文章: 让反射飞起来并探索代表

这是我最终得到的代码的 jist:

public class Helper
{
    private IDictionary<string, Func<object, object>> PropertyGetters { get; set; }

    private IDictionary<string, Action<object, object>> PropertySetters { get; set; }

    public static Func<object, object> CreateGetter(PropertyInfo property)
    {
        if (property == null)
            throw new ArgumentNullException("property");

        var getter = property.GetGetMethod();
        if (getter == null)
            throw new ArgumentException("The specified property does not have a public accessor.");

        var genericMethod = typeof(Helper).GetMethod("CreateGetterGeneric");
        MethodInfo genericHelper = genericMethod.MakeGenericMethod(property.DeclaringType, property.PropertyType);
        return (Func<object, object>)genericHelper.Invoke(null, new object[] { getter });
    }

    public static Func<object, object> CreateGetterGeneric<T, R>(MethodInfo getter) where T : class
    {
        Func<T, R> getterTypedDelegate = (Func<T, R>)Delegate.CreateDelegate(typeof(Func<T, R>), getter);
        Func<object, object> getterDelegate = (Func<object, object>)((object instance) => getterTypedDelegate((T)instance));
        return getterDelegate;
    }

    public static Action<object, object> CreateSetter(PropertyInfo property)
    {
        if (property == null)
            throw new ArgumentNullException("property");

        var setter = property.GetSetMethod();
        if (setter == null)
            throw new ArgumentException("The specified property does not have a public setter.");

        var genericMethod = typeof(Helper).GetMethod("CreateSetterGeneric");
        MethodInfo genericHelper = genericMethod.MakeGenericMethod(property.DeclaringType, property.PropertyType);
        return (Action<object, object>)genericHelper.Invoke(null, new object[] { setter });
    }

    public static Action<object, object> CreateSetterGeneric<T, V>(MethodInfo setter) where T : class
    {
        Action<T, V> setterTypedDelegate = (Action<T, V>)Delegate.CreateDelegate(typeof(Action<T, V>), setter);
        Action<object, object> setterDelegate = (Action<object, object>)((object instance, object value) => { setterTypedDelegate((T)instance, (V)value); });
        return setterDelegate;
    }

    public Helper(Type type)
    {
        var Properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where(p => p.CanRead && !p.GetIndexParameters().Any()).AsEnumerable();
        PropertyGetters = Properties.ToDictionary(p => p.Name, p => CreateGetter(p));
        PropertySetters = Properties.Where(p => p.GetSetMethod() != null)
            .ToDictionary(p => p.Name, p => CreateSetter(p));
    }
}

平均而言,生成的代表似乎比使用反射快 80%,所以我对结果很满意!

于 2013-07-03T05:38:55.533 回答
-1

我遇到了同样的错误。我使用Expressions API来解决这个问题。

注意:要引用的方法是

  • 不是通用的。
  • 是静态的。

代表名称为公式,其签名如下

public delegate float Formula(Dictionary<string, float> cr, 
                              List<Dictionary<string, float>> allr);
  1. 获取作为 Delegate 引用的 MethodInfo

    Assembly assembly = results.CompiledAssembly;
    var generatedType = assembly.GetType("First.NewClass");
    var generatedMethod = generatedType.GetMethod("FormulaMethod");
    
  2. 将委托的参数准备为参数表达式。论点 1:Dictionary<string, float> 论点 2:List<Dictionary<string, float>>

    var arg1Expression = Expression.Parameter(typeof(Dictionary<string, float>));
    

    var arg2Expression = Expression.Parameter(typeof(List>));

  3. 生成最终方法调用表达式并返回委托。

    var methodCall = Expression.Call(generatedMethod,
                                    arg1Expression,
                                    arg2Expression);  
    
    return Expression.Lambda <Formula> (methodCall, 
                                        arg1Expression, 
                                        arg2Expression).Compile();
    
于 2016-03-01T10:30:39.030 回答