Cannot bind to the target method when creating delegates for properties

Alex Hope O'Connor picture Alex Hope O'Connor · Jul 2, 2013 · Viewed 17.2k times · Source

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?

Answer

Alex Hope O&#39;Connor picture Alex Hope O'Connor · Jul 3, 2013

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!