I have been dappling in C# after coming from several years in PHP. I don't find the language particularly difficult, although there are considerably more constructs for me to get used to.
My question is to help me change way of thinking when using C#. More specifically, to help me think of solutions in a static language when I've been spoiled by a dynamic one.
For my experiment I have created a simple copy of Symfony's EventDispatcher, which is used as so:
// PHP
class Sample
{
public function __construct()
{
$dispatcher = new EventDispatcher();
$dispatcher->addListener("sample.event", array($this, "onEvent"));
$dispatcher->dispatch ("sample.event", new Event());
}
public function onEvent(Event $event)
{
echo "Event Dispatched";
}
}
And this is my C# replica of EventDispatcher:
// C#
public delegate void EventListener (EventArgs args);
public class EventDispatcher
{
protected Dictionary<string, EventListener> Listeners = new Dictionary<string, EventListener> ();
public void AddListener (string eventName, EventListener listener)
{
EventListener l;
Listeners.TryGetValue (eventName, out l);
if (null == l) {
l = listener;
Listeners.Add (eventName, l);
} else {
l += listener;
}
}
public void Dispatch(string eventName, EventArgs args)
{
EventListener listener;
Listeners.TryGetValue (eventName, out listener);
if (null != listener) {
listener (args);
}
}
}
And finally this is a copy of the PHP example:
// C#
class Application
{
protected static EventDispatcher Dispatcher = new EventDispatcher();
public static void Main(string[] args)
{
Dispatcher.AddListener ("sample.event", onEvent);
Dispatcher.Dispatch ("sample.event", new ErrorEventArgs(new Exception()));
}
public static void onEvent(EventArgs args)
{
Console.Out.WriteLine ("Event Dispatched");
}
}
Now the difference here is I am dispatching the event with ErrorEventArgs
, a child class of EventArgs
. This is fine because the instance is substituted.
However, in PHP I can declare the listener as this to make certain the correct event is given to the method:
// PHP
public function onEvent(ErrorEvent $event)
{
echo "Event Dispatched";
}
However the same in C# will not work:
// C#
public static void onEvent(ErrorEventArgs args)
{
Console.Out.WriteLine ("Event Dispatched");
}
Because it does not match the delegate delegate void EventListener (EventArgs args)
.
Now I know .NET already has an event system and it looks like I am reinventing the wheel, also if you are familiar with Symfony2 you will know that it's event dispatcher is not a typical example of itself (events are usually globalized).
I could in this case only define the methods to conform with the required delegate and use introspection to determine whether the event should be handled. Or I can implement AddListener
and Dispatch
as generic methods, at the cost of defining the dictionary as Dictionary<string, object>
.
So my question boils down to whether I am approaching this the wrong way, whether this type of semi-dynamic programming is suitable, or is there a truely static way of implementing this?
Please bare in mind that this is an example experiment and in the real world I would look for traditional practices to implement this specific scenario.
EDIT:
Using the generics and object dictionary solution, here is the refactor:
public delegate void EventListener<T> (T args);
public class EventDispatcher
{
protected Dictionary<string, object> Listeners = new Dictionary<string, object> ();
public void AddListener<T> (string eventName, EventListener<T> listener)
{
EventListener<T> l;
object obj;
if (Listeners.TryGetValue (eventName, out obj)) {
l = (EventListener<T>)obj; // handling required here if obj cannot be casted.
l += listener;
} else {
l = listener;
Listeners.Add (eventName, l);
}
}
public void Dispatch<T> (string eventName, T args)
{
EventListener<T> l;
object obj;
if (Listeners.TryGetValue (eventName, out obj)) {
l = (EventListener<T>)obj;
l (args);
}
}
}