Я не уверен, что об этом спрашивали раньше, но я действительно не знал, как его искать, так как я не совсем уверен, как точно называется эта вещь/то, что я пытаюсь сделать...
У меня есть общая система обмена сообщениями на основе делегатов, которую я использую в Unity3D. Взято из здесь.
Он используется следующим образом:
// 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);
}
Проблема, с которой я сталкиваюсь, заключается в том, что код в настоящее время очень не СУХОЙ (много копий и вставок), и я хочу СУХОЙ его, надеюсь, параметризировав его, сделав его более общим.
Я опубликую соответствующий код. Обратите внимание, что вам не обязательно подробно разбираться в коде и в том, что он делает, чтобы ответить.
Вот класс, который делает что-то за сценой:
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);
}
}
}
}
Вот версия, которая принимает один аргумент (я просто копирую и вставляю 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>
и т. д.
Это та часть, которую я пытаюсь устранить, но пока не знаю, как. Точнее, я ищу: только один класс 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);
Вы поняли идею...
Как я могу это сделать, как я могу написать общий мессенджер, чтобы, когда я хочу добавить еще один общий аргумент, мне не нужно было копировать-вставлять и писать совершенно другую версию, просто потому, что мне нужен дополнительный аргумент?
Большое спасибо!
The two languages are similar
- Да, в 2001 году это было правдой. Но сейчас 2013 год. С# вырос. у джавы нет. В 2013 году у разработчика C# возникает рвота при виде java-кода. Гораздо больше, если это действительно код C#, написанный java-программистом. - person Federico Berasategui   schedule 06.11.2013Messenger
реализации MVVM Light есть некоторые особенности WPF, большая часть кода не зависит от пользовательского интерфейса/платформы. I основан только на таких вещах, какAction<T>
иWeakReference
, которые определены вmscorlib
и не зависят от фреймворка пользовательского интерфейса или подобных вещей. Вы можете использовать ихMessenger
и удалить определенные части WPF, такие какDialogMessage
и тому подобное. - person Federico Berasategui   schedule 06.11.2013Make your methods generic
- НравитсяBroadcast<T>, Broadcast<T, U>, Broadcast<T, etc>
? та же проблема. Или вы имели в виду что-то другое? (Кстати, вы не изобретаете велосипед, если вы делаете телевизор, чтобы узнать, как он сделан. Но вы делаете это, чтобы смотреть телевизор) - person vexe   schedule 06.11.2013Otherwise, good luck reinventing the wheel. see ya
такое отношение....... - Не уверен, что ты не хочешь помочь или не можешь. У меня есть простая (должна быть) проблема, и я хочу знать, как ее решить. Мне не нужны готовые вещи. Я хочу учиться. Если вы можете помочь решить проблему, я был бы признателен, если вы не можете / не хотите, просто потому, что код выглядит отвратительно, тогда я действительно думаю, что мы не должны принимать эти комментарии. любая дальнейшая причина, по которой это становится длинным и не по теме. Спасибо. - person vexe   schedule 06.11.2013