C#语言设计:事件的显式接口实现

时间:2010-02-15 18:27:07

标签: c# events interface explicit-implementation

关于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 个答案:

答案 0 :(得分:29)

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

在我的头脑中,我没有看到任何理论上或实际上的原因,为什么我们不能有类似字段的显式实现事件。我也没有看到任何我们特别需要的理由。这可能不得不是未知的奥秘之一。

答案 1 :(得分:26)

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

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;
}

在这里,您甚至无法通过向上转换到接口来引发事件,因为事件只能从实现类中引发。因此,对于明确实现的事件需要访问器语法似乎是完全合理的。

答案 2 :(得分:18)

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

例如,这将触发错误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() { }
}

请参阅Compiler Error CS0071

答案 3 :(得分:1)

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

但是,我想我可能会对此作出回应:

  

“在我的头脑中,我没有看到任何理论上或实际上的原因,为什么我们不能有类似字段的明确实施事件。我也没有看到任何我们特别需要的原因。这可能必须保持一个未知的奥秘。“   -Eric Lippert


程序员的C#简介第二章第二版中,Eric Gunnerson写道:

  

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

     

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

     

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

     

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

     

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

<强> ...

  

“我们需要的是保护委托字段的一些方法,以便只能使用+ =和 - =来访问它。”


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

我会添加更多内容,但在未经他许可的情况下,我太过尊重作者了。我建议找一本这本书的副本,并推荐Eric Gunnerson的一般内容(博客等等)

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