1

I'm looking for a way to creating a delegate for the setter of a property without knowing the property type, say, instead of having Action<TClass, TType>, I would like Action<TClass, object>. The code which will be calling Action<TClass, object> will always have the right underlying type boxed as object. The intention is to then cache those delegates later.

Please consider the example below.

public class MyClass<T>
{
    public T MyProperty { get; set; }
}

I can set MyProperty as below.

var myClass = new MyClass<int>();
var property = typeof(MyClass<int>).GetProperty("MyProperty");
object intAsObject = 2;

//Option 1. This works, but is slow
property.SetValue(myClass, intAsObject);

//Option 2. This works, but I need to know the type of MyProperty at compile time.
var setter = (Action<MyClass<int>, int>)property.SetMethod.CreateDelegate(typeof(Action<MyClass<int>, int>));
setter(myClass, 5);

//Option 3. This does not work. It throws ArgumentException. Is it possible to achieve something like this?!
var objSetter = (Action<MyClass<int>, object>)property.SetMethod.CreateDelegate(typeof(Action<MyClass<int>, object>));
objSetter(myClass, intAsObject);

How to achieve Option #3? The message withing the ArgumentException thrown is the following: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

Furthermore, is it possible to make it work for any T, i.e., int, double, string, object etc?

I have found interesting answers, such as the following, but can't find a way to achieve what I want. Creating a property setter delegate

Thank you very much in advance.

1 Answers1

1

Thanks to the comment from Iliar Turdushev who pointed the thread here, and thanks also to this blog post here I was able to come up with the below.

public static Delegate CreateSetter(PropertyInfo propertyInfo)
{
    ParameterExpression instance = Expression.Parameter(propertyInfo.ReflectedType, "instance");
    ParameterExpression propertyValue = Expression.Parameter(propertyInfo.PropertyType, "propertyValue");
    var body = Expression.Assign(Expression.Property(instance, propertyInfo.Name), propertyValue);
    return Expression.Lambda(body, instance, propertyValue).Compile();
}

It can then be used this way (still considering the example class in my question).

//The types are known for this example. But in my real scenario only an instance of PropertyInfo will be available.
var propertyInfo = typeof(MyClass<int>).GetProperty(nameof(MyClass<int>.MyProperty));

var deleg = CreateSetter(propertyInfo);
deleg.DynamicInvoke(myClass, 10);

//Prints 10 as expected
Console.WriteLine(myClass.MyProperty);

Edit: just found the below is possible too:

public static Action<object, object> CreateSetter(PropertyInfo propertyInfo)
{
    ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
    UnaryExpression instanceExpresion = Expression.Convert(instance, propertyInfo.DeclaringType);

    ParameterExpression propertyValue = Expression.Parameter(typeof(object), "propertyValue");
    UnaryExpression propertyValueExpression = Expression.Convert(propertyValue, propertyInfo.PropertyType);

    var body = Expression.Assign(Expression.Property(instanceExpresion, propertyInfo.Name), propertyValueExpression);
    return Expression.Lambda<Action<object, object>>(body, instance, propertyValue).Compile();
}

And used this way.

var action = CreateSetter(propertyInfo);
action.Invoke(myClass, 10);