在哪里订阅内部对象的事件?

时间:2013-01-23 12:13:07

标签: c# oop events

我经常遇到一种情况,我必须决定在哪里订阅内部对象的事件?

例如,我的对象模型如下所示:

class ClassA
{
    public event EventHandler SomeEvent1;
    public event EventHandler SomeEvent2;
    public event EventHandler SomeEvent3;
    public event EventHandler SomeEvent4;
}

class ClassB
{
    private ClassA a;
    public ClassA A 
    {
        get
        {
            return this.a;
        }
    }

    public ClassB()
    {
        this.a = new ClassA();
        // here subscribe to some events (for example, SomeEvent1 and SomeEvent2)
        // this.a.SomeEvent1 += OnSomeEvent1Raised;
        // this.a.SomeEvent2 += OnSomeEvent2Raised;
    }
}

class ClassC
{
    public ClassB B { get; }
}

class ClassD
{
    public ClassC C { get; }

    public void SomeMethod()
    {
        // Here subscribe to another ones events of object C.B.A. For example:
        C.B.A.SomeEvent3 += OnSomeEvent3Raised;
        C.B.A.SomeEvent4 += OnSomeEvent4Raised;
    }

    private void OnSomeEvent4Raised(object sender, EventArgs e)
    {
        throw new NotImplementedException();
    }

    private void OnSomeEvent3Raised(object sender, EventArgs e)
    {
        throw new NotImplementedException();
    }
}

我试图创建类似UML图的东西: enter image description here

我的项目的现有代码的结构具有这样的对象模型存在的位置(它具有订阅如上例中所实现的事件的地方 - C.B.A.SomeEvent + =)。 我不喜欢它,想要改变它。 我想向您提供有关这种情况的最佳做法。

替代方法是复制classB,classC,classD中classA的所有事件。 然后将所有订阅事件替换为 ONE PLACE (我的意思是在classB中我们将订阅/取消订阅ClassA对象的所有事件。在classC中,我们将订阅/取消订阅所有事件classB的对象。依此类推......)在这种情况下,所有订阅和取消订阅都将在一个地方。希望,你明白我的意思。

再次,请依靠您的知识和经验告诉我们如何解决这种情况。

更新

您是否同意我的观点,订阅和取消订阅必须放在一个地方? 请回答这个附加问题。

提前致谢。

3 个答案:

答案 0 :(得分:2)

您可能对event aggregator感兴趣。

它基本上做的是将发布者与订阅者分离 - 它是一种事件容器。您可以通过依赖注入(例如MEF)为您要订阅或发布的每个类获取事件聚合器。

我个人使用和最喜欢的方式是Rob Eisenberg在Caliburn Micro中实施事件聚合器的方式:

在您的情况下,对象A,B和C将共享事件聚合器的同一实例,这意味着只要在此事件聚合器上发布事件,所有这些对象都会识别它。由于对某些事件的不同处理,A,B和C类的行为会有所不同。

修改

使用事件聚合器,您使用实例订阅聚合器本身。发布者和订阅者类之间的连接通过依赖于事件聚合器的相同实例来实现。在Caliburn.Micro的情况下,通过实现通用接口(IHandle<>)来订阅某些事件。 例如:如果您想订阅MyCustomEvent,请在要订阅的类中实现IHandle<MyCustomEvent>接口。 这需要从void Handle(MyCustomEvent e)接口为此类事件实现IHandle<MyCustomEvent>方法。每次在共享事件聚合器上发布(新)MyCustomEvent时,都会调用此方法。

答案 1 :(得分:0)

您的示例中有太多public内容。希望我在下面有意义:

  1. ClassB包含ClassA类型的对象,并处理一些ClassA事件
  2. ClassC包含ClassB类型的对象,但忽略事件。
  3. ClassD包含ClassC 类型的对象,并处理ClassB对象中包含的ClassA对象中的事件
  4. #2和#3不好:ClassC应该处理并实现事件,处理它们并让它们“冒泡”(调用它们自己的,相同的事件)让ClassD正确处理。

    基本上,所有这些应该处理所有事件,要么对它们作出反应(如ClassB到ClassA的事件),要么传播它们。

答案 2 :(得分:0)

在这里找到一个好的解决方案:

Csharp-NotificationCenter