聚合事件源和重新调度事件的最佳方法

时间:2012-05-30 18:58:35

标签: c# events architecture lambda

我正在尝试找到创建系统的最佳方法,其中可以将事件源添加到管理器类中,然后管理器类将其事件重新分派给侦听器。具体来说,我有许多不同的输入源(键盘输入源,鼠标输入源,虚拟键盘输入源等),我想让开发人员在键盘输入源和输入上监听KeyDown事件。经理本身(从任何有效的输入源捕获此事件)。

很容易暴力破解解决方案,最终我创建了许多“调度”功能,只需在事件发生时重新调度事件,但我最终拥有数十个单行函数,我必须创建新函数每当将新事件添加到输入源接口时。

我考虑过使用lambdas,但是如果从管理器中删除了输入源,我需要一种解开事件的方法。我可以将lambda保存在字典中,由输入源键入,但许多事件具有不同的arg类,并且创建多个字典来实现这一点开始变得丑陋。

我想知道我是否缺少一些简单的方法来保持清洁,并保留我需要写下的额外代码。

作为参考,这里是我正在使用的对象的示例:

public interface IInputSource {}

public interface IKeyboardInputSource : IInputSource
{
    event EventHandler<KeyboardEventArgs> KeyDown;
    event EventHandler<KeyboardEventArgs> KeyUp;
}

public interface IMouseInputSource : IInputSource
{
    event EventHandler<MouseEventArgs> MouseDown;
    event EventHandler<MouseEventArgs> MouseUp;
}

public class InputManager : IKeyboardInputSource, IMouseInputSource
{
    private List<IInputSource> InputSources;

    //Event declarations from IKeyboardInputSource and IMouseInputSource

    public void AddSource(IInputSource source)
    {
        InputSources.Add(source);

        if (source is IKeyboardInputSource)
        {
            var keyboardSource = source as IKeyboardInputSource;
            keyboardSource.KeyDown += SendKeyDown;
            // Listen for other keyboard events...
        }
        if (source is IMouseInputSource)
        {
            // Listen for mouse events...
        }
    }

    public void RemoveSource(IInputSource source)
    {
        if (source is IKeyboardInputSource)
        {
            var keyboardSource = source as IKeyboardInputSource;
            keyboardSource.KeyDown -= SendKeyDown;
            // Remove other keyboard events...
        }
        if (source is IMouseInputSource)
        {
            // Remove mouse events...
        }

        InputSources.Remove(source);
    }

    private void SendKeyDown(object sender, KeyboardEventArgs e)
    {
        if (KeyDown != null) 
            KeyDown(sender, e);
    }

    //Other "send" functions
}

2 个答案:

答案 0 :(得分:1)

你看过Reactive Extensions(Rx)框架了吗?看起来它会满足您的要求,并为您提供丰富的功能/ lambda,如api来管理和处理事件。

  

Reactive Extensions(Rx)是一个库,用于使用可观察序列和LINQ样式查询运算符来编写异步和基于事件的程序

答案 1 :(得分:0)

这样的事情可能会有所帮助 - 这是一种通用方法,包括直接订阅事件或通过“接收器”界面

interface IInputSource<T> where T : EventArgs
{
    event EventHandler<T> InputEvent;
}
interface IInputSink<in T> where T : EventArgs
{
    void InputMessageHandler(object sender, T eventArgs);
}

internal class InputManager
{
    private Dictionary<Type, object> _inputSources;
    private Dictionary<Type, object> _inputSinks;
    private Dictionary<Type, object> _events;

    public void AddSource<T>(IInputSource<T> source) where T : EventArgs
    {
        _inputSources[typeof(T)] = _inputSources;      //add source
        _events[typeof(T)] = (EventHandler<T>)Dispatch; //register event for subscribers

        source.InputEvent += Dispatch;
        source.InputEvent += Dispatch2;
    }


    // Dispatch trough direct event subscriptions;
    private void Dispatch<T>(object sender, T e) where T : EventArgs
    {
        var handler = _events[typeof(T)] as EventHandler<T>;
        handler.Invoke(sender, e);
    }
    // Dispatch trough IInputSink subscriptions;
    private void Dispatch2<T>(object sender, T e) where T : EventArgs
    {
        var sink = _inputSinks[typeof(T)] as IInputSink<T>;
        sink.InputMessageHandler(sender, e);
    }

    //Subscription:  Client should provide handler into Subscribe()
    //or subscribe with IInputSink<MyEvent> implementation (Subscribe2())
    public void Subscribe<T>(EventHandler<T> handler) where T : EventArgs
    {
        var @event = _events[typeof(T)] as EventHandler<T>;
        _events[typeof(T)] = @event + handler;
    }

    public void Subscribe2<T>(IInputSink<T> sink) where T : EventArgs
    {
        _inputSinks[typeof(T)] = sink;
    }
}
class XXXX : EventArgs
{

}
public class Sink: IInputSink<XXXX>
{
    #region Implementation of IInputSink<in XXXX>

    public void InputMessageHandler(object sender, XXXX eventArgs)
    {
        throw new NotImplementedException();
    }

    #endregion

    public Sink() 
    {
        var v = new InputManager();
        v.Subscribe<XXXX>(GetInputEvent);
        v.Subscribe2(this);
    }

    private void GetInputEvent(object sender, XXXX xxxx)
    {
        throw new NotImplementedException();
    }
}