C# 对于一个类来说,为另一个类注册事件处理程序是一种不好的做法吗?

C# 对于一个类来说,为另一个类注册事件处理程序是一种不好的做法吗?,c#,events,event-handling,C#,Events,Event Handling,事件订阅者应该始终注册事件处理程序,还是由另一个类来注册 例如: class EventPublisher { public event EventHandler Event; } class EventSubscriber { public void Handler(object sender, EventArgs e) { } } class Glue { private EventPublisher _publisher = new EventPublis

事件订阅者应该始终注册事件处理程序,还是由另一个类来注册

例如:

class EventPublisher {

    public event EventHandler Event;
}

class EventSubscriber {

    public void Handler(object sender, EventArgs e) { }
}

class Glue {

    private EventPublisher _publisher = new EventPublisher();

    private EventSubscriber _subscriber = new EventSubscriber();


    public Glue() {
        _publisher.Event += _subscriber.Handler;
    }
}
事件订阅者应该始终注册事件处理程序,还是 让另一个班做可以吗

这种设计本身并没有什么固有的错误,尽管它实际上取决于您的体系结构以及您希望它的可伸缩性和解耦程度

请注意,您使用
Glue
提出的一般假设没有上下文,因此很难说出您的确切需求

现在,我很少写传统的事件,倾向于使用更现代的方法,发布/次级制作人/消费者解耦消息或(取决于您使用的框架)。消费者可以随意订阅,制作人可以随意发布,而彼此事先不知情。Martin Follower在他的网站上更详细地介绍了这种模式

更重要的是,使用解耦消息(和kin)的优势在于,消费者可以订阅对话,而不必事先知道制作者是谁,这将为您提供更好的服务,从而创建一个更易于维护和扩展的系统。如果系统变得足够大,那么将类推到另一个类上就不会那么痛苦了

说到这里,如果这只是一个非常简单的实现(并且是紧密耦合的实现),那么标准香草冰淇淋味的.NET事件和让订阅者注册事件处理程序没有什么错,尽管您必须再次将其分解为设计中最合乎逻辑的问题

也就是说,音响管理员应该天生就知道狗和它的吠叫。或者你的狗类应该注册到声音管理器。你的设计直觉应该引领方向


不管怎样,祝你好运

编程,这没有错,对您来说会很顺利

事实上,您需要在以下情况下执行此操作:在
EventSubscriber
中使用哪种方法 (
处理程序
)应该在
事件
上调用,没有
事件发布者
类的对象

如何订阅活动是一个主观问题,它取决于你的总体流量和舒适度

但据我所知,订阅者类中的订阅事件使代码更具可读性和易懂性

要订阅任何类的事件,subscriber类必须有该类的对象,但在您的示例中它没有

当订阅者类本身持有发布者类的对象时,人们通常用这种方法设计体系结构。这种设计在某些流程中有其自身的优势,参见下面的示例

public class EventPublisher
{
    public event EventHandler HeavyLogicDone;

    public void ExposedMethod(string subScriberSpecificData)
    {
        Thread logicCaller = new Thread(() => HeavyLogic(subScriberSpecificData));
        logicCaller.Start();
    }

    private void HeavyLogic(string subScriberSpecificData)
    {
        //logic which may take time
        if (HeavyLogicDone != null)
            HeavyLogicDone(this, new EventArgsClass(subScriberSpecificData));
    }
}
这里,
EventPublisher
类有一个公开的功能,该功能应该由
EventSubscriber
调用,但由于此方法可能需要时间,所以正在线程中编写

现在的问题是,由于它在线程中,该方法的调用将在启动线程后很快返回,订阅者无法启动依赖于该方法结果的功能,它必须等待。因此,要通知订阅服务器任务已完成,有一个事件

public class EventSubscriber
{
    string currentData = "";
    public EventSubscriber(EventPublisher eventPublisher, string data)
    {
        currentData = data;
        eventPublisher.HeavyLogicDone += eventPublisher_HeavyLogicDone;
        eventPublisher.ExposedMethod(currentData);
        //Contineous without waiting for heavy logic to compelete
    }

    void eventPublisher_HeavyLogicDone(object sender, EventArgs e)
    {
        if(((EventArgsData)e).subScriberSpecificData == currentData)
        {
           //Do further task which is dependant to result of logic
           //if now subscriber doesn't need to listen this event anymore
           ((EventPublisher)sender).HeavyLogicDone -= eventPublisher_HeavyLogicDone;
        }
    }
}
正如您所看到的,何时订阅事件以及何时取消订阅事件,订阅者现在拥有所有控制权


但是,如果你愿意这样做的话

static void Main(string[] args)
{
    EventSubscriber subscriber1 = new EventSubscriber("sub1");
    EventSubscriber subscriber2 = new EventSubscriber("sub2");
    EventPublisher pub = new EventPublisher();
    pub.HeavyLogicDone += subscriber1.eventPublisher_HeavyLogicDone;
    pub.HeavyLogicDone += subscriber2.eventPublisher_HeavyLogicDone;
    pub.ExposedMethod("sub1");
    pub.ExposedMethod("sub2");
}
第一个问题:正如您所看到的,每次创建Subscriber实例时,您都必须显式地编写它的订阅和对当前订阅服务器的publisher方法的调用。这是耦合的代码,每次都需要这样做

和用户类别

public class EventSubscriber
{
    string currentData = "";
    public EventSubscriber(string data)
    {
        currentData = data;
    }
    public void eventPublisher_HeavyLogicDone(object sender, EventArgs e)
    {
        if(((EventArgsData)e).subScriberSpecificData == currentData)
        {
           //Do further task which is dependant to result of logic

           //if now subscriber doesn't need to listen this event anymore
           ((EventPublisher)sender).HeavyLogicDone -= eventPublisher_HeavyLogicDone;
        }
    }
}

第二个问题:由于订阅不受subscriber类的控制,所以只有使用subscriber类才能取消订阅。代码理解起来会有点混乱。

Glue的作用似乎有点类似于in-Flux架构语义。请确保是否需要在每个inGlue中创建发布者的新实例(假设订阅方的新实例每次都会调用Glue),由于当前您正在创建一对
发布订阅
,事件将仅在此对中工作。如果您想要一个发布者和多个订阅者,则它将不起作用。