2013-11-06 4 views
3

Я не уверен, что это было задано раньше, но я действительно не знал, как его искать, так как я не совсем уверен, что это за вещь или что я пытаюсь сделать точно так ...Parametrize generic event system

У меня есть общая система обмена сообщениями на основе делегаций, которую я использую в Unity3D - взято из here.

[UA Crosslink]

Он используется так:

// 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); 

Вы получил идею ...

Как это сделать, как я могу написать общий мессенджер, что, когда я хочу добавить еще один общий аргумент, мне не нужно копировать-вставлять и писать целую другую версию , просто потому, что мне нужен дополнительный аргумент?

Большое спасибо!

+0

Спасибо. Я не слышал о MVVM. Я смотрю на это сейчас. Это не мой код, это из вики Unity. Я не уверен, что эта вещь MVVM подходит для Unity. Я не хотел добавлять причину тега Unity3d, действительно, проблема не связана с единством. – vexe

+0

@HighCore btw, в чем проблема, если код C# выглядит слишком java *? Эти два языка похожи во многих отношениях. Что от этого отвратительно? – vexe

+1

'Эти два языка похожи» - Да, в 2001 году это было правдой. Но это уже 2013 год. C# вырос. java не имеет. видя код java, он делает vomit разработчика C# в 2013 году. Тем более, если это действительно код C#, написанный Java-программистом. –

ответ

1

У вас есть мессенджер, но вы, кажется, не отправляете никаких сообщений! Вы пытаетесь отправить содержимое без надлежащего конверта.Оберните значения, которые вы хотите отправить в классе, который представляет ваше фактическое сообщение, и затем вы можете подписаться на тип сообщения, которое будет содержать все значения, которые вы пытались отправить.

public class PlayerSpeedChangedMessage { 
    public Guid PlayerId { get; set; } 
    public int OldSpeed { get; set; } 
    public int NewSpeed { get; set; } 
} 

public class MyMessageHandler { 

    public MyMessageHandler() { 
     Messenger<PlayerSpeedChangedMessage>.Subscribe(OnDead); 
    } 

    HandleSpeedChange(PlayerSpeedChangedMessage message) { 
     // Do stuff with the message 
    } 
} 
+0

Спасибо, Майк, это то, что я на самом деле закончил делать - я нашел систему что делает это - похоже на встроенную систему обработки событий на C#, но это круто, потому что объекты не должны знать друг о друге, чтобы подписаться - см. мой комментарий здесь http://answers.unity3d.com /questions/570483/parametrize-generic-event-system.html#answer-571131 – vexe

0

Я думаю, для разработчиков C#, что класс Message в вики немного устарел. C#, и даже у Unity есть довольно хорошая система обмена сообщениями уже на месте (до тех пор, пока ваши потребности не слишком сложны). Выезд SendMessage и BroadcastMessage.

+0

Спасибо за ваш ответ, но система обмена сообщениями Unity немного медленная - но иногда это полезно. Никогда не использовал его. См. Это сравнение http://forum.unity3d.com/threads/173492-Comparison-of-Unity%28C-%29-Messaging-Events-Systems – vexe

Смежные вопросы