13

使用我的自定义 EventArgs,例如:

public event EventHandler<MyEventArgs> SampleEvent;

来自msdn 例如:

public class HasEvent
{
// Declare an event of delegate type EventHandler of 
// MyEventArgs.

    public event EventHandler<MyEventArgs> SampleEvent;

    public void DemoEvent(string val)
    {
    // Copy to a temporary variable to be thread-safe.
        EventHandler<MyEventArgs> temp = SampleEvent;
        if (temp != null)
            temp(this, new MyEventArgs(val));
    }
}

我有两个问题:

1)查看标记的代码:

在此处输入图像描述

我看不出为什么应该将它复制到另一个参数(关于线程)

因为我们有eventkeyowrd ,所以没有人可以触摸它的调用列表(我的意思是类没有外部代码)

2)如果我没记错的话,这个 DemoEvent函数应该是虚拟的,所以它可以在子类中被覆盖......(我确定我在某处见过它)

奇怪的是 resharper 也不会添加 virtual :

所以如果我有这个代码:

在此处输入图像描述

它建议我:

在此处输入图像描述

当我按下它时:

在此处输入图像描述

所以我的2 个问题:

EventHandler<MyEventArgs> temp = SampleEvent;1)关于线程安全,这条线将解决什么场景 ?

2)功能不应该是virtual吗?(我敢肯定我已经用虚拟看到过这种模式)

4

2 回答 2

10

这一行 EventHandler temp = SampleEvent; 的场景是什么?将解决,关于线程安全?

想象一下你这样做:

if (SampleEvent != null)
    SampleEvent(this, new MyEventArgs());

如果另一个线程将在 if 之后但在调用之前分离事件处理程序,那么您将尝试调用null委托(并且您将获得异常)。

该功能不应该是虚拟的吗?(我确定我见过这种虚拟模式)

是的,如果该类不是,sealed那么您应该标记该功能virtual(这不是强制性的,但它是一种被广泛接受的模式)。

编辑

时间 线程 1 线程 2
1 obj.SampleEvent += MyHandler;
2 if (SampleEvent != null)                     
3 { obj.SampleEvent -= MyHandler;
4 SampleEvent(this, new MyEventArgs());
5 }

在这种情况下,在时间 4,您将调用一个null委托,它会抛出一个NullReferenceException. 现在看这段代码:

时间 线程 1 线程 2
1 obj.SampleEvent += MyHandler;
2 var sampleEvent = SampleEvent;
3 if (sampleEvent != null)                     
4 { obj.SampleEvent -= MyHandler;
5 sampleEvent(this, new MyEventArgs());
6 }

现在在时间 5 您调用sampleEvent它,它保存 的内容SampleEvent,在这种情况下,它不会抛出任何异常(但MyHandler即使它已被第二个线程删除,它也会调用)。

于 2012-07-16T13:14:01.317 回答
5
 if (SampleEvent != null)
    SampleEvent(this, new MyEventArgs(val));

这是一场经典的穿线比赛。此代码运行时,另一个线程可以取消订阅事件处理程序。这使得 if() 语句得出结论,即存在订阅者,但事件调用失败并出现 NullReferenceException。将委托对象复制到局部变量中可确保通过取消订阅事件处理程序来更改委托对象引用的客户端代码不会导致崩溃。仍然是一个问题,您将在取消订阅后调用事件处理程序,但这是不可避免的竞争,不一定像 NRE 那样致命,并且可以由事件处理程序处理,这与 NRE 不同。

是的,这样的方法通常被设置为受保护的虚拟方法并命名为 OnSampleEvent(),因此派生类可以更改事件引发行为。

于 2012-07-16T13:16:40.140 回答