4

Preceding posts:

Event Signature in .NET — Using a Strong Typed 'Sender'?

In a C# event handler, why must the “sender” parameter be an object?


Microsoft's conventions and guidelines force .NET users to use special pattern for creating, raising and handling events in .NET.

Event design guidelines http://msdn.microsoft.com/en-us/library/ms229011.aspx state that


Citation:

The event-handler signature observes the following conventions :

  • The return type is Void.

  • The first parameter is named sender and is of type Object. This is the object that raised the event.

  • The second parameter is named e and is of type EventArgs or a derived class of EventArgs.This is the event-specific data.

  • The method takes exactly two parameters.


These conventions tell developers that the (following) shorter and more obvious code is evil:

public delegate void ConnectionEventHandler(Server sender, Connection connection);

public partial class Server
{
    protected virtual void OnClientConnected(Connection connection)
    {
        if (ClientConnected != null) ClientConnected(this, connection);
    }

    public event ConnectionEventHandler ClientConnected;
}

and the (following) longer and less obvious code is good:

public delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);

public class ConnectionEventArgs : EventArgs
{
    public Connection Connection { get; private set; }

    public ConnectionEventArgs(Connection connection)
    {
        this.Connection = connection;
    }
}

public partial class Server
{
    protected virtual void OnClientConnected(Connection connection)
    {
        if (ClientConnected != null) ClientConnected(this, new ConnectionEventArgs(connection));
    }

    public event ConnectionEventHandler ClientConnected;
}

Though these guidelines not state why is it so important to follow these conventions, making developers act like monkeys who don't know why and what are they doing.

IMHO, Microsoft's event signature conventions for .NET are bad for your code because they cause additional zero-efficiency effort to be spent on coding, coding, coding:

  1. Coding "(MyObject)sender" casts (not speaking about 99% of situations that don't require sender at all)
  2. Coding derived "MyEventArgs" for the data to be passed inside event handler.
  3. Coding dereferences (calling "e.MyData" when the data is required instead of just "data")

It's not that hard to do this effort, but practically speaking what are we loosing when not conforming to Microsoft's conventions, except that people take you as an heretic because your act of confrontation to Microsoft's conventions verges on blasphemy :)

Do you agree?

Community
  • 1
  • 1
Lu4
  • 14,873
  • 15
  • 79
  • 132
  • As a side note, this line is pure evil: `if (ClientConnected != null) ClientConnected(...);`. You should never, *ever* invoke events like that because it assumes that nobody is ever going to remove an event handler from another thread. You risk throwing an NRE here. You should instead do: `var h = ClientConnected; if (h != null) h(...);`. – cdhowie Nov 11 '10 at 18:13
  • 1
    Unfortunately cdhowie your solution for event thread safety won't work. Please check "The Wrong Solution #2, from the Framework Design Guidelines and MSDN" http://www.codeproject.com/Articles/37474/Threadsafe-Events.aspx (I had no intentions making this event thread safe, it's only for the sake of example) But thanks anyway. – Lu4 Nov 11 '10 at 21:22

4 Answers4

3

Regarding having a strongly-typed sender, I've often wondered that myself.

Regarding the EventArgs, I'd still recommend you use an intermediate EventArgs class because you may want to add event information in the future which you don't currently foresee. If you've used a specific EventArgs class all along, you can simply change the class itself and the code where it gets fired. If you pass the Connection as per your example, you'd have to refactor every event handler.

Edit

Jim Mischel made a good point in his comments. By making the sender an object, we enable the same event method to potentially be reused to handle a variety of events. For example, let's say that a grid needs to update itself if:

  • the user clicks a "refresh" button, or
  • the system detects that a new entry has been loaded from the server.

You could then say something like this:

serverBus.EntryReceived += RefreshNeededHandler;
refreshButton.Click += RefreshNeededHandler;

...
public void RefreshNeededHandler(object sender, EventArgs args) 
{
    ...
}

Of course, in practice, I have pretty much never had any call for this kind of reuse, whereas the first thing I tend to to in many, many cases is cast the sender to the object type that I know it has to be. If I want to reuse handlers like this, I think it would be easy enough to make two handlers that both call the same convenience method. For me, an event handler is conceptually supposed to handle a specific type of event on a particular group of objects. So I am not personally convinced that the object sender approach is the best convention.

However, I can imagine cases where this would be extremely handy, like if you want to log every event that gets fired.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • There is no difference where you will put the information that you can't foresee. The difference between "Connection" and "EventArgs" only in their names. – Lu4 Nov 11 '10 at 21:36
  • @Lu4: If the Connection class is only used as an event argument, that may be true. However, I have a feeling that Connection objects are used for a lot more than that. If I'm right, you might some day want to add data that is related to the actual firing of the event, but which wouldn't make sense to put on a Connection object. – StriplingWarrior Nov 11 '10 at 21:57
  • If you go for a strongly-typed sender, then you're limiting the types of objects that can generate events. For example, if the UI controls specified that `Sender` is a `Control`, then only objects that inherit from `Control` can send events to UI controls. That seems to me to be an arbitrary limitation even for the limited case of UI controls. – Jim Mischel Nov 12 '10 at 17:13
  • Yes, I agree with you Jim Mischel, so this means when we are not limiting the sender this allows broader reuse of the delegate which comes with obligations to cast the sender. From the other side absolutely limited sender type will cause a separate implementation of a delegate for each possible sender i.e. then we would have ButtonEventHandler, CheckBoxMouseEventHandler... We should just count which approach causes less code to develop – Lu4 Nov 26 '10 at 23:55
3

The problems you will have:

  1. When you add another argument, you will have to change your event handler signature.

  2. When a programmer first looks at your code, your event handlers will not look like event handlers.

Especially the latter can waste you far more time than writing a 5 line class.

Stu
  • 15,675
  • 4
  • 43
  • 74
  • #2 was my first thought: "No one else will want to play with (ahem-- 'code with') you" – Chris Pfohl Nov 11 '10 at 18:14
  • 1. There is no difference between the way the data is passed inside event handler, would it be argument, or would it be MyEventArgs class field. 2. The purpose of this post is to change the way people think about event handlers, developers who will see this new approach, will be stumbled first 5 seconds, but then they will realize that all the pain they had to come through was pointless – Lu4 Nov 11 '10 at 21:24
  • 2
    Ah, I didn't know your objective was purely political. I thought you were actually asking a question. – Stu Nov 11 '10 at 21:39
2

The biggest problem I see in not following the convention is that you're going to confuse developers who are used to handling events in the way that the runtime library does. I won't say that the convention is good or bad, but it's certainly not evil. .NET developers know and understand how to work with events that are written in conformance with Microsoft's guidelines. Creating your own event handling mechanism on top of that may be more efficient at runtime and might even lead to code that you think is cleaner. But it's going to be different and you'll end up with two event handling "standards" in your program.

My position is that it's better to use a single less-than-ideal standard (as long as it's not horribly broken) than to have two competing standards.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Please describe the way you will get confused when you will see the following method: private void server_ClientConnected(Server sender, Connection connection) { ... } – Lu4 Nov 11 '10 at 21:46
  • @Lu4: Why should he? You've already made up your mind on this, so what good would it do to explain things further? – StriplingWarrior Nov 12 '10 at 00:06
  • @Lu4: confusion *always* results when there is more than one way to do something. If a C# programmer who is accustomed to handling events the .NET way (i.e. the way that the entire Framework handles events) is then faced with a program in which some events are handled the .NET way and others are handled some other way, then he has to remember which types handle events which way. But, hey ... it's your code. You're free to write it however you like. – Jim Mischel Nov 12 '10 at 17:08
  • Well, still I don't agree with you guys, not because I'm willing just to argue (I have better things to spend time on) but because your approach causes me and everybody else to be a superstitious developers. I agree that having two parallel standards is bad. But this is not the case. Because selecting the signature of the event or method is our born right, there shouldn't be a standard there. But before doing that we should understand what consequences it will lead (I mean code, not people). How will our code change if we will use non microsoft signature. Your thoughts on that... – Lu4 Nov 27 '10 at 00:14
1

I used strongly typed events (instead of object as it saves me having to cast), it really isn't that hard to understand, "oh look they've used a type that isn't an object"

As for eventArgs, you should use this in case the object changes as per @StriplingWarrior answer.

I don't understand why devs would get confused over it?

Mr Shoubs
  • 14,629
  • 17
  • 68
  • 107
  • ...especially as the event handler can be generated for you... can't think of a time when I've manually written out the event handling method. – Mr Shoubs Nov 12 '10 at 18:56