在.Net中设置/删除事件处理程序

时间:2009-03-04 16:58:42

标签: c# .net event-handling lambda

所以我很难修复/维护另一个程序员代码(blech)

我是一个坚定的规则教授“如果它没有破坏不修复它!”所以当每次遇到可怕的代码时想要改变一些东西,我只能限制自己仅更改可能的绝对最小代码量以进行所需的修复。但在某些情况下,我真的需要先了解一些事情,然后再尝试遵循它/改变它。

我在这里看到了这一点:

region.LineSelected = (x) => { };

我想知道它是否与此相同:

region.LineSelected = null;

在我改变它的方法之前,我希望对第一行的做法100%肯定。

7 个答案:

答案 0 :(得分:8)

根据我目前对该主题的意见进行编辑

他们不一样。 lambda版本正在为一个空的匿名方法添加一个事件处理程序。这允许其他代码自由地引发LineSelected()而不用担心它是否为null(即没有监听器)。

例如

var lineSelected = this.LineSelected;

if (lineSelected != null)
{
    lineSelected(EventArgs.Empty);
}

如果在if之后但在引发事件之前在另一个线程中从LineSelected取消订阅,则上述语句可以抛出NullReferenceException。 将LineSelected分配给临时变量然后引发它可以调用未订阅的事件侦听器。将事件处理程序分配给局部变量是处理空委托的推荐方法。

通过添加一个空委托,其他代码总是能够调用LineSelected而不用担心NullReferenceException。通过将多播事件委托分配给局部变量,可以确保该值不能是由另一个线程修改。

答案 1 :(得分:2)

我想不出任何理由拥有第一行代码。我唯一能想到的是,在提升LineSelected事件时,如果你在类中有第一行代码,则不需要检查LineSelected事件是否为null。即:

if (this.LineSelected != null)
{
   LineSelected(this,new EventArgs());
}

相反,您可以在不进行空检查的情况下引发事件。

但是,使用第二行代码,您需要检查空值。

答案 2 :(得分:2)

这不是一个事件处理程序,这是一个简单的委托。 (必须使用+ =和 - =修改事件处理程序以附加和分离事件。)

如前所述,将delegate属性设置为空处理程序意味着在调用委托之前不必执行空检查(假设没有其他任何东西可以将其设置为null)。

这可能会使调用代码更方便,但不应将其混淆为性能改进(通过删除检查null的必要性)。通常,委托方法调用的开销将显着高于空检查的开销。可能存在一些场景(例如,如果委托在99.99%的时间内具有“真实”实现),其中避免空检查可以提高性能但是很难想象一个场景中可能存在足够小的性能差异。是值得的,也不值得完全取消委托调用以支持更高效的东西。

答案 3 :(得分:1)

不,这不是一回事 - 第一行为LineSelected分配了一个与null非常不同的空委托。

发现差异的最简单方法是在使用lambda语法时查看编译器代表您生成的代码。这段代码:

using System;

class Program
{
    static void Main()
    {
        Action<int> func0 = (x) => { };
        Action<int> func1 = null;
    }
}

真的可以编译:

internal class Program
{
    // Methods
    private static void Main()
    {
        Action<int> func0 = delegate (int x) {
        };
    }
}

请注意,编译器足够聪明,可以删除func1,因为它已设置为null而未在其他位置引用。但请注意,func0仍然存在,并设置为代理,尽管与null有很大不同。

答案 4 :(得分:1)

它们不一样,因为事件处理程序已设置。

让我们说暴露LineSelected的类,忘了:

if( LineSelected != null )
    LineSelected(...)

如果该类要调用LineSelected并且没有人正在侦听,那么它将抛出NullReferenceException

请注意,您也可以(在区域内)避免竞争条件:

var event = LineSelected;     if(event!= null)         事件(...

答案 5 :(得分:1)

我认为这是一种避免对每个事件进行空检查的技术。

如果LineSelected事件引发代码没有正确的空检查,那么这将导致异常:

region.LineSelected = null;

/* no event handlers added to LineSelected */

class Region {
    void OnLineSelected() {
        // Null error!
        LineSelected();
    }
}

但是,如果添加了一个空的无副作用处理程序,那么即使没有人为事件添加处理程序,上面的代码也能正常工作,因为总会有附加的空处理程序。

答案 6 :(得分:1)

为了扩展理查德和安德鲁所说的内容,它相当于

region.LineSelected = delegate {};

这意味着当你举起活动时,你不需要先检查是否为空,因为它有一个委托(代价很小)