I have been developing for a while different event systems for games where the listener received a generic event-type object and had to distinguish its real type with a switch or similar and then cast to the correct subclass event.
After different aproaches I was able to get rid of the switch-case using a structure as follows (simplified):
public class Event {}
public class EventA : Event {}
public class EventB : Event {}
public delegate void HanlderDelegate(Event ev);
public class EventManager
{
Dictionary<Type, HanlderDelegate> delegateMap = new Dictionary<Type, HanlderDelegate>();
public void Dispatch(Event ev)
{
if (delegateMap.ContainsKey(ev.GetType()))
{
delegateMap[ev.GetType()].Invoke(ev);
}
}
public void Register<T>(HanlderDelegate handler) where T : Event
{
delegateMap.Add(typeof(T), handler);
}
}
public class EventListener
{
public EventListener(EventManager evtMgr)
{
evtMgr.Register<EventA>(this.HandleEventA);
evtMgr.Register<EventB>(this.HandleEventB);
}
public void HandleEventA(Event ev)
{
EventA evA = (EventA)ev;
//... do stuff
}
public void HandleEventB(Event ev)
{
EventB evB = (EventB)ev;
//... do stuff
}
}
I'm quite happy with this approach, but I still find the casting in each method improvable. I tried to make the delegate more generic
public delegate void HanlderDelegate<T>(T ev) where T : Event; so listeners could directly implement public void HandleEvent(EventA ev) and public void HandleEvent(EventB ev) and register them.
But of course, the dictionary in the EventManager class should store pointers to HanlderDelegate<Event>, and there's where problems begin, I cannot cast HanlderDelegate<EventA> to HanlderDelegate<Event> in order to store them, and at the same time casting it the other way to invoke them.
Is there a way to achieve this? I know the compiler would allow weird stuff, but I'm aware of it and can control by code that EventB is not incorrectly being casted to EventA and so on.
Thanks in advance!
You could make the delegate and the Dispatch method generic, and store the handlers as Delegate rather than HandlerDelegate<T>:
delegate void HandlerDelegate<TEvent>(TEvent ev) where TEvent : Event;
class EventManager
{
Dictionary<Type, Delegate> delegateMap = new Dictionary<Type, Delegate>();
public void Dispatch<TEvent>(TEvent ev) where TEvent : Event
{
Delegate d;
if (delegateMap.TryGetValue(typeof(TEvent), out d))
{
var handler = (HandlerDelegate<TEvent>)d;
handler(ev);
}
}
public void Register<TEvent>(HandlerDelegate<TEvent> handler) where TEvent : Event
{
delegateMap.Add(typeof(TEvent), handler);
}
}
Of course, you still have to cast in the Dispatch method, but at this point you know that the cast is correct.
Related
Suppose I have two classes - one that is provided to me (but suppose I am not allowed to change it as it is maintained by someone else), and one that I control and can change.
// Class A is provided to me by someone else, and suppose I can't modify it
public class A
{
public A()
{
...
}
public void DoSomethingInA()
{
...
}
}
// Class B is what I control
public class B
{
public A MyClassAInstance;
public B(A myClassAInstance)
{
MyClassAInstance = myClassAInstance;
// *** HERE IS WHERE I NEED HELP
// NEED TO WRITE AN EVENT / EVENT HANDLER, WITH / WITHOUT REFLECTION
// THAT RUNS DoSomethingInB WHENEVER MyClassAInstance's DoSomethingInA
// METHOD IS CALLED (AND COMPLETED)
}
public void DoSomethingInB()
{
...
}
}
How can I define a Event / EventHandler in class B that kicks off its DoSomethingInB method whenever the class A instance MyClassAInstance's method DoSomethingInA is called (and completed).
I tried lot of options, but none seem to work.
For instance, I tried:
public class B
{
public A MyClassAInstance;
public B(A myClassAInstance)
{
MyClassAInstance = myClassAInstance;
var eventInfo = GetType().GetEvent("MyEvent");
var methodInfo = myClassAInstance.GetType().GetMethod("DoSomethingInA");
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, myClassAInstance, methodInfo);
eventInfo.AddEventHandler(this, handler);
MyEvent += DoSomethingInB;
}
public event EventHandler MyEvent;
public void DoSomethingInB()
{
...
}
}
But this doesn't work.
Any suggestions or help would be greatly appreciated.
I'm writing down a customized Timer, that extends DispatcherTimer. It has a method called TimerSetUp, in which I associate the Tick event to an EventHandler.
public class MyTimer : DispatcherTimer
{
public MyTimer(){}
public void TimerSetUp(EventHandler<EventArgs> _EventHandler)
{
base.Tick += _EventHandler;
}
}
I have now another couple of classes, StopwatchTimer and CountdownTimer, that extend MyTimer. Let's look at CountdownTimer.
public class CountdownTimer : MyTimer
{
public CountdownTimer()
{
base.TimerSetUp(CountdownTimer_Tick);
}
public void CountdownTimer_Tick(object sender, EventArgs e)
{
//...do something
}
}
When either of them is declared, the constructor must call the base class method TimerSetUp and just set up the event handler.
The problem is that, in the signature of the TimerSetUp function, it gives me this error
Cannot implicitly convert type 'System.EventHandler<System.EventArgs>'
to'System.EventHandler<object>
And I can't get rid of it. If I just put TimerSetUp inside CountdownTimer, and just assign directly the event handler, like this
base.Tick += CountdownTimer_Tick;
it works, but I'd really like to make it work the other way.
So I'd like to know if
1) There's a way I can resolve this
2) Is this a good approach to extensibility of the code?
Nevermind, I made it. I just casted a generic object in the function signature and wrapped a new eventHandler inside the code.
public void TimerSetUp(EventHandler<object> _EventHandler)
{
base.Tick += new EventHandler<object>(_EventHandler);
}
DispatchTimer.Tick seems to be the non generic EventHandler rather than EventHandler<EventArgs>. Since you are using the base EventArgs anyway, they are functionally equivalent:
public void TimerSetUp(EventHandler _EventHandler)
{
base.Tick += _EventHandler;
}
See: https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer.tick(v=vs.110).aspx
I fail to find an answer so far, probably just lacking the appropriate keywords to search for.
I want to implement an Observer Pattern in C#, so any Observer object can subscribe to a Subject object and then receives all its notifications. Then it decides based on the Notification type whether it's important or not.
public class Subject
{
private List<Observer> observers;
public void AttachObserver(Observer Observer)
{
this.observers.Add(Observer);
}
public void DetachObserver(Observer Observer)
{
this.observers.Remove(Observer);
}
public void NotifyObservers(CommonNotification Notification) // who we are, what kind of notification, bla bla
{
foreach(Observer Observer in observers)
{
Observer.OnNotify(Notification);
}
}
}
public class Observer
{
public abstract void OnNotify(CommonNotification Notification);
}
So any object wanting to subscribe to a Subject needs to be an inheritance of the Observer class. But how to do that? My MainForm is based on Form. If I replace the Observer class with a general object it won't implement an OnNotify() event.
What's the point I am missing here? I know I should properly implement it using Event handlers but in order to learn how basic design patterns work I rather implement things myself first.
Short Answer: look at first answer to this question: Super-simple example of C# observer/observable with delegates
I understand you wanting to try and implement it yourself but delegates and events are really the natural fit here (and are in fact an implemention of the observer pattern built into c#).
If still want to do it yourself I would recommend using interfaces instead of abstract/concrete classes.
public interface ISubject
{
void AttachObserver(IObserver observer);
void DetachObserver(IObserver observer);
void NotifyObservers(CommonNotification Notification);
}
public interface IObserver
{
void OnNotify(CommonNotification Notification);
}
Your form could then implement IObserver (or ISubject or both!!).
public class MyForm : Form, IObserver
{
...
}
you can use Interface instead of abstract class like this
public Interface IObserver
{
public void OnNotify(CommonNotification Notification);
}
....
public class MyForm:Form, IObserver {
....
}
You should replace your Observer class with an Interface:
public Interface IObserver
{
public void OnNotify(CommonNotification Notification);
}
Then your mainform (or anything else) can implement IObserver
You can implement it very easily using event. I am giving a sample code -
public class MyForm : Form
{
public event Action btn1Clicked;
private void button1_Click(object sender, EventArgs e)
{
btn1Clicked();
}
}
public abstract class AbsObserver
{
protected MyForm Form;
public AbsObserver(Subject subject)
{
subject.Attach(OnNotify);
Form = new MyForm();
Form.btn1Clicked += Form_btn1Clicked;
}
void Form_btn1Clicked()
{
Console.WriteLine("Do button click task");
}
public abstract void OnNotify();
}
public class Observer1 : AbsObserver
{
public Observer1(Subject subject)
: base(subject)
{
}
public override void OnNotify()
{
Console.WriteLine("observer1 notified");
}
}
public class Observer2 : AbsObserver
{
public Observer2(Subject subject)
: base(subject)
{
}
public override void OnNotify()
{
Console.WriteLine("observer2 notified");
}
}
public class Subject
{
private event Action Notify;
public void Attach(Action a)
{
Notify += a;
}
private void NotifyAll()
{
Notify();
}
}
Here forms are not observers. The observers have the object of form and all form related issues are handled by the observers. This is kind of composting.
My current situation looks like this:
I have these two delegates in a separate file:
public delegate void EventHandler(Object obj, EventArgs e);
public delegate void OtherEventHandler(Object obj, OtherEventArgs e);
I have an Event class that look like this:
class Event {
EventHandler _handler;
public Event(EventHandler handler) {
_handler = handler;
}
}
I have another class that inherits Event this way:
class OtherEvent : Event {
OtherEventHandler _handler;
public OtherEvent (OtherEventHandler handler) : base(handler) {
_handler = handler;
}
}
This one is where the problem occurs. The error is with the part on base(handler). Because handler in OtherEvent is an OtherEventHandler, the base class cannot accept it; the base class only accepts EventHandler.
My intention is to have a "generic" delegate such that when OtherEvent inherits the methods from Event, OtherEvent still has the flexibility to use its own type of delegate with different parameters from its parent for the handler class field.
What can I do to achieve my intention?
Whats wrong with usage of default delegate EventHandler<TEventArgs>? It can be parametrized with any type, inherited from EventArgs.
Also I don't really understand purpose of Event class. You can define events simply this way:
public event EventHandler<OtherEventArgs> OtherEvent;
Perhaps use a generic instead of inheritance?
class Event<TEvent>
{
TEvent _handler;
public Event(TEvent handler)
{
_handler = handler;
}
}
Lets say I have the below code. What is the difference between assigning the actions directly and subscribing to an event?
//Action directly assigned
public class ClassA
{
public Action<string> OnAdd;
private void SomethingHappened()
{
OnAdd("It Happened");
}
}
public class ClassB
{
public ClassB()
{
var myClass = new ClassA();
myClass.OnAdd = Add;
}
private void Add(string Input)
{
//do something
}
}
//Event handlers
public class ClassA
{
public event Action<string> OnAdd;
private void SomethingHappened()
{
if (OnAdd != null)
OnAdd("It Happened"); //Should it be OnAdd.Invoke("It Happened") ???????
}
}
public class ClassB
{
public ClassB()
{
var myClass = new ClassA();
myClass.OnAdd += Add;
}
private void Add(string Input)
{
//do something
}
}
(As an aside, it's hard to explain things when you've used the same type names twice.)
When you use a public field, clients can not only subscribe to events - they can also completely remove other event handlers by assigning instead of adding:
myClass.OnAdd = Add;
They can also invoke the handler directly:
myClass.OnAdd("foo");
Both of these violate the normal pub/sub pattern, where the various subscribers are isolated from one another. Subscribers don't get to overwrite each other's subscriptions (only add or remove their own) and they don't get to raise the event themselves.
For more on events and delegates, see my article on the topic.
You can assign more than one delegates to one event (thus the += operator).
An Event acts like a wrapper around a Delegate to offer protection from reassigning/removing as John has pointed out. I found this quite a good read.