【问题标题】:Initializing events with initializer syntax使用初始化器语法初始化事件
【发布时间】:2011-01-20 19:49:24
【问题描述】:

我经常想这样写:

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

初始化器语法只是糖,为什么编译器不能弄清楚如何为事件订阅生成代码?

给点糖,宝贝!

当初始化器语法被发明时,一定有人考虑过事件并拒绝了它们。我一直在尝试想象其中的原因可能是什么,但我一直处于空白状态。

是因为一个事件是一个多播对象,可能有多个订阅者吗?不,这是一个初始化过程;不能有其他订阅者。 [已更新]不正确,初始化器在构造后应用,对象可以subscribe to its own events

给 Eric 的说明:我听说过为什么 C# 不实现特性 X 演讲。在这种情况下,已经有人在那里实现了初始化程序。

更新

似乎存在争用/混淆,因为我在示例中使用了Click =。实际语法与问题无关。它可以很容易地成为Click +=,它反映了您必须正常添加处理程序的方式。我更喜欢前者,因为它与初始化器语法的其余部分一致,但最终我不在乎,只要我可以订阅初始化器列表中的事件即可。

另一个更新

我确实意识到现在添加该功能可能不太可能。想到的第一个问题是 Intellisense 必须更新。现在可能还有许多其他事情会阻碍添加此功能。我的问题是:他们为什么不首先添加它。一定是有什么令人信服的东西阻止了“反对”的投票。

【问题讨论】:

  • 事件不适用于分配。你必须使用 +=,所以这个语法是不正确的(因为 Click 是一个事件)对吧?
  • 我认为这与“当前的合法语法”无关。这是一种附加语法,特定于初始化程序。

标签: c#


【解决方案1】:

我看不出他们为什么不能提供这一小茶匙糖,我猜他们只是没有!

事件中已经涉及到相当多的语法糖,如果只是在类上声明一个事件而不提供您自己的实现,编译器会为您提供一个委托支持字段,加上添加/删除“方法”实现。此外,当您添加事件处理程序时,编译器使用委托推断,允许您简单地指向一个方法,而不是创建一个代表该方法的委托。

有趣的是,Mono C# 确实允许您在对象初始化程序中添加事件处理程序:

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

是时候切换到单声道了 ;-)

【讨论】:

  • 好吧,至少 Mono 的人在我的角落里。
  • 这点糖是错的。您不应该能够在对象初始值设定项之外的 内将某些内容分配给事件成员。这是一个事件,而不是可设置的属性。
【解决方案2】:

尝试简单地分配一个事件:

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

不起作用。初始化器仅适用于您可以直接分配的东西。这是因为活动需要能够通知他们想要通知的任何人(您不应该被允许在意外情况下删除其他人对该活动的注册)。

我不确定这是否是他们的推理,但它对我有用。

【讨论】:

  • 我再次提出相反的论点,即在初始化期间事件保证为空,但如果人们更喜欢它,可以说语法应该是Click += (s, e) =&gt; { &lt;CODE&gt; }
  • 不一定。如果您的初始化程序使用任何方法来设置值,那么该方法可能会产生订阅事件的副作用。
  • 啊,你提出了我最喜欢的一个问题。首先,您有一个必须考虑的有效点(在我的脑海中由 += 语法解决)。二、这是我对自助订阅的感受:tergiver.wordpress.com/2011/01/20/self-subscription-is-asinine
【解决方案3】:

fieldsevents 之间有很大的不同。有一篇很棒的文章 here 概述了差异,但这就是您问题的答案:可以为字段分配值;一个事件看起来像一个领域,但却是一个非常不同的野兽。

编辑

来自我链接到的文章:

我们已经看到 event 关键字是委托声明的修饰符,它允许它包含在接口中,从声明它的类中限制它的调用,为它提供一对可定制的访问器(添加和删​​除),并强制委托签名

记住event是一个快捷方式;在幕后,编译器使用add()remove() 方法创建一个对象。喜欢:

public class Button {

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

}

也许这会提供一些见解...:

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

您收到的错误消息是“无法使用集合初始化程序初始化类型 'Button',因为它没有实现 IEnumerable”


还要注意...如果您从表单中分配事件处理程序,您可以这样做:

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

您无法从您创建的代码中访问表单变量。我知道你来自哪里,我不反对......你正在做的事情可以起作用。我想我的观点是,做出决定让它发挥作用是有原因的。

【讨论】:

  • 我认为,从初始化的角度来看,字段和事件之间没有区别。
  • 感谢您的意见。我与您无法从您创建的代码中访问表单变量 发生争执。我提供的代码中没有“表单变量”。它们不是必需的。事实上,它们从来没有必要。表单设计者将它们提供给您(除非您告诉它不要这样做)只是为了方便。
  • Initializer 语法用于初始化字段或属性。属性与事件的关系与字段的关系相同。它们都是一对合成方法或程序员提供的一对方法的糖。所以我认为字段与事件的比较无效。您的错误消息也没有洞察力,因为大括号初始化根据上下文有两种含义。编译器决定您尝试进行集合初始化,但这也是无效的。
【解决方案4】:

是的,应该是语言的一部分!

但是,这里有一个棘手的解决方法,可以让您订阅初始化列表中的事件...

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 2018-07-15
    相关资源
    最近更新 更多