C# static practices coming from dynamic background

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);
        }
    }
}

8

One of the most important things when using a programming language is understanding what it’s weaknesses are. As many languages have substantially different weaknesses, it is often a very bad idea to directly port code from one to another.

In both examples you have provided, you are presented with a fairly severe typing issue. Even the dictionary has a large weakness, because you can pass in any key with any type, meaning that failed casts are likely.

If I understand what your goal was, you are trying to build a system for events that work through subscriptions and publishing. The best example I’ve seen of this is PRISM’s EventAggregator. It works something like this:

public class EventAggregator : IEventAggregator
{
  private List<PubSubEvent> events;
  public T Get<T>() where T : PubSubEvent, new()
  {
    var matchingEvent = events.OfType<T>.FirstOrDefault()
    if(matchingEvent != null)
      return matchingEvent;
    matchingEvent = new T();
    events.Add(matchingEvent);
    return matchingEvent;
  }
}

Where the PubSubEvent is something like this:

public abstract class PubSubEvent
{
  private List<Action> actions;
  public void Publish()
  {
    foreach (var action in actions)
      action();
  }
  public void Subscribe(Action action)
  {
    actions.Add(action);
  }
}

In practice, the Get on the event aggregator is static, but I prefer dependency injection if possible. You may not even need the aggregator if you have good dependency injection. The pub sub event is also a bit different, because the PRISM versions take arguments, which is more like what you want. That project is open source, so you may as well look at it.

In any case, an event may be a simple, empty subclass, because the most important thing is the type name. Each event handles it’s argument type, and if you want to subscribe to multiple events, you import multiple events.

Using MEF for dependency injection, for instance, you might do something like this:

[ImportingConstructor]
public SomeClass(DatabaseChangedEvent changedEvent)
{
  changedEvent.Subscribe(ReloadData);
}
private void ReloadData()
{
  ...
}

So it’s important to think about whether or not you even need all your events to be managed by the same object. It may make far more sense to only have any access at all to the exact events you need. There are many ways to do it. You can easily make the base event class generic, where the generic type is the argument class, and every subclass specifies it specifically. That would be an easy solution.

You generally don’t need global things in C#, and static typing can help you quite a bit if you don’t try to fight against it.

Let me ask a question : what you happen if in your PHP version I would write:

// PHP
class Sample
{
    public function __construct()
    {
        $dispatcher = new EventDispatcher();
        $dispatcher->addListener("sample.event", array($this, "onEvent"));
        $dispatcher->dispatch   ("sample.event", new WarnEvent()); // oops?
    }

    public function onEvent(ErrorEvent $event)
    {
        echo "Event Dispatched";
    }
}

I’m not experienced with PHP, but this would probably cause some kind of error behavior. In best case, you can handle it, in worst case, you are going to spend days trying to hunt down the error.

C#’s static type system simply doesn’t allow to even write code like that for above reasons. If you really don’t mind this kind of behavior, you can write:

public static void onEvent(EventArgs args)
{
    if (!(args is ErrorEventArgs))
    {
        // something really wrong just happened
    }
    Console.Out.WriteLine ("Event Dispatched");
}

And about your suggestion of using generics + object. I thinks this might be a good idea. As long as the object is fully encapsulated inside the Dispatcher.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật