自动创建空的 C# 事件处理程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/340610/
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-03 23:50:32  来源:igfitidea点击:

Create empty C# event handlers automatically

c#eventsdelegatesclr

提问by Tomas Andrle

It is not possible to fire an event in C# that has no handlers attached to it. So before each call it is necessary to check if the event is null.

不可能在 C# 中触发没有附加处理程序的事件。所以在每次调用之前,有必要检查事件是否为空。

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

I would like to keep my code as clean as possible and get rid of those null checks. I don't think it will affect performance very much, at least not in my case.

我想保持我的代码尽可能干净并摆脱那些空检查。我认为它不会对性能产生太大影响,至少在我的情况下不会。

MyEvent( param1, param2 );

Right now I solve this by adding an empty inline handler to each event manually. This is error prone, since I need to remember to do that etc.

现在我通过手动向每个事件添加一个空的内联处理程序来解决这个问题。这很容易出错,因为我需要记住这样做等等。

void Initialize() {
  MyEvent += new MyEvent( (p1,p2) => { } );
}

Is there a way to generate empty handlers for all events of a given class automatically using reflection and some CLR magic?

有没有办法使用反射和一些 CLR 魔法自动为给定类的所有事件生成空处理程序?

采纳答案by Dinah

I saw this on another post and have shamelessly stolen it and used it in much of my code ever since:

我在另一篇文章中看到了这一点,并无耻地窃取了它并在我的大部分代码中使用了它:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!

//Let you do this:
public void DoSomething() {
    Click(this, "foo");
}

//Instead of this:
public void DoSomething() {
    if (Click != null) // Unnecessary!
        Click(this, "foo");
}

* If anyone knows the origin of this technique, please post it in the comments. I really do believe in the source getting due credit.

*如果有人知道这种技术的起源,请在评论中发表。我真的相信来源得到应有的信任。

(Edit:I got it from this post Hidden Features of C#?)

编辑:我是从这篇文章C# 的隐藏特性中得到的?

回答by leppie

You can write is as:

你可以写成:

MyEvent += delegate { };

I am not sure what you want to do is correct.

我不确定你想要做什么是正确的。

回答by TcKs

The notation:

符号:

if ( MyEvent != null ) {
  MyEvent( param1, param2 );
}

is not thread safe. You should do it this way:

不是线程安全的。你应该这样做:

EventHandler handler = this.MyEvent;
if ( null != handler ) { handler( param1, param2 ); }

I understand, that this is a bother, so you can do helper method:

我明白,这很麻烦,所以你可以做辅助方法:

static void RaiseEvent( EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}

and then call:

然后调用:

RaiseEvent( MyEvent, param1, param2 );

If you are using C# 3.0, you can declare helper method as extension method:

如果您使用的是 C# 3.0,您可以将辅助方法声明为扩展方法:

static void Raise( this EventHandler handler, object sender, EventArgs e ) {
    if ( null != handler ) { handler( sender, e ); }
}

and then call:

然后调用:

MyEvent.Raise( param1, param2 );

Also you can create next extension/helper methods for other event handlers. For example:

您也可以为其他事件处理程序创建下一个扩展/帮助程序方法。例如:

static void Raise<TEventArgs>( this EventHandler<TEventArgs> handler,
    object sender, TEventArgs e ) where TEventArgs : EventArgs
{
    if ( null != handler ) { handler( sender, e ); }
}

回答by mcintyre321

This is a bad idea in that the code which is consuming the event now has an expectation that the object with the event has been coded with an action by default. If your code is never going to be used anywhere else by anyone else then I guess you can get away with it.

这是一个坏主意,因为现在使用该事件的代码期望具有该事件的对象在默认情况下已使用操作进行编码。如果您的代码永远不会被其他任何人使用,那么我想您可以摆脱它。

回答by vkelman

You don't need several extension methods for different event handlers, you just need one:

对于不同的事件处理程序,您不需要多个扩展方法,您只需要一个:

public static class EventHandlerExtensions {
  public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs {
    if (handler != null) handler(sender, args);
  }
}

回答by Jairo

You can use PostSharp to on build time add this magic. It is the best way.

您可以使用 PostSharp 在构建时添加此魔法。这是最好的方法。

回答by naasking

C# event declarations unfortunately include a number of well-known safety problems and inefficiencies. I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner.

不幸的是,C# 事件声明包含许多众所周知的安全问题和低效率。我在委托上设计了许多扩展方法来安全地调用它们,并以线程安全的方式注册/取消注册委托

Your old event-raising code:

您旧的事件引发代码:

if (someDelegate != null) someDelegate(x, y, z);

Your new code:

您的新代码:

someDelegate.Raise(x, y, z);

Your old event registration code:

您的旧活动注册码:

event Action fooEvent;
...
lock (someDummyObject) fooEvent += newHandler;

Your new code:

您的新代码:

Action fooEvent;
...
Events.Add(ref fooEvent, newHandler);

No locking needed, no compiler-inserted dummy objects used to lock the events.

不需要锁定,没有用于锁定事件的编译器插入的虚拟对象。

回答by gandaliter

In C# 6.0 there's no need to go to any of these lengths to do the null check, thanks to the conditional null operator ?.

在 C# 6.0 中,由于条件 null 运算符,无需使用这些长度中的任何一个来执行 null 检查 ?.

The docsexplain that calling MyEvent?.Invoke(...)copies the event to a temporary variable, performs the null check, and if not null, calls Invokeon the temporary copy. This isn't necessarily thread-safe in every sense, as someone could have added a new event after the copy to the temporary variable, which wouldn't be called. It does guarantee you won't call Invokeon null though.

文档解释说,调用MyEvent?.Invoke(...)将事件复制到临时变量,执行空检查,如果不是空,则调用Invoke临时副本。这在任何意义上都不一定是线程安全的,因为有人可能在复制到临时变量后添加了一个新事件,而该事件不会被调用。不过,它确实保证您不会调用Invokenull。

In short:

简而言之:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click;

public void DoSomething() {
    Click?.Invoke(this, "foo");
}