BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Reflection
.NET 3.0+

Raising Events Using Reflection

Sometimes it is necessary to raise an object's events using reflection. This can be achieved by obtaining the event's multicast delegate and invoking each of its child delegates in turn.

Events

Events are commonly used in .NET applications, as they provide a handy way for objects to communicate in a similar manner to that of the Observer design pattern. One class defines an event that its instances can raise. Other objects subscribe to the event and react when the event is raised. This means that the object that raises the event need not be aware of the objects that it is calling.

In some cases it is useful to raise an object's events via reflection. For example, you might develop a plug-in architecture where third parties can create objects that are called by your application. You might specify that plug-ins have a particular event that you can raise by name, even though you will not know the type of the object being manipulated.

To demonstrate how to raise events using reflection, create a new console application. Once the project is ready, open the automatically generated class and add a using directive to simplify access to the System.Reflection namespace:

using System.Reflection;

Add two new classes to the project with the code shown below. These two classes are unrelated but both include a Roar event.

public class Tiger
{
    public string Name { get; set; }
    public event EventHandler Roar;

    public override string ToString()
    {
        return Name;
    }
}

public class River
{
    public string Description { get; set; }
    public event EventHandler Roar;

    public override string ToString()
    {
        return Description;
    }
}

Update the Main method, as shown below. This creates an instance of each of the above classes and subscribes to their events. The River object has its event subscribed twice to show what happens when an event with multiple subscribers is raised via reflection.

static void Main(string[] args)
{
    var tiger = new Tiger { Name = "Tigger" };
    tiger.Roar += Roared;

    var river = new River { Description = "Amazon" };
    river.Roar += Roared;
    river.Roar += RoaredAgain;
}

To complete the basic program, add the two event handler methods:

static void Roared(object sender, EventArgs e)
{
    Console.WriteLine(sender + " roared");
}

static void RoaredAgain(object sender, EventArgs e)
{
    Console.WriteLine(sender + " roared again");
}

Raising Events Using Reflection

To raise events via reflection, you don't reflect the event directly. Instead, you use reflection to obtain the multicast delegate that holds all of the subscription information. The subscriptions are simple delegates that you can then call within a loop.

We'll raise the events in a new method to separate the reflection elements from the other code. Update the Main method and add a new, empty method, as follows:

static void Main(string[] args)
{
    var tiger = new Tiger { Name = "Tigger" };
    tiger.Roar += Roared;

    var river = new River { Description = "Amazon" };
    river.Roar += Roared;
    river.Roar += RoaredAgain;

    RoarViaReflection(tiger);
    RoarViaReflection(river);
}

static void RoarViaReflection(object roarer)
{
}

The first step in order to imitate raising an event using reflection is to get the type of the object that defines the event. We can do this using the GetType method, which returns on object of the type, Type.

Add the following line to the RoarViaReflection method:

Type type = roarer.GetType();

The second step is to obtain the field that holds the multicast delegate. This is a private field that has the same name as the event. You can reflect it using the GetField method of the Type object.

Add the following code as the next line in the method. This obtains the Roar field, using binding flags that allow private fields to be reflected and that specify that the target is an instance field, not static.

var field = type.GetField("Roar", BindingFlags.NonPublic | BindingFlags.Instance);

Once you have the field information, you can use GetValue to retrieve the field's value from an instance. Add the next line to obtain the value from the roarer parameter's variable. The value is cast to a MulticastDelegate, as this is the container type for the subscription delegates.

var eventDelegate = field.GetValue(roarer) as MulticastDelegate;

Finally, you can obtain the individual subscriber delegates using the GetInvocationList method of the multicast delegate. You can then invoke the methods to mimic raising the event. As we've used standard event handlers, we need to invoke the methods and pass the source object and an event arguments object. The type for the event arguments is dependent upon the type of event that you wish to raise. In this case we'll just pass EventArgs.Empty to indicate that there is no additional information for the event.

Add the following code to the method. Note that the first line checks to see if the multicast delegate is null. This will be the case if the field does not represent an event or there are no subscribers.

if (eventDelegate != null)
{
    foreach (var eventHandler in eventDelegate.GetInvocationList())
    {
        eventHandler.Method.Invoke(
        eventHandler.Target,
        new object[] { roarer, EventArgs.Empty });
    }
}

Run the program to see the results. You will see that both events are raised. The River object has two subscribers, so generates two messages.

Tigger roared
Amazon roared
Amazon roared again
25 February 2016