How to implement an event system - c#

Im trying to implement an Event System for a game, where there are classes that can fire or handle an event wheter or not they implement these interfaces:
public interface IGameEvent<T> where T : EventArgs
{
event EventHandler<T> OnEvent;
}
public interface IGameHandler<T> where T : EventArgs
{
void OnEvent(object sender, T e);
}
everything looked great until i realized that no class can implement more than 1 IGameEvent
because it would cause duplicate declaration,
Here is an example:
public class Event
{
public KeyPressEvent OnKeyPress;
public UpdateEvent OnUpdate;
public void AddHadler<T>(IGameEvent<T> eEvent , IGameHandler<T> eHandler) where T : EventArgs
{
eEvent.OnEvent += eHandler.OnEvent;
}
public void RemoveHandler<T>(IGameEvent<T> eEvent, IGameHandler<T> eHandler) where T : EventArgs
{
eEvent.OnEvent -= eHandler.OnEvent;
}
}
KeyPressEvent:
public class KeyPressEvent : IGameEvent<KeyPressEvent.KeyPressedEventArgs>
{
public class KeyPressedEventArgs : EventArgs
{
public KeyPressedEventArgs(Keys key)
{
Key = key;
}
public Keys Key { get; private set; }
}
public event EventHandler<KeyPressedEventArgs> OnEvent;
private void OnCheckForKeyPressed() //Example
{
if (OnEvent != null)
OnEvent(this, new KeyPressedEventArgs(Keys.Space));
}
}
Would be better to manually store the suscribers in a list in the EventSystem ?
How slower or faster than this approach that would be?
Thanks!

The common approach is to implement events aggregator pattern. Google search will provide you with tons of different variations. For example, it might look like this (what i am currently using):
interface IEventsAggrgator
{
//fires an event, reperesented by specific message
void Publish<TMessage>(TMessage message);
//adds object to the list of subscribers
void Subscribe(object listener);
//remove object from the list of subscribers
void Unsubscribe(object listener);
}
interface IHandler<TMessage>
{
//implement this in subscribers to handle specific messages
void Handle(TMessage message);
}

Related

Event handler inheritance

I have a parent class that is firing an event to derived classes. The problem is that the event handler is alway null.
Class Plugin()
{
public delegate void BufferReadyHandler(string str);
public event BufferReadyHandler OnBufferReady;
public ClassPlugin(eGuiType _guyType)
{
GuiType = _guyType;
}
protected void Sp_DataReceived_Parent(object sender, SerialDataReceivedEventArgs e)
{
strCommonBuffer += serial.ReadExisting();
if (strCommonBuffer.Contains("\r\n"))
{
if (OnBufferReady != null) <<-------NULL
OnBufferReady(strCommonBuffer);
strCommonBuffer = string.Empty;
}
}
}
then there are some derived classes that are linked to that event:
class ClassIO : ClassPlugin
{
public ClassIO(eGuiType _guyType) : base(_guyType)
{
...
OnBufferReady += ClassIO_OnBufferReady;
}
private void ClassIO_OnBufferReady(string str)
{
...
}
}
the problem is that the OnBufferReady event in the parent class is alway null and therefore never fired.
Thanks for any help.
I might be wrong but have you thought about making the event static?
public delegate void BufferReadyHandler(string str);
public static event BufferReadyHandler OnBufferReady;
I am not sure why you are having this problem, I suspect it has something to do with the code you have not shown us. However in this situation I would not have the child subscribe to the event at all, instead make a protected method that raises the event that the child can override.
Here is how I would implement the class.
public class BufferReadyEventArgs : EventArgs
{
public BufferReadyEventArgs(string commonBuffer)
{
CommonBuffer = commonBuffer;
}
public string CommonBuffer {get; private set;}
}
Class Plugin()
{
public event EventHandler<BufferReadyEventArgs> OnBufferReady;
public ClassPlugin(eGuiType _guyType)
{
GuiType = _guyType;
}
protected void Sp_DataReceived_Parent(object sender, SerialDataReceivedEventArgs e)
{
strCommonBuffer += serial.ReadExisting();
if (strCommonBuffer.Contains("\r\n"))
{
RaiseOnBufferReady(strCommonBuffer);
strCommonBuffer = string.Empty;
}
}
protected virtual void RaiseOnBufferReady(string commonBuffer)
{
var temp = OnBufferReady;
if(temp != null)
temp(this, new BufferReadyEventArgs(commonBuffer));
}
}
class ClassIO : ClassPlugin
{
public ClassIO(eGuiType _guyType) : base(_guyType)
{
...
}
protected override void RaiseOnBufferReady(string commonBuffer)
{
base.RaiseOnBufferReady(commonBuffer);
...
}
}
Here is a working example based on your code:
using System;
using System.Collections.Generic;
public class MyClass
{
public static void Main()
{
ClassIO c = new ClassIO();
c.DataReceived();
Console.ReadLine();
}
}
public class ClassPlugin
{
public delegate void BufferReadyHandler(string str);
public event BufferReadyHandler OnBufferReady;
public ClassPlugin()
{
}
public void DataReceived()
{
if (OnBufferReady != null) {
OnBufferReady("Calling OnBufferReady");
}
}
}
public class ClassIO : ClassPlugin
{
public ClassIO() : base()
{
OnBufferReady += ClassIO_OnBufferReady;
}
private void ClassIO_OnBufferReady(string str)
{
Console.WriteLine("Inside ClassIO_OnBufferReady");
}
}
I don't understand why you would like to work with events in the first place for communication between parent and derived class.
If you need this communication, you would be better of with an (abstract) method in your base class that you implement in your derived classes.
If you need communication to all instances of derived types, you should look into composition instead of inheritance. Make some sort of manager instance that holds references to a list of instances of that base type and invokes a certain method on each of them in case of an 'event'.

Overriding events of a base event to an application and sequence of flow c#

What is the best way or best practice to propagate events from a base class (and handle them at the base as well) to the inherited or implementing application - because I want all the code to execute from the base to the MyClientListener to the Winform ?
I have a [WCF DuplexClient] class which other [WCF ClientListener] classes will derive from. I want to make it reusable for all of my services. I have an event InnerChannel_Faulted - in this base class I have an initializer in the base class which subscribes to events and the base class will generally handle these as far as the WCF side of things goes. I want to also be able to have my specific ClientListener implementation to be able to provide additional functionality - behavior to those events - mostly for the Winforms Application.
Is my thinking right here - or do I need to regurgitate the events up the food chain so they will be available to the Winforms app ?
I have made the handlers in the base class like this:
class MyClient<T> :DuplexClientBase<T> where T : class
{
protected virtual void InitializeClient()
{
base.InnerChannel.Faulted += InnerChannel_Faulted;
}
protected virtual void InnerChannel_Faulted(object sender, EventArgs e)
{
// ... do something()
}
}
class MyListener : MyClient<MyListenerService>
{
public MyListener(){ // do stuff}
// .. other methods
}
WINDOWFORMAPP : FORM
{
private MyListener mylistener = new MyListener();
WINDOWFORMAPP()
{
// somehow subscribe to
mylistener.InnerChannel_Faulted +=
}
private override void InnerChannel_Faulted(object sender, EventArgs e)
{
// DoSomething to GUI - notifications GUI elements etc..
// then call.
mylistener.InnerChannel_Faulted()
}
}
It's not standard to subscribe to the event from the same class or subclass. The usual approach is to structure the code as:
public class MyClass {
public event EventHandler SomeAction;
private void DoStuff() {
bool fireAction = false;
//....
if (fireAction) {
EventArgs e = ...; // can be more specific if needed
OnSomeAction(e);
}
}
protected virtual void OnSomeAction(EventArgs e) {
if (SomeAction != null)
SomeAction(this, e);
}
}
public class MySubclass : MyClass {
protected override void OnSomeAction(EventArgs e) {
// code before event is triggered
base.OnSomeAction(e); // fires event to listeners
// code after event is triggered
}
}
Then in your form:
public class MyForm : Form {
MyClass mc = new MyClass();
public MyForm() {
mc.SomeAction += mc_SomeAction;
}
private void mc_SomeAction(Object sender, EventArgs e) {
//...
}
}

How to make an event which gets triggered in another class

In my code for the PluginManager the event PluginEvent gets triggered after
a plugin has been added. But I want to get the event also triggered in the test class.
Somehow I cant solve this problem. The event only gets triggered in the PluginManager class. I read some articles how to create events and so on, but I got even more confused
PluginManager class
public class PluginEventArgs
{
public PluginEventArgs(string s) { Text = s; }
public String Text { get; private set; } // readonly
}
public class PluginManager
{
// Declare the delegate (if using non-generic pattern).
public delegate void PluginEventHandler(object sender, PluginEventArgs e);
// Declare the event.
public event PluginEventHandler PluginEvent;
protected virtual void RaiseSampleEvent(string message)
{
if (PluginEvent != null)
PluginEvent(this, new PluginEventArgs(message));
}
public PluginManager()
{
PluginEvent += PluginManager_PluginEvent;
SomeMethod();
}
void PluginManager_PluginEvent(object sender, PluginEventArgs e)
{
//This event gets triggered =)
}
public void SomeMethod()
{
//Code
RaiseSampleEvent("Name of the Plugin");
//Code
}
}
My test class:
class test
{
public test()
{
PluginManager pluginMg = new PluginManager();
pluginMg.PluginEvent += pluginMg_PluginEvent;
}
//I want this event to get triggered when a new plugin has been found
void pluginMg_PluginEvent(object sender, PluginEventArgs e)
{
MessageBox.Show(e.Text);
}
}
How can I manage to get the event triggered in the test class?
Thanks for any advise!
You're actually doing things right except for one logical Mistake.
In your test class you're creating the PluginManager by using the constructor. The constructor of PluginManager first subscribes to the event and then raises it.
AFTERWARDS you're subscribing to that event.
The simple Problem is that when you are raising the event your test class has not subscribed yet. When you raise that event again everything should work out just fine.
Another thing is that I would use the generic EventHandler class instead of creating your own delegates. This keeps your code cleaner and everyone knows that this is meant to be an event at first glance.
Just inherit PlugInEventArgs from EventArgs and then use EventHandler.
In your PluginManager class you shouldn't subscribe to your own event PluginEvent, you should subscribe to an external event or just raise the PluginEvent.
Let me give you an example:
public class PluginEventArgs
{
public PluginEventArgs(string s) { Text = s; }
public String Text { get; private set; } // readonly
}
public class OtherClass
{
public event PluginEventHandler PluginEvent;
private void RaiseEvent()
{
if (null != PluginEvent)
PluginEvent(this, new PluginEventArgs("some message"));
}
}
public delegate void PluginEventHandler(object sender, PluginEventArgs e);
public class PluginManager
{
public event PluginEventHandler PluginEvent;
private OtherClass otherClass;
protected virtual void RaiseSampleEvent(string message)
{
if (PluginEvent != null)
PluginEvent(this, new PluginEventArgs(message));
}
public PluginManager(OtherClass otherClass)
{
this.otherClass = otherClass;
this.otherClass.PluginEvent += otherClass_PluginEvent;
SomeMethod();
}
void otherClass_PluginEvent(object sender, PluginEventArgs e)
{
if (PluginEvent != null)
PluginEvent(sender, e); // this way the original sender and args are transferred.
}
public void SomeMethod()
{
//Code
RaiseSampleEvent("Name of the Plugin");
//Code
}
}
class test
{
public test()
{
OtherClass otherClass = new OtherClass();
PluginManager pluginMg = new PluginManager(otherClass);
pluginMg.PluginEvent += pluginMg_PluginEvent;
}
//I want this event to get triggered when a new plugin has been found
void pluginMg_PluginEvent(object sender, PluginEventArgs e)
{
MessageBox.Show(e.Text);
}
}

Best way to aggregate event sources and redispatch events

I'm trying to find the best way to create a system where event sources can be added to a manager class, which will then re-dispatch their events to listeners. Specifically, I have many different input sources (keyboard input source, mouse input source, virtual keyboard input source, etc) and I'd like to allow developers to listen for, say, the KeyDown event on both the keyboard input source and the input manager itself (to catch this event from any active input source).
It's easy to brute-force a solution where I end up creating many "dispatch" functions, that simply re-dispatch events when they come through, but I end up having dozens of single line functions and I have to create new functions whenever a new event is added to an input source interface.
I've considered using lambdas, but I need a way to unhook the events if an input source is removed from the manager. I can keep the lambda in a dictionary, keyed by input source, but many of the events have different arg classes, and creating multiple dictionaries to do this starts to get ugly.
I'm wondering if I'm missing some simple way of doing this which keeps things clean and keeps the amount of additional code I need to write down.
For reference, here's a sample of the objects I'm working with:
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
}
Have you looked at the Reactive Extensions (Rx) framework? Looks like it will what you are asking for and gives you a rich functional/lambda like api to manage and process events.
The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators
Probably something like this would help - it's a generic approach, with both direct event subscription or via 'sink' interface
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();
}
}

Collection of Events

I am using events as a publisher/subscriber pattern in c#. However I dont know at design time how many publishers my program will be using. I would like to dynamically add events to either a class directly, or more plausibly to a collection/dictionary containing the events.
Are either of these scenarios possible using C#?
Create a mediator that your publishers publish to and that your subscribers subscribe to. For example:
public class Mediator
{
public static readonly Mediator Current = new Mediator();
public event EventHandler<EventArgs> EventRaised;
public void RaiseEvent(object sender, EventArgs eventArgs)
{
if (EventRaised!=null)
EventRaised(sender, eventArgs);
}
}
public class PublisherEventArgs : EventArgs
{
public string SomeData { get; set; }
}
public class Publisher
{
public void Publish(string data)
{
Mediator.Current.RaiseEvent(this, new PublisherEventArgs() { SomeData = data} );
}
}
public class Subscriber
{
public Subscriber()
{
Mediator.Current.EventRaised += HandlePublishedEvent;
}
private void HandlePublishedEvent(object sender, EventArgs e)
{
if(e is PublisherEventArgs)
{
string data = ((PublisherEventArgs)e).SomeData;
// todo: do something here
}
}
}
Make sure you implement IDisposable on your subscriber (its not in my example) so that it unsubscribes from the Mediator during dispose.
You'll need to disconnect the event from it's source in order to do this. This is commonly done with an event aggregator; it manages clients that want to publish events as well as those that want to subscribe to events. All of this decouples the publishers from the listeners and allows you to do what you are describing.
Prism has an event aggregator out-of-the-box that you can use in the form of the IEventAggregator interface.

Categories

Resources