.Net generate generic methods

2019-09-04 16:56发布

I have a bunch of generic events I want to subscribe to and make them all call one non-generic method. Here's my code:

public delegate void PropertyChangedDelegate<OwnerType, PropertyType>(OwnerType sender, PropertyType oldValue, PropertyType newValue);

public class EventObject
{
    public event PropertyChangedDelegate<Object, Boolean> PropertyChanged;
                public event PropertyChangedDelegate<Object, Int32> XChanged;
}

static void Main()
{
    EventObject eventObject = new EventObject();
    EventInfo eventInfo = eventObject.GetType().GetEvent("PropertyChanged");
    eventInfo.AddEventHandler(eventObject, PropertyChanged);
}

    static void PropertyChanged(Object obj, Object oldValue, Object newValue)
    {
    }

Obviously this doesn't work, is there any way to do a wrapper generic method?

标签: c# generics
2条回答
干净又极端
2楼-- · 2019-09-04 17:20

You can't pass a method group as such in place of a delegate. You can specify the exact delegate your method matches to, like this:

EventObject eventObject = new EventObject();
EventInfo eventInfo = eventObject.GetType().GetEvent("PropertyChanged");
eventInfo.AddEventHandler(eventObject, (Action<Object, Object, Object>)PropertyChanged);

But it still would give you runtime exception since the signatures don't match.

Why not the straightforward += implementation?

EventObject eventObject = new EventObject();
eventObject.PropertyChanged += PropertyChanged;

But this wont compile because of type mismatch in signature. You have to either change signature of delegate

public delegate void PropertyChangedDelegate(Object sender, Object oldValue, Object newValue);

or change signature of event

public event PropertyChangedDelegate<Object, Object> PropertyChanged;

(but that spoils all your efforts to have a strongly typed delegate) or change signature of method

static void PropertyChanged(Object obj, Boolean oldValue, Boolean newValue)
{
}

which is what I would go with.

查看更多
ゆ 、 Hurt°
3楼-- · 2019-09-04 17:28

The issue is that PropertyChanged method is not contravariant to the PropertyChangedDelegate type because sending bool as object require boxing, so it is clear that you cannot make delegate to work universally with all events. The solution is to write a static method as a "landing method". Here is my solution:

using System;
using System.Reflection;

public delegate void PropertyChangedDelegate<OwnerType, PropertyType>(OwnerType sender, PropertyType oldValue, PropertyType newValue);

public class EventObject
{
    public event PropertyChangedDelegate<Object, Boolean> PropertyChanged;
    public event PropertyChangedDelegate<Object, Int32> XChanged;

    public void RaisePropertyChanged() {
        PropertyChanged(this, true, false);
    }
}

class Test {

    static void Main()
    {
        EventObject eventObject = new EventObject();
        EventInfo eventInfo = eventObject.GetType().GetEvent("PropertyChanged"); 
        Type evType = eventInfo.EventHandlerType;
        // replace below with this.GetType() in case of instance method
        Type thisType = typeof(Test); 
        MethodInfo mi = thisType.GetMethod("PropertyChanged", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
        MethodInfo genericMi = mi.MakeGenericMethod(evType.GetGenericArguments());
        Delegate del = Delegate.CreateDelegate(evType, genericMi);
        eventInfo.AddEventHandler(eventObject, del);
        // Test
        eventObject.RaisePropertyChanged();
    }

    static void PropertyChanged<TOwner, T>(TOwner obj, T oldValue, T newValue)
    {
        Console.WriteLine("-- Invoked --");
    }
}

Inspired from

Using Reflection to get static method with its parameters

and

Create generic delegate using reflection

查看更多
登录 后发表回答