📜  如何在许多类上应用事件 (1)

📅  最后修改于: 2023-12-03 14:52:58.522000             🧑  作者: Mango

如何在许多类上应用事件

在一个大型的程序中,通常会涉及多个类之间的交互,为了保证代码的可维护性和可扩展性,我们常常采用事件的方式来处理类之间的交互。但是如何在许多类上应用事件呢?下面,我们将介绍一些实用的方法。

方法一:基于委托实现事件的发布和订阅

在.NET中,我们可以通过委托来实现事件的发布和订阅。在多个类之间传递事件时,我们可以定义一个公共委托,并在需要订阅事件的类中注册该委托。当事件发生时,所有注册了该委托的对象都会收到通知。

public delegate void EventHandler(object sender, EventArgs e);

public class Publisher
{
    public event EventHandler MyEvent;

    public void RaiseMyEvent()
    {
        MyEvent?.Invoke(this, EventArgs.Empty);
    }
}

public class Subscriber1
{
    public Subscriber1(Publisher publisher)
    {
        publisher.MyEvent += HandleMyEvent;
    }

    private void HandleMyEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Subscriber1 handles the event.");
    }
}

public class Subscriber2
{
    public Subscriber2(Publisher publisher)
    {
        publisher.MyEvent += HandleMyEvent;
    }

    private void HandleMyEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Subscriber2 handles the event.");
    }
}

在上述代码中,我们定义了一个公共委托EventHandler和一个Publisher类,该类定义了一个名为MyEvent的事件,并在RaiseMyEvent方法中发布事件。同时,我们定义了两个订阅者类Subscriber1Subscriber2,它们在构造函数中注册MyEvent事件,并实现了事件处理方法HandleMyEvent

最后,我们就可以使用以下代码来验证事件的发布和订阅了:

Publisher publisher = new Publisher();
Subscriber1 subscriber1 = new Subscriber1(publisher);
Subscriber2 subscriber2 = new Subscriber2(publisher);

publisher.RaiseMyEvent(); // 输出 Subscriber1 handles the event. 和 Subscriber2 handles the event.
方法二:基于反射实现动态注册事件

如果我们需要在运行时动态注册事件,可以使用反射来实现。在.NET中,我们可以使用EventInfo类来获取类的事件信息,并使用Delegate.CreateDelegate方法来动态创建委托并将其添加到事件的订阅列表中。

以下代码演示了如何使用反射动态注册事件:

public class Publisher
{
    public event EventHandler MyEvent;

    public void RaiseMyEvent()
    {
        MyEvent?.Invoke(this, EventArgs.Empty);
    }
}

public class Subscriber
{
    public void HandleMyEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Subscriber handles the event.");
    }
}

// 动态注册事件
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();

EventInfo eventInfo = publisher.GetType().GetEvent("MyEvent");
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, subscriber, "HandleMyEvent");
eventInfo.AddEventHandler(publisher, handler);

// 触发事件
publisher.RaiseMyEvent(); // 输出 Subscriber handles the event.
方法三:基于消息队列实现事件的分发

如果我们的程序存在多个进程或多台机器,则需要将事件分发到各个进程或机器上。这时,我们可以使用消息队列来实现事件的分发。

消息队列的基本原理是,生产者将消息放入队列中,消费者从队列中获取消息并进行处理。在.NET中,我们可以使用System.Messaging命名空间下的MessageQueue类来实现消息队列。

以下代码演示了如何使用MessageQueue来实现事件的分发:

public class MessagePublisher
{
    private MessageQueue queue;

    public MessagePublisher(string queueName)
    {
        if (!MessageQueue.Exists(queueName))
        {
            MessageQueue.Create(queueName);
        }

        this.queue = new MessageQueue(queueName);
    }

    public void Publish(string message)
    {
        this.queue.Send(message);
    }
}

public class MessageSubscriber
{
    private MessageQueue queue;
    private bool stop;

    public MessageSubscriber(string queueName)
    {
        if (!MessageQueue.Exists(queueName))
        {
            throw new Exception($"Queue {queueName} does not exist.");
        }

        this.queue = new MessageQueue(queueName);
        this.queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
    }

    public void Subscribe()
    {
        this.stop = false;
        while (!stop)
        {
            try
            {
                Message message = this.queue.Receive();
                string content = (string)message.Body;
                HandleMessage(content);
            }
            catch (MessageQueueException e)
            {
                if (e.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
                {
                    // Ignore timeout and continue.
                }
                else
                {
                    throw;
                }
            }
        }
    }

    private void HandleMessage(string message)
    {
        Console.WriteLine($"Subscriber handles message {message}.");
    }

    public void Unsubscribe()
    {
        this.stop = true;
    }
}

在上述代码中,我们定义了一个名为MessagePublisher的类和一个名为MessageSubscriber的类。其中,MessagePublisher类负责将消息放入消息队列中,MessageSubscriber类负责从消息队列中获取消息并进行处理。

可以使用以下代码来验证该方法是否可行:

string queueName = "event-queue";
MessagePublisher publisher = new MessagePublisher(queueName);
MessageSubscriber subscriber = new MessageSubscriber(queueName);

Task.Run(() => subscriber.Subscribe());

publisher.Publish("Hello, world!");
publisher.Publish("Goodbye, world!");

Thread.Sleep(1000);

subscriber.Unsubscribe();