条件事件处理
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);
}
}
}
我想我有一个设计问题,我希望得到你的意见。我做了一个小程序来说明我的问题。基本上,我的节目由一个无线电系统组成,大楼的每个房间都能听到。声音取决于接收端,取决于房间是否将自己注册到无线电系统。
我的问题是发送的消息在每个房间都被触发,即使房间没有注册。我更愿意在消息发出之前而不是在接收端执行条件。通过这样做,我可以节省不必要的流量。谁能给我一个想法或解决此类情况的正确方法?
郑重声明,我不希望收音机中有多个事件处理程序,因为我不知道会有多少房间。
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);
}
}
}