Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing events with initializer syntax

Tags:

c#

I often want to write something like this:

new Form
{
    Text = "Caption",
    Controls =
    {
        new Button { Text = "Button 1", Click = (s, e) => MessageBox.Show("Button 1 Clicked"), Location = new Point(10, 10) },
        new Button { Text = "Button 2", Click = new EventHandler(Button2Clicked), Location = new Point(10, 40) },
        new Button { Text = "Button 3", Click = Button3Clicked, Location = new Point(10, 70) },
    },
}

Initializer syntax is just sugar, so why can't the compiler figure out how to generate code for an event subscription?

Gimme some sugar, baby!

When initializer syntax was invented, someone must have thought about events and rejected them. I've been trying to imagine what the rationale might have been and am coming up blank.

Is it because an event is a multi-cast object that might have more than one subscriber? No, this is an initialization process; There can be no other subscribers. [Updated] Not true, initializers are applied post-construction and an object can subscribe to its own events.

A note to Eric: I've heard the Why doesn't C# implement feature X speech. In this case, someone was already there, implementing initializers.

Updated

There seems to be contention/confusion because I used Click = in my example. The actual syntax is not relevant to the question. It could just as easily be Click += which mirrors the way you have to add a handler normally. I prefer the former because it's consistant with the rest of the initializer syntax, but ultimately I don't care, just so long as I can subscribe to an event in an initializer list.

Another Update

I do realize that adding the feature now is probably unlikely. The first issue that comes to mind is that Intellisense has to be updated. There are probably many other things that would hinder adding this feature now. My question is: Why didn't they add it in the first place. There must have been something compelling that warrented the 'nay' vote.

like image 505
Tergiver Avatar asked Jan 20 '11 19:01

Tergiver


4 Answers

I cannot see any reason why they could not have provided this small teaspoon of sugar, I guess they just didn't!

There is already quite a lot of syntactic sugar involved in events, if simply declare an event on a class without providing your own implementation, the compiler is providing a delegate backing field for you, plus add / remove 'method' implementations. ALso, when you add an event handler, the compiler uses delegate inference, allowing you to simply point to a method, rather than create a delegate that represents the method.

Interestingly, Mono C# does allow you to add an event handler in an object initializer:

http://tirania.org/blog/archive/2009/Jul-27-1.html

Time to switch to Mono ;-)

like image 153
ColinE Avatar answered Oct 13 '22 14:10

ColinE


Try simply assigning an event:

Click = (o,e) => { <CODE> }

Doesn't work. Initializers work only with things you can directly assign like that. This is because events need to be able to notify anyone they want (you shouldn't be allowed to remove someone else's registration for that event on accident).

I'm not sure if this is their reasoning, but it works for me.

like image 45
Chris Pfohl Avatar answered Oct 13 '22 13:10

Chris Pfohl


There's a big difference between fields and events. There's an excellent article here outlining the differences, but that's the answer to your question: A field can be assigned a value; an event looks like a field but is a very different beast.

Edit

From the article I linked to:

We have seen that the event keyword is a modifier for a delegate declaration that allows it to be included in an interface, constrains its invocation from within the class that declares it, provides it with a pair of customizable accessors (add and remove), and forces the signature of the delegate

Remember that event is a shortcut; behind the scenes, the compiler creates an object with add() and remove() methods. Like:

public class Button {

    public event EventHandler Click {
        void add {...}
        void remove {...}
    }

}

Perhaps this will offer some insight... :

Button btn = new Button {Click += (s, e) => MessageBox.Show("hello")};

The error message you get is "Cannot initialize type 'Button' with a collection initializer because it does not implement IEnumerable"


Still another note... if you assign the event handler from within the form, you can do this:

this.button1.Click += (s, e) => this.textBox1.Text = e.ToString();

You couldn't access form variables from the code you've created. I get where you're coming from, and I don't disagree... what you're doing could be made to work. I guess my point is that there are reasons why the decision was made not to make it work.

like image 33
James King Avatar answered Oct 13 '22 13:10

James King


Yep, should be part of the language!

But, here's a tricky workaround that lets you subscribe to events within an initializer list...

public class TestClass
{
    public class MyButton : Button
    {
        public EventHandler ClickSubscriber
        {
            get { return null; }
            set { Click += value; }
        }
    }

    public static void RunTest()
    {
        new Form
            {
                Text = "Caption",

                Controls =
                    {
                        new MyButton 
                            { 
                                ClickSubscriber = (s, e) => 
                                     MessageBox.Show("Button 1 Clicked"), 
                            },
                    },
            };
    }        
}
like image 32
James Avatar answered Oct 13 '22 13:10

James