Я не уверен, что это было задано раньше, но я действительно не знал, как его искать, так как я не совсем уверен, что это за вещь или что я пытаюсь сделать точно так ...Parametrize generic event system
У меня есть общая система обмена сообщениями на основе делегаций, которую я использую в Unity3D - взято из here.
Он используется так:
// Writing an event listener
void OnSpeedChanged(float speed)
{
this.speed = speed;
}
// Registering an event listener
void OnEnable()
{
Messenger<float>.AddListener("speed changed", OnSpeedChanged);
}
// Unregistering an event listener
void OnDisable()
{
Messenger<float>.RemoveListener("speed changed", OnSpeedChanged);
}
Проблема у меня, что код в настоящее время очень не-DRY (есть много копий пасты), и я хочу СУХОЙ его, надеясь параметризировать его, делая его более общим.
Я отправлю соответствующий код. Обратите внимание, что вам действительно не нужно подробно разбираться в коде и что делать, чтобы ответить.
Вот класс, который делает вещи за сценой:
static internal class MessengerInternal
{
static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
static public readonly MessengerMode DEFAULT_MODE = MessengerMode.REQUIRE_LISTENER;
static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded)
{
if (!eventTable.ContainsKey(eventType)) {
eventTable.Add(eventType, null);
}
Delegate d = eventTable[eventType];
if (d != null && d.GetType() != listenerBeingAdded.GetType()) {
throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
}
}
static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved)
{
if (eventTable.ContainsKey(eventType)) {
Delegate d = eventTable[eventType];
if (d == null) {
throw new ListenerException(string.Format("Attempting to remove listener with for event type {0} but current listener is null.", eventType));
}
else if (d.GetType() != listenerBeingRemoved.GetType()) {
throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
}
}
else {
throw new ListenerException(string.Format("Attempting to remove listener for type {0} but Messenger doesn't know about this event type.", eventType));
}
}
static public void OnListenerRemoved(string eventType)
{
if (eventTable[eventType] == null) {
eventTable.Remove(eventType);
}
}
static public void OnBroadcasting(string eventType, MessengerMode mode)
{
if (mode == MessengerMode.REQUIRE_LISTENER && !eventTable.ContainsKey(eventType)) {
throw new BroadcastException(string.Format("Broadcasting message {0} but no listener found.", eventType));
}
}
}
Теперь у меня есть общие классы мессенджеров, которые не имеют ни один, два, три или даже нет аргументов - Таким образом, пользователь может выбрать подходящее событие обработчик для подписки на событие.
Вот версия, которая не принимает общие аргументы:
// No parameters
static public class Messenger {
private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;
static public void AddListener(string eventType, Callback handler) {
MessengerInternal.OnListenerAdding(eventType, handler);
eventTable[eventType] = (Callback)eventTable[eventType] + handler;
}
static public void RemoveListener(string eventType, Callback handler) {
MessengerInternal.OnListenerRemoving(eventType, handler);
eventTable[eventType] = (Callback)eventTable[eventType] - handler;
MessengerInternal.OnListenerRemoved(eventType);
}
static public void Broadcast(string eventType) {
Broadcast(eventType, MessengerInternal.DEFAULT_MODE);
}
static public void Broadcast(string eventType, MessengerMode mode) {
MessengerInternal.OnBroadcasting(eventType, mode);
Delegate d;
if (eventTable.TryGetValue(eventType, out d)) {
Callback callback = d as Callback;
if (callback != null) {
callback();
} else {
throw MessengerInternal.CreateBroadcastSignatureException(eventType);
}
}
}
}
Вот версия, которая принимает один ARG, (я просто скопировать пасту и добавить T):
// One parameter
static public class Messenger<T> {
private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;
static public void AddListener(string eventType, Callback<T> handler) {
MessengerInternal.OnListenerAdding(eventType, handler);
eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
}
static public void RemoveListener(string eventType, Callback<T> handler) {
MessengerInternal.OnListenerRemoving(eventType, handler);
eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;
MessengerInternal.OnListenerRemoved(eventType);
}
static public void Broadcast(string eventType, T arg1) {
Broadcast(eventType, arg1, MessengerInternal.DEFAULT_MODE);
}
static public void Broadcast(string eventType, T arg1, MessengerMode mode) {
MessengerInternal.OnBroadcasting(eventType, mode);
Delegate d;
if (eventTable.TryGetValue(eventType, out d)) {
Callback<T> callback = d as Callback<T>;
if (callback != null) {
callback(arg1);
} else {
throw MessengerInternal.CreateBroadcastSignatureException(eventType);
}
}
}
}
Как вы могли бы уже догадались, тот, который принимает два аргумента, я просто копирую пасту снова и добавляю еще один общий тип, например <T, U>
и т. д.
Это та часть, которую я пытаюсь сделать liminate - Но все же я понятия не имею, как. Точнее, то, что я ищу это: только один Messenger
класс, но все же я могу сделать:
Messenger<float>.Subscribe("player dead", OnDead);
Messenger<int, bool>.Subscribe("on something", OnSomething);
Messenger<bool, float, MyType>.Subscribe(stuff);
Или, (на самом деле не имеет значения, какой)
Messenger.Subscribe<float> ("player dead", OnDead);
Вы получил идею ...
Как это сделать, как я могу написать общий мессенджер, что, когда я хочу добавить еще один общий аргумент, мне не нужно копировать-вставлять и писать целую другую версию , просто потому, что мне нужен дополнительный аргумент?
Большое спасибо!
Спасибо. Я не слышал о MVVM. Я смотрю на это сейчас. Это не мой код, это из вики Unity. Я не уверен, что эта вещь MVVM подходит для Unity. Я не хотел добавлять причину тега Unity3d, действительно, проблема не связана с единством. – vexe
@HighCore btw, в чем проблема, если код C# выглядит слишком java *? Эти два языка похожи во многих отношениях. Что от этого отвратительно? – vexe
'Эти два языка похожи» - Да, в 2001 году это было правдой. Но это уже 2013 год. C# вырос. java не имеет. видя код java, он делает vomit разработчика C# в 2013 году. Тем более, если это действительно код C#, написанный Java-программистом. –