我设置了一个事件总线,只传递所有事件类型的字符串。这很好但现在我想为每种事件类型设置不同的事件参数。我没有看到一种方法来保持所有具有不同事件参数的订阅者集合。我可以为事件参数使用基类型,但是事件处理程序被强制使用基类型,订阅者必须将事件参数强制转换为具体类型(我不想要)。我基本上有类似的东西:
public abstract class PresentationEvent
{
private readonly List<Action<IPresentationEventArgs>> _subscribers = new List<Action<IPresentationEventArgs>>();
public void Subscribe(Action<IPresentationEventArgs> action)
{
_subscribers.Add(action);
}
public void Publish(IPresentationEventArgs message)
{
foreach (var sub in _subscribers)
{
sub.Invoke(message);
}
}
}
public class MessageChangedEvent : PresentationEvent
{
}
public static class EventBus
{
private static readonly Dictionary<Type, PresentationEvent> _mapping = new Dictionary<Type, PresentationEvent>();
private static PresentationEvent GetPresentationEvent<T>() where T : PresentationEvent, new()
{
if (_mapping.ContainsKey(typeof(T)))
{
return _mapping[typeof(T)];
}
var presEvent = new T();
_mapping.Add(typeof(T), presEvent);
return presEvent;
}
public static void Subscribe<T>(Action<IPresentationEventArgs> action) where T: PresentationEvent, new()
{
var presEvent = GetPresentationEvent<T>();
presEvent.Subscribe(action);
}
public static void Publish<T>(IPresentationEventArgs args) where T : PresentationEvent, new()
{
var presEvent = GetPresentationEvent<T>();
presEvent.Publish(args);
}
}
但是在处理这个事件时,我不得不这样做:
private void OnMessageChanged(IPresentationEventArgs x)
{
// do cast here
}
而不是:
private void OnMessageChanged(MessageChangedEventArgs args)
{
label1.Text = args.Message;
}
除了为每个事件类型保留一些具有不同列表的事件字典之外,我不知道如何处理这个问题。我知道那里有第三方库,但我更喜欢自己编写代码。我也看了类似的问题,但没有找到任何东西。如果有人建议如何解决这个问题或其他建议,将不胜感激。
答案 0 :(得分:1)
使用通用接口可以做一些有趣的事情,这是代理人无法做到的。一种可能在这里工作的方法,如果每个使用“事件”的类只需要为每种类型的参数设置一个处理程序,那么就是使用方法IKingEventHandler<T>
定义一个接口InvokeEvent(T param)
,并且有一个方法RaiseKingEvent<TT>(TT param)
,它搜索订阅的处理程序对象列表并调用任何实现IKingEventHandler<TT>
的方法。如果不想为每种类型的处理程序定义单独的参数类型,除了参数类型之外,还可以包括伪类型参数。这种方法会在某种程度上限制可以处理的事件模式,但它会比普通代表具有一些优势:
这不是接口可以做的最有趣的事情,委托不能(接口支持开放泛型方法的能力更有趣)但在某些情况下它可能仍然是一个有用的模式。
答案 1 :(得分:1)
我使用类似的东西来提升域名事件。这是基本的想法(更改了代码,因此未经测试):
public static class EventBus
{
private static List<Delegate> actions;
public static void Register<T>(Action<T> callback) where T : IPresentationEvent
{
if (actions == null)
{
actions = new List<Delegate>();
}
actions.Add(callback);
}
public static void ClearCallbacks()
{
actions = null;
}
public static void Raise<T>(T args) where T : IPresentationEvent
{
if (actions == null)
{
return;
}
foreach (var action in actions)
{
if (!(action is Action<T>))
{
continue;
}
((Action<T>)action).Invoke(args);
}
}
}
<强>更新强>:
我有一个标记界面:
public interface IPresentationEvent
{
}
处理程序看起来像这样:
public interface IHandlePresentationEvent<T> where T : IPresentationEvent
{
void Handle(T args);
}
答案 2 :(得分:1)
如果添加另一个通用参数,则可以使用强类型事件。
public interface IPresentationEventArgs { }
public abstract class PresentationEvent<TPresentationEventArgs> where TPresentationEventArgs : IPresentationEventArgs
{
private readonly List<Action<TPresentationEventArgs>> _subscribers = new List<Action<TPresentationEventArgs>>();
public void Subscribe(Action<TPresentationEventArgs> action)
{
_subscribers.Add(action);
}
public void Publish(TPresentationEventArgs message)
{
foreach (var sub in _subscribers)
{
sub.Invoke(message);
}
}
}
public class MessageChangedEventArgs : IPresentationEventArgs
{
public string Message { get; set; }
}
public class MessageChangedEvent : PresentationEvent<MessageChangedEventArgs>
{
}
public static class EventBus
{
private static readonly Dictionary<Type, Func<Object>> _mapping = new Dictionary<Type, Func<Object>>();
private static T GetPresentationEvent<T, TArgs>()
where T : PresentationEvent<TArgs>, new()
where TArgs : IPresentationEventArgs
{
if (_mapping.ContainsKey(typeof(T)))
{
return _mapping[typeof(T)]() as T;
}
var presEvent = new T();
_mapping.Add(typeof(T), () => presEvent);
return presEvent;
}
public static void Subscribe<T, TArgs>(Action<TArgs> action) where T : PresentationEvent<TArgs>, new()
where TArgs : IPresentationEventArgs
{
var presEvent = GetPresentationEvent<T, TArgs>();
presEvent.Subscribe(action);
}
public static void Publish<T, TArgs>(TArgs args) where T : PresentationEvent<TArgs>, new()
where TArgs : IPresentationEventArgs
{
var presEvent = GetPresentationEvent<T, TArgs>();
presEvent.Publish(args);
}
}
这是一个小型测试程序,用于演示如何使用它:
class Program
{
static void OnMessageChanged(MessageChangedEventArgs args)
{
Console.WriteLine(args.Message);
}
static void Main(string[] args)
{
EventBus.Subscribe<MessageChangedEvent, MessageChangedEventArgs>(OnMessageChanged);
EventBus.Publish<MessageChangedEvent, MessageChangedEventArgs>(new MessageChangedEventArgs{ Message = "Hello world."});
Console.ReadKey();
}
}
您有使用2个通用参数调用subscribe和publish的额外开销,但另一方面,您可以将事件绑定到特定的eventArgs,并且消费者无法为给定事件传递任何任意的eventArgs。他们需要匹配。
这是一个小优化。您可以只添加操作,并允许多播委托的强大功能为您跟踪所有操作,而不是创建自己的操作列表。例如:
public abstract class PresentationEvent<TPresentationEventArgs> where TPresentationEventArgs : IPresentationEventArgs
{
private Action<TPresentationEventArgs> _actions = args => { };
public void Subscribe(Action<TPresentationEventArgs> action)
{
_actions += action;
}
public void Publish(TPresentationEventArgs message)
{
_actions(message);
}
}
<强>更新强>
这是您可以进行订阅的另一种方式。但无论你采用哪种方法,如果你想要静态链接和编译时间检查,那么你需要提供2种类型的参数。
考虑到这一点,这是另一种方式,但你不必避免必须指定2个参数。
public static class IPresentationEventArgsExtensions
{
public static void SubscribeTo<TEvent, TArgs>(this TEvent target, Action<TArgs> action)
where TArgs : IPresentationEventArgs
where TEvent : PresentationEvent<TArgs>, new()
{
EventBus.Subscribe<TEvent, TArgs>(action);
}
}
// Use
Action<MessageChangedEventArgs> messageChangedMethod = OnMessageChanged; // The compiler cannot infer that OnMessageChanged is a Action<IPresentationEventArgs>
new MessageChangedEvent().SubscribeTo(messageChangedMethod);