向另一个添加一个事件处理程序

时间:2009-06-26 02:22:31

标签: c# .net delegates event-handling

我有一个包装另一个类的类,并从它包装的类中公开了几个事件。 (它包装的实例可以改变)

我使用了以下代码:

public event EventHandler AnEvent;

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

然而,事件的提出不一致。

这段代码出了什么问题?

2 个答案:

答案 0 :(得分: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;
    }
}

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

我自己解决了这个问题并发布了问题&回答有类似问题的人的利益。

答案 1 :(得分:3)

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

首先:在这两种情况下,您都使用错误的发件人引发外部事件。订阅外部类上的事件的人会期望使用该外部类的 sender 来引发这些类。

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

这应该是这样的:

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;

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