条件事件处理

Conditional EventHandling

我想我有一个设计问题,我希望得到你的意见。我做了一个小程序来说明我的问题。基本上,我的节目由一个无线电系统组成,大楼的每个房间都能听到。声音取决于接收端,取决于房间是否将自己注册到无线电系统。

我的问题是发送的消息在每个房间都被触发,即使房间没有注册。我更愿意在消息发出之前而不是在接收端执行条件。通过这样做,我可以节省不必要的流量。谁能给我一个想法或解决此类情况的正确方法?

郑重声明,我不希望收音机中有多个事件处理程序,因为我不知道会有多少房间。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Radio
{
#region Speakers
public interface ISound
{
    string sound { get; set; }
}
public abstract class RoomSpeaker : ISound
{
    public string sound { get; set; }
}
public class Room1Speaker : RoomSpeaker
{
}
public class Room2Speaker : RoomSpeaker
{
}
public class BuildingSpeaker : RoomSpeaker
{
}
#endregion
#region Rooms
public abstract class Room
{
    public Radio radioPlayer;
    public string name;
    public HashSet<Type> registeredSpeakers = new HashSet<Type>();

    public virtual void RoomPlayer(string lyrics)
    {
        registeredSpeakers.Add(typeof(BuildingSpeaker));
        Console.WriteLine(lyrics);
    }
}
public class Room1 : Room
{
    public Room1(Radio radioPlayer)
    {
        this.radioPlayer = radioPlayer;
        name = "Room1";
        registeredSpeakers.Add(typeof(Room1Speaker));
        radioPlayer.onRadio += radioPlayer_onRadio;
    }

    // This is what I don't think I like.  It will only do something if it's registered. That's fine.
    // But on any radio message out, this room will get called regardless.  Should I NOT be doing this? Should I go back to
    // making an eventHandler for every room? rather then having one even handler for all the rooms and have a condition on the receiving end.

    void radioPlayer_onRadio(object sender, ISound e)
    {
        if (registeredSpeakers.Contains(e.GetType()))
            RoomPlayer(name + e.sound);
    }
}
public class Room2 : Room
{
    public Room2(Radio radioPlayer)
    {
        this.radioPlayer = radioPlayer;
        name = "Room2";
        registeredSpeakers.Add(typeof(Room2Speaker));
        radioPlayer.onRadio += radioPlayer_onRadio;
    }

    void radioPlayer_onRadio(object sender, ISound e)
    {
        // same problem as in Room1. 
        if (registeredSpeakers.Contains(e.GetType()))
            RoomPlayer(name + e.sound);
    }
}
#endregion

public class Radio
{
    public event EventHandler<ISound> onRadio;

    public void PlayRoom1()
    {
        onRadio(this, new Room1Speaker() { sound = "Test" });
    }
    public void PlayRoom2()
    {
        onRadio(this, new Room2Speaker() { sound = "Test" });
    }
    public void PlayAllRooms()
    {
        onRadio(this, new BuildingSpeaker() { sound = "Test All Rooms" });
    }
}

class Program
{
    static void Main(string[] args)
    {
        var radio = new Radio();
        var room1 = new Room1(radio);
        var room2 = new Room2(radio);

        radio.PlayRoom1();
        radio.PlayRoom2();
        radio.PlayAllRooms();
        Console.ReadLine();
    }
}
}

好的,您正在查看的是发布-订阅模式(也称为事件总线)。在事件总线模式中,您有一个 class 来注册侦听器并发送消息。听众告诉事件总线 "I'm listening for an event X"。当事件总线 "sends" 事件 X 时,它会查询该事件的侦听器列表,如果它们已注册,则执行侦听器告诉它执行的方法。

public class EventBus
{
    private Dictionary<Type, List<Action<IEvent>>> actions = new Dictionary<Type, List<Action<IEvent>>>();

    public void Listen<T>(Action<IEvent> callback) where T : IEvent
    {
        if (!actions.ContainsKey(typeof(T)))
        {
            actions.Add(typeof(T), new List<Action<IEvent>>());
        }

        actions[typeof(T)].Add(callback);
    }

    public void ClearCallbacks<T>() where T : IEvent
    {
        actions[typeof (T)] = null;
    }

    public void Send<T>(T @event) where T : IEvent
    {
        if (!actions.ContainsKey(typeof(T)))
        {
            return;
        }

        foreach (var action in actions[typeof(T)])
        {
            action(@event);
        }
    }
}

public interface IEvent
{
}

用法:

public static void main () {
    var eventBus = new EventBus();

    var aRoom = new NoisyRoom(eventBus);
    var bRoom = new NoisyRoom(eventBus);
    var cRoom = new NoisyRoom(eventBus);
    var dRoom = new QuietRoom(eventBus);

    eventBus.Send(new NoisyEvent()); //sends to a,b,c room
}

public class EasyListeningEvent : IEvent
{
}
public class QuietRoom
{
    public QuietRoom(EventBus eventBus)
    {
        eventBus.Listen<EasyListeningEvent>(BringTheNaps);
    }

    private void BringTheNaps(IEvent @event)
    {
        //its been brought!
    }
}

class NoisyEvent : IEvent
{
}
public class NoisyRoom
{
    public NoisyRoom(EventBus eventBus)
    {
        eventBus.Listen<NoisyEvent>(BringTheNoise);
    }

    private void BringTheNoise(IEvent @event)
    {
        //its been brought!
    }
}

试试像这样的东西:

编辑:请注意,这只是正确方向的开始。你可以更进一步。基本上你拥有的是一个声源和一个发声器。显然,收音机是声源,扬声器是发声器,但房间之类的东西可以两者兼而有之。收音机不应该知道扬声器或房间是什么,它应该只知道发射器,它应该只向它们发送声音。基于此,一个房间应该有一组发射器(可能是扬声器),当一个房间从收音机接收到声音时,它会简单地将声音转发给它注册的任何发射器。也没有什么能阻止您将扬声器直接注册到收音机。这段代码应该有助于展示您如何实现所有这些。

public class Radio
{
    private HashTable<string, EventHandler<ISound>> rooms = new ...;

    public void RegisterRoom(string room, EventHandler<ISound> onSound)
    {
        rooms[room] = onSound;
    }

    public void UnregisterRoom(string room)
    {
        rooms.Remove(room);
    }

    public void PlayRoom(string room)
    {
        EventHandler<ISound> onSound;

        if (rooms.TryGetValue(room, out onSound))
        {
            onSound(this, new BuildingSpeaker() { sound = "Test" });
        }
    }

    public void PlayAllRooms()
    {
        if (rooms.Count == 0)
        {
            return;
        }

        var speaker = new BuildingSpeaker() { sound = "Test All Rooms" };

        foreach (var room in rooms)
        {
            room.Value(this, speaker);
        }
    }
}