2009-06-26 51 views
2

我有一个类封装了另一个类,并公开了它包装的类中的几个事件。 (它包装的情况下可以改变)添加一个事件处理程序到另一个

我用下面的代码:

public event EventHandler AnEvent; 

public OtherClass Inner { 
    get { /* ... */ } 
    set { 
     //... 
     if(value != null) 
      value.AnEvent += AnEvent; 
     //... 
    } 
} 

然而,事件引发不一致。

这段代码有什么问题?

回答

3

问题是Delegate是不可变的。

如果添加了处理程序事件时,它会创建一个新的Delegate实例其中包含了旧处理器和新添加的处理程序。旧的Delegate未被修改并被丢弃。

当我编写value.AnEvent += AnEvent时,它将包含当前处理程序(如果有)的Delegate添加到内部类的事件中。但是,对外部类事件的更改将被忽略,因为它们不会更改我添加到内部类事件中的Delegate实例。同样,如果在设置Inner属性后删除处理程序,则不会从内部类的事件中删除该处理程序。


有两种正确的方法可以做到这一点。

我可以做我自己的处理程序调用包装的情况下,像这样:

public event EventHandler AnEvent; 

public OtherClass Inner { 
    get { /* ... */ } 
    set { 
     if(Inner != null) 
      Inner.AnEvent -= Inner_AnEvent; 

     //... 

     if(value != null) 
      value.AnEvent += Inner_AnEvent; 

     //... 
    } 
} 

void Inner_AnEvent(object sender, EventArgs e) { 
    var handler = AnEvent; 
    if (handler != null) handler(sender, e); 
} 

的另一种方法是创建一个自定义事件,增加了其处理程序的内部类的事件包装,像这样:

EventHandler anEventDelegates 

public OtherClass Inner { 
    get { /* ... */ } 
    set { 
     //... 
     if(value != null) 
      value.AnEvent += anEventDelegates; 
     //... 
    } 
} 
public event EventHandler AnEvent { 
    add { 
     anEventDelegates += value; 
     if (Inner != null) Inner.AnEvent += value; 
    } 
    remove { 
     anEventDelegates -= value; 
     if(Inner != null) Inner -= value; 
    } 
} 

请注意,这不是完全线程安全的。

我解决了这个问题,我和我发帖的问题&答案的人有类似问题的好处。

+2

其中,我认为唯一的语义正确的方法是第一个。甚至超出谁是“发件人”财产应报告的问题(这可能是有点模糊的情况下收到订阅的对象不是启动动作的对象)还有一个问题o f如果一个对象为一个内部和外部类实例的事件订阅方法,然后取消订阅一个方法,会发生什么。即使重复取消一个实例的事件,也不应取消订阅另一个事件。 – supercat 2012-05-22 19:02:33

3

your answer - 这里有两个问题...

第一:在这两种情况下,你提出了错误的发送者的外部事件。有人订阅外部类别的活动,预计这些类别将通过该外部类别的发件人进行募集。

这对于winform控件或绑定列表实现,其中发件人用于识别共享处理程序的许多对象之间的对象尤为重要。

这应该不是是这样的:

void Inner_AnEvent(object sender, EventArgs e) { 
    var handler = AnEvent; 
    if (handler != null) handler(this, e); 
} 

第二(更小的)问题是,你正在服用了事件的内部类,即使外部类没有订户。你可以修复这个更多的自定义处理...

private EventHandler anEvent; 
public event EventHandler AnEvent { 
    add { // note: not synchronized 
     bool first = anEvent == null; 
     anEvent += value; 
     if(first && anEvent != null && inner != null) { 
      inner.SomeEvent += Inner_AnEvent; 
     } 
    } 
    remove { // note: not synchronized 
     bool hadValue = anEvent != null; 
     anEvent -= value; 
     if(hadValue && anEvent == null && inner != null) { 
      inner.SomeEvent -= Inner_AnEvent; 
     } 
    } 
} 

(以及类似的代码在内蒙古获取/设置成只订阅,如果我们有听众......

if(value != null && anEvent != null) 
    value.AnEvent += Inner_AnEvent; 

如果你有很多的outer-的实例,这可能是一个很大的保护但很少使用事件

+0

我不是在写图书馆;我是唯一一个处理事件的人,我希望sender参数成为内部类。 – SLaks 2009-06-26 12:24:32

相关问题