Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot bind to the target method when creating delegates for properties

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

Code:

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

However I get the following exception:

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

From what I have read this will be caused by static/indexed/value type properties, the Properties collection contains no static or indexed properties but I do however need this to work for value type properties such as int and double.

How can I create the getters/setters that I need while keeping my code abstract and avoiding generics?

like image 514
Alex Hope O'Connor Avatar asked Jul 02 '13 05:07

Alex Hope O'Connor


1 Answers

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!

like image 129
Alex Hope O'Connor Avatar answered Nov 15 '22 21:11

Alex Hope O'Connor