Cannot bind to the target method when creating delegates for properties

前端 未结 2 1902
灰色年华
灰色年华 2020-12-16 04:27

Trying to create two dictionaries of emitted delegates to allow for improved performance when dynamically getting/setting the values of properties.

Code:

<         


        
相关标签:
2条回答
  • 2020-12-16 04:39

    I was getting same error. I used Expressions API to fix this issue.

    Note : Method to be referenced is

    • not generic.
    • is static.

    Delegate name is Formula and its signature is as follows

    public delegate float Formula(Dictionary<string, float> cr, 
                                  List<Dictionary<string, float>> allr);
    
    1. Get MethodInfo which is to be referenced as Delegate

      Assembly assembly = results.CompiledAssembly;
      var generatedType = assembly.GetType("First.NewClass");
      var generatedMethod = generatedType.GetMethod("FormulaMethod");
      
    2. Prepare arguments of the Delegate as Parameter Expression. Argument 1 : Dictionary<string, float> Argument 2 : List<Dictionary<string, float>>

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

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

    3. Generate final method Call Expression and return Delegate.

      var methodCall = Expression.Call(generatedMethod,
                                      arg1Expression,
                                      arg2Expression);  
      
      return Expression.Lambda <Formula> (methodCall, 
                                          arg1Expression, 
                                          arg2Expression).Compile();
      
    0 讨论(0)
  • 2020-12-16 05:05

    Ok ended up finding my answer from this question: MethodInfo.Invoke performance issue

    More specifically this article: Making reflection fly and exploring delegates

    Here is the jist of the code that I ended up with:

    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));
        }
    }
    

    The generated delegates on average seem to be 80% faster than using reflection, so I am happy with the result!

    0 讨论(0)
提交回复
热议问题