如何将事件传递给 C# 中的函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/623716/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 10:44:02  来源:igfitidea点击:

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

c#delegates

提问by strager

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).

我希望将事件传递给辅助函数。此函数将向事件附加一个方法。但是,我无法正确传递事件。我试过通过一个EventHandler<TEventArgs>. 它编译,但没有附加事件(但仍然添加;似乎制作了事件处理程序的副本)。

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.

采纳答案by JaredPar

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.

这在方法之外起作用的原因是因为 LHS 仍然是实际的字段。所以 += 将创建一个新的委托并分配回成员字段。

回答by Michael Stum

Just guessing: Have you tried passing it as ref?

只是猜测:您是否尝试过将其作为参考传递?

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

MyHelperFunction(ref MyEvent);

回答by Daniel Saidi

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? :)

这是可以理解的,还是您更喜欢一些代码?:)

回答by Ray

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.

其中 eventObject 是包含事件的对象,而 eventName 是事件的名称。SetEvent 是您的事件处理程序。

I also have a dispose method like this:

我也有一个像这样的处理方法:

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

回答by Kevin McCormick

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.");
        }
    }
    
  1. 请澄清,但我怀疑您的预期用途如下:

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

    您可以简单地通过公开(或内部,如果您愿意)访问您预期的通用事件处理程序来完成此操作:

     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;
         }
     }
    

    在此示例中,我的全能处理程序位于静态类中,但您也可以使用非静态类。另外,我看到在您的示例中,您已将方法设为通用 (TEventArgs)。因为所有 EventHandler 派生类(例如 CancelEventHandler)都与基础 EventHandler 匹配,所以您不需要涉及泛型(这也无济于事)。

  2. 如果注册逻辑很复杂或者您必须保持 EventHandler 私有,请考虑使用Interface Events。这可能无法满足您减少代码量的预期目标,但它允许您创建一个可以预测地处理特定类型的所有事件的类。

    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.");
        }
    }
    

回答by Sven

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();

回答by Elros

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

传递类似 Action e = e => myevent += e; 并使用处理程序从方法调用?它具有使用 .NET 类的好处。