How to set the property value using reflection in C# when the property is a delegate type?

Viewed 1175

I have a property in a dll that i am using using Reflection feature in C#. I would like to set the property and the property type is a delegate type.

Please let me know the steps involved in setting the property using the Reflection.

I know that SetValue method need to be used to do this, but don't know how to set the property of the delegate type.

SetValue(objectName, 1, null) is to set an integer property using reflection, but don't know how to set the property of the delegate type.

How to declare a variable of the delegate property type and initialize it using reflection?

2 Answers

It is rather straightforward

Given a class with a delegate property such as

class A
{
    public Action Action { get; set; }
}

we can set it via reflection like so

void Main()
{
    var a = new A();

    var type = a.GetType();

    var delegateProperty = type.GetProperty(nameof(A.Action));

    delegateProperty.SetValue(a, (Action)(() => Console.WriteLine("via reflection")));

    a.Action();
}

Note that we have to cast a lambda expression to a delegate type, here System.Action because there is no type inference context but that is an unimportant, it is just one of many ways to obtain a value of a delegate type.

That does lead us to the second part of your question however:

How to declare a variable of the delegate property type and initialize it using reflection?

We need to create a delegate value of a type matching the declared property. In this case the property was System.Action.

At the simplest level you set it the same way you set the int property:

Assuming this class:

public class MyClass
{
    internal Action<object> Simple { get; set; }
}

Setting the property:

var type = typeof(MyClass);

var action = new Action<object>((o) => Console.WriteLine("Invoked with {0}", o));
var obj = new MyClass();
type.GetProperty("Simple", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(obj, action);

If the delegate contains a type that is not public as an argument, you can still set it using object, depending on covariance/contravariance.

Assuming the class is as below, setting the property with Action<object> still works

public class MyClass
{
    internal Action<Arg> Simple { get; set; }
}
internal class Arg { }

If the delegate type itself is internal you need to create an instance of that internal delegate type:

Assuming:

public class MyClass
{
    internal Internal Simple { get; set; }
}
internal class Arg { }

internal delegate void Internal(Arg arg);

You can use:

var type = typeof(MyClass);

var prop = type.GetProperty("Simple", BindingFlags.NonPublic | BindingFlags.Instance);
var action = new Action<object>((o) => Console.WriteLine("Invoked with {0}", o));
var delegateInsrtance = Delegate.CreateDelegate(prop.PropertyType, action.Target, action.Method);
var obj = new MyClass();
prop.SetValue(obj, delegateInsrtance);

Note: The above code works, because we created a delegate (Action<object>) that can be compatible with the Internal delegate (the arguments do not need any explicit casting) and used the methodInfo of that delegate to create out new internal delegate. depending on the type of the delegate you want to create this may be trickier, but without more info, this is example should be a good starting point.

Related