简体   繁体   中英

How can I pass an event to a function in C#?

I am looking to pass an event to a helper function. This function will attach a method to the event. However, I am having trouble properly passing the event. I have tried passing a EventHandler<TEventArgs> . It compiles, but events are not attached (but are still added; it seems a copy of the event handler is made).

For example, if I have this:

public event EventHandler<EventArgs> MyEvent;

And the helper function:

public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo)
{
    eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); };
}

And the caller:

MyHelperFunction(MyEvent);
MyEvent(null, new EventArgs()); // Does nothing.

The reason why this does not work is += when applied to a delegate creates a new delegate which is the combination of the old and the new. It does not modify the existing delegate.

In order to get this to work you will have to pass the delegate by reference.

public static void Helper(ref EventHandler<EventArgs> e)
{
    e+= (x,y) => {};
}

The reason this works outside of the method is because the LHS is still the actual field. So += will create a new delegate and assign back to the member field.

Just came up with this little helper. If it is your self-created Event you could use a wrapper like this. You can use your += operators to attach handlers as normal but can pass the wrapper around and even raise the event from elsewhere.

public class GenericEvent<T> where T:EventArgs
{
    public event EventHandler<T> Source = delegate { };

    public void Raise(object sender, T arg = default(T))
    {
        Source(sender, arg);
    }

    public void Raise(T arg = default(T))
    {
        Source(this, arg);
    }

    public void AddHandler(EventHandler<T> handler)
    {
        Source += handler;
    }

    public void RemoveHandler(EventHandler<T> handler)
    {
        Source -= handler;
    }

    public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler)
    {
        genericEvent.AddHandler(handler);
        return genericEvent;
    }
}

Create the event like:

public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>();

Attach handlers:

MyEvent += (s,e) => {};

Raise event:

MyEvent.Raise();

Just guessing: Have you tried passing it as ref?

public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo)

MyHelperFunction(ref MyEvent);

It's not exactly nice, but you can use reflection to do this.

    public EventMonitor(object eventObject, string eventName)
    {
        _eventObject = eventObject;
        _waitEvent = eventObject.GetType().GetEvent(eventName);

        _handler = new EventHandler(SetEvent);
        _waitEvent.AddEventHandler(eventObject, _handler);
    }

Where eventObject is the object containing the event, and eventName is the name of the event. SetEvent is your event handler.

I also have a dispose method like this:

    public void Dispose()
    {
        _waitEvent.RemoveEventHandler(_eventObject, _handler);
    }

Pass something like Action e = e => myevent += e; And call from method with the handler? It has the benefit of working with .NET classes.

I have a solution where I have an two interfaces. The first interface has methods for binding certain events, while the other interface has event methods that can be bound to those events.

The first interface's bind methods takes the second interface as parameter, which makes it possible to bind the events to the event methods of any class that implements the second interface.

Is that understandable, or would you prefer some code? :)

As many have pointed out, passing an event to a method is either not possible or not simple.

  1. Please clarify, but I suspect your intended usage will look something like:

     void Register() { var super = new SuperHandler(); //not valid syntax: super.HandleEvent(MyEvent1); super.HandleEvent(MyEvent2); super.HandleEvent(MyEvent3); super.HandleEvent(MyEvent4); }

    You can accomplish this simply by making your intended generic event handlers accessible publicly (or internally, if you desire):

     public static class GenericHandler { public static void HandleAnyEvent(object sender, EventArgs e) { //handle } } public class SomeClass { void RegisterEvents() { var r = new EventRaiser(); r.ImportantThingHappened += GenericHandler.HandleAnyEvent; } }

    In this example my catch-all handler is in a static class, but you can just as well use a non-static class. Also, I see that in your example you have made the method generic (TEventArgs). Because all EventHandler derivatives (such as CancelEventHandler) match the base EventHandler, you do not need to involve generics (nor would it be helpful).

  2. If the registration logic is complex or you must keep the EventHandler private, consider using Interface Events . This may not meet your intended goal of reducing the amount of code, but it will allow you to create a class that can predictably handle all of the events of a specific type.

     interface IRaiseEvents { event EventHandler ConnectionCreated; event EventHandler ConnectionLost; } public class SuperHandler { void RegisterEvents(IRaiseEvents raiser) { raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected."); raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected."); } }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM