8

One thing that annoys me is that in a default Event, Sender is of type object and therefore almost always requires a manual cast before we can use it. Luckily since VB now also supports variance in delegates, we can update the signature of an event in such a way that the sender is strongly typed, see: Event parameter; "sender as Object", or "sender as T"?

Unfortunatly this doesn't work for existing declared events which senders are of type object.

Now one solution would be ofcourse to generate a fake EventHandler which internally takes care of the cast for you. I made a quick example, see:

struct EventHandler<TSender, TEventArgs>
    where TEventArgs: EventArgs
{
    private readonly Action<TSender, TEventArgs> _delegate;

    public EventHandler(Action<TSender, TEventArgs> @delegate)
    {
        if (@delegate == null)
            throw new ArgumentNullException("@delegate");

        _delegate = @delegate;
    }

    public static implicit operator EventHandler<TEventArgs>(EventHandler<TSender, TEventArgs> eventHandler)
    {
        return new EventHandler<TEventArgs>(eventHandler.Execute);
    }

    private void Execute(object sender, EventArgs e)
    {
        TSender typedSender = (TSender)sender;
        TEventArgs typedEventArgs = (TEventArgs)e;

        _delegate(typedSender, typedEventArgs);
    }
}

which can be used as you would expect it to be used:

class Program
{
    event EventHandler<EventArgs> Test;

    static void Main(string[] args)
    {
        new Program().Main();
    }

    void Main()
    {
        Test += new EventHandler<Program, EventArgs>(TestEventHandler);

        Test(this, EventArgs.Empty);
    }

    void TestEventHandler(Program sender, EventArgs e)
    {
        throw new NotImplementedException();
    }
}

Now if I really want to use this, there is alot of work to be done. (The struct should behave just like the original delegate). Yet i do have the feeling that there is either already a great implementation out there, or there is no implementation since there are some major drawbacks i overlooked.

Who can answer me the above question? Any other hints?

Community
  • 1
  • 1
Polity
  • 14,734
  • 2
  • 40
  • 40
  • 3
    Now is doing all this easier or the cast ? – V4Vendetta Oct 10 '11 at 06:08
  • @V4Vendetta: This is a generic solution for avoiding the cast, if you heavily rely on the sender throughout your application then surely this solution would be way more convenient – Polity Oct 10 '11 at 06:11
  • 8
    @John Saunders: You're not very constructive. Since we didnt use lambda's since day 1 why the heck would we use lambda's now. Get over it. I'm not trying to start a revolution, just some curiosity from my side – Polity Oct 10 '11 at 06:27
  • How would you unsubscribe event in your solution? – Muhammad Hasan Khan Oct 10 '11 at 06:34
  • @Polity my point is for unsubscribing you would have to keep refence to your middle man struct. That is not the cleanest way. I'd still go with casting. – Muhammad Hasan Khan Oct 10 '11 at 06:41
  • @HasanKhan: Thanks, that sounds logical. If you update your answer, i'll accept it – Polity Oct 10 '11 at 06:43
  • @Polity: the rest of us have lived with this for a decade. You can, too. – John Saunders Oct 10 '11 at 13:46
  • Sorry, I tend to ignore the "enthusiast programmer" part of this site's purpose, and assume I'm dealing with professionals. If you're not a professional, have fun. If you _are_ a professional, then get over this and get on with your job. – John Saunders Oct 10 '11 at 14:11
  • 2
    @JohnSaunders how on earth can you know the case i'm dealing with and how arrogant can you be to think that approaches not usefull or preferable to you wont be that for somebody else? I assume you are a professional so act like one, jeez... – Polity Oct 10 '11 at 14:27
  • @Polity: you have made no case for being different from the millions of us who have had to deal with this .NET design decision for the past ten years. If you have different requirements, then you should state them. Otherwise, I hope you have fun with this. – John Saunders Oct 10 '11 at 14:35

2 Answers2

4

I can't think of any solution that would have less code than

var original = (OriginalType)sender;

Also if class is yours nothing stops you from creating your own delegate instead of EventHandler delegate

delegate void EventHandler<in TSender, in TArgs>(TSender sender, TArgs args);

This is contravariant in args and sender

People usually also have the reference of the object for which the event is raised and therefore they rarely need to get the thing out of the sender. Could it be possible in your case?

Edit: Since you mentioned you're dealing with events that are not written by you so modifying the delegate is out of question and you probably don't have reference to the object so you have to resort to the sender in that case. Now in your solution you can not unsubscribe to the event. If you modify it to support that then you'll have to keep the reference to this struct of yours which is more work than simple casting. I think casting is still the cleanest solution.

Muhammad Hasan Khan
  • 34,648
  • 16
  • 88
  • 131
  • Thanks Hasan, I know of defining your own delegate. But i'm talking about subscribing to existing events. In my case i have a Winforms application with many many eventhandlers on many controls where i heavily rely on the Sender parameter to do the nessesary action. – Polity Oct 10 '11 at 06:26
-2

I'm of the opinion that using methods as event handlers is now bad OOP practice. Now that we have anonymous delegates we just don't need to make methods just to handle events. Creating an event handler method that could get called by every other method in a class is much like making class members public and letting any class call anything.

In the past we have had to write this:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += button_Click;
    }

    void button_Click(object sender, EventArgs e)
    {
        var button = (Button)sender; //Need to cast here
    }
}

But now we can write this:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += (sender, e) =>
        {
            //Can use `button` here
            //Just ignore `sender`
        };
    }
}

Using anonymous delegates allows us to use the event "sender" reference directly without the need of any casting.

Much cleaner object oriented coding.

Community
  • 1
  • 1
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Having separate event handler methods makes the code reusable. If you're the one writing the code, just don't call them in every other method ;-) – eric Oct 10 '11 at 07:26
  • @eric - You might as well make them public so that all your code has the option "not" to call them. Can I ask how often you have reusable event handlers? – Enigmativity Oct 10 '11 at 08:18
  • @Enigmativity - How about readability? Having all your code inside your main function isnt really friendly in conjuction with tooling like Visual Studio and codemap. Next to that, closures might be unwanted and therefore wastefull (although you really need an high performant app to notice this ofcourse). Lastly, how are you gonna unit test your lambda's? – Polity Oct 10 '11 at 08:26
  • @Polity - I think readability is improved - all your event handling code is together rather than split amongst multiple methods. I don't know what you mean about tooling - my tools work fine. What do you mean unwanted and wasteful? I don't know what performance difference you would get? – Enigmativity Oct 10 '11 at 09:45
  • @Enigmativity readability is a personal preference, i rather have short concrete methods rather then long methods. Why? because of the tooling. For example, in Visual Studio when editing a file in most languages, you can enjoy 2 dropdowns in which you can select and quickly navigate to certain classes and methods. Next to that. Codemap really makes a difference. Regarding performance, I just verified my statement and your right. there is no loss in performance, sorry for the confusion – Polity Oct 10 '11 at 09:54
  • Ten years on, I still agree with my younger self. – Enigmativity Feb 18 '22 at 11:48