35

关于 C# 语言设计的小问题 :))

如果我有这样的界面:

interface IFoo {
  int Value { get; set; }
}

可以使用 C# 3.0 自动实现的属性显式实现这样的接口:

sealed class Foo : IFoo {
  int IFoo.Value { get; set; }
}

但是如果我在界面中有一个事件:

interface IFoo {
  event EventHandler Event;
}

并尝试使用类似字段的事件显式实现它:

sealed class Foo : IFoo {
  event EventHandler IFoo.Event;
}

我将收到以下编译器错误:

error CS0071: An explicit interface implementation of an event must use event accessor syntax

我认为类场事件是自动实现属性的某种二元论。

所以我的问题是:做这种限制的设计原因是什么?

4

4 回答 4

32

有趣的问题。我在语言笔记档案中翻了一番,发现这个决定是在 1999 年 10 月 13 日做出的,但这些笔记并没有给出这个决定的理由。

在我的脑海中,我没有看到任何理论或实际原因说明我们不能有类似字段的明确实现的事件。我也看不出我们特别需要这样做的任何理由。这可能仍然是未知的谜团之一。

于 2010-02-15T19:43:33.917 回答
29

我想这可能与您无法从该类的其他成员调用显式接口实现的事实有关:

public interface I
{
    void DoIt();
}

public class C : I
{
    public C()
    {
        DoIt(); // error CS0103: The name 'DoIt' does not exist in the current context
    }

    void I.DoIt() { }
}

请注意,您可以通过先向上转换到接口来调用该方法:((I)this).DoIt();. 有点难看,但它的工作原理。

如果事件可以按照 ControlFlow(OP)的建议显式实现,那么您将如何实际提出它们?考虑:

public interface I
{
    event EventHandler SomethingHappened;
}

public class C : I
{
    public void OnSomethingHappened()
    {
        // Same problem as above
        SomethingHappened(this, EventArgs.Empty);
    }

    event EventHandler I.SomethingHappened;
}

在这里,您甚至不能通过首先向上转换到接口来引发事件,因为事件只能从实现类中引发。因此,为显式实现的事件要求访问器语法似乎是非常有意义的。

于 2012-02-07T15:28:48.980 回答
23

当显式实现在接口中声明的事件时,您必须手动提供通常由编译器提供的添加和删除事件访问器。访问器代码可以将接口事件连接到您的类中的另一个事件或它自己的委托类型。

例如,这将触发错误 CS0071:

public delegate void MyEvent(object sender);

interface ITest
{
    event MyEvent Clicked;
}

class Test : Itest
{
    event MyEvent ITest.Clicked;  // CS0071
    public static void Main() { }
}

正确的方法是:

public delegate void MyEvent(object sender);

interface ITest
{
    event MyEvent Clicked;
}

class Test : Itest
{
    private MyEvent clicked;

    event MyEvent Itest.Clicked
    {
        add
        {
            clicked += value;
        }

        remove
        {
            clicked -= value;
        }
    }

    public static void Main() { }
}

请参阅编译器错误 CS0071

于 2010-02-15T20:04:43.977 回答
1

这实际上不是我自己的原创想法。

但是,我想我可能会回应这个:

“在我的脑海中,我没有看到任何理论上或实践上的原因为什么我们不能有类似场的明确实施的事件。我也没有看到我们特别需要的任何理由。这可能仍然是谜团之一未知的。” ——埃里克·利珀特


A Programmer's Introduction to C#第二版的第 23 章中,Eric Gunnerson 写道:

“[I]如果另一个类也想在单击按钮时被调用,则可以使用 += 运算符,如下所示:

button.Click += new Button.ClickHandler(OtherMethodToCall);

不幸的是,如果其他类不小心,它可能会执行以下操作:

button.Click = new Button.ClickHandler(OtherMethodToCall);

这会很糟糕,因为这意味着我们的 ButtonHandler 将被取消挂钩,并且只会调用新方法。”

...

“需要某种保护委托字段的方法,以便只能使用 += 和 -= 访问它。”


他在接下来的几页中继续评论包括 add() 和 remove() 方法来实现此行为;能够直接写入这些方法以及为不需要的委托引用分配存储空间的结果。

我会补充更多,但我太尊重作者了,未经他的许可就这样做了。我建议找到这本书的副本,并且一般会推荐 Eric Gunnerson 的任何内容(博客等...)

无论如何,我希望这与该主题相关,如果是这样,希望它能阐明这个“未知之谜”吗?(我正在阅读这一章并搜索 Stack Overflow 以深入了解从自定义对象创建自定义集合时的事件处理程序逻辑注意事项) - 我只提到这一点是因为我对这个特定主题没有特定的权威。我自己只是一个寻求“启蒙”的学生:-)

于 2010-05-06T07:34:48.650 回答