【问题标题】:Why do virtual field-like events work the way they do in C#? [duplicate]为什么虚拟字段类事件的工作方式与它们在 C# 中的工作方式相同? [复制]
【发布时间】:2014-08-01 21:08:58
【问题描述】:

最近我发现虚拟事件在 C# 中并不像人们预期的那样工作。 考虑这段代码:

public abstract class MyClassBase
{
    public virtual event EventHandler<EventArgs> MyEvent;

    public void DoStuff()
    {
        if (MyEvent != null)
        {
            MyEvent(this, EventArgs.Empty);
        }
    }
}

public class MyClassDerived : MyClassBase
{
    public override event EventHandler<EventArgs> MyEvent;
}

鉴于此定义,以下代码的行为与我预期的不同:

MyClassBase obj = new MyClassDerived();
obj.MyEvent += (s, e) => { /* Never gets called */ };

// Call method on base class that raises MyEvent
obj.DoStuff();

我已经完成了更广泛的write-up on my blog,但我只想说我找到了一个warning on MSDN,它确认了该行为是意外的,但它没有说明原因。谁能想到以这种方式实施的原因?它最初是为了向后兼容而留下的错误吗?我想至少可以添加某种编译器警告。

我意识到这很可能是猜测,但我正试图为这种奇怪的行为找到一个合理的解释。

【问题讨论】:

  • 到底为什么要覆盖事件定义?我有点惊讶它甚至被允许:/
  • 那么会发生什么,您认为应该发生什么,以及您认为应该发生什么?
  • 只是一个建议:遵循示例代码中的正常大小写约定。 eventargs 不是标准类型,但 EventArgs 是。同样,您的博客文章包含一个类型参数 t,而 T 是常规的。这些不难解决,但会分散注意力。
  • @Thorarin:嗯?众所周知的事实是,MS 因其与向后兼容性有关的程度而受到敏捷性的影响……不知道这意味着什么。无论如何,那个飞碟烧了。
  • C# 编译器应该为此生成诊断。但是,是的,您总是必须对虚拟事件使用 add 和 remove 访问器。 那些方法是虚拟的,字段不能是虚拟的。

标签: c# events virtual


【解决方案1】:

编译器将类字段事件扩展为如下内容:

public abstract class MyClassBase
{
    private EventHandler _myEvent;
    public virtual event EventHandler MyEvent
    {
        add { _myEvent += value; }
        remove { _myEvent -= value; }
    }

    public void DoStuff()
    {
        if (_myEvent != null)
        {
            _myEvent(this, EventArgs.Empty);
        }
    }
}

(注意DoStuff 方法如何使用字段,而不是事件)

因此,当您使用另一个类似字段的事件覆盖该事件时,您会获得一个新字段,并且被覆盖的事件会修改新字段,而不是来自基类的字段:

public class MyClassDerived : MyClassBase
{
    private EventHandler _myEvent;
    public override event EventHandler MyEvent
    {
        add { _myEvent += value; }
        remove { _myEvent -= value; }
    }
}

因此,当您向MyClassDerived 的实例添加处理程序时,MyClassBase 中的_myEvent 字段不受影响,因此DoStuff 方法会看到一个空处理程序。

【讨论】:

  • 你确信这行得通吗?我没有得到任何输出(我相信你必须将if (MyEvent != null) 更改为if (_myEvent != null))。
  • @JeroenVannevel,是的,这是一个错字,谢谢。 “作品”是什么意思?我只是描述了 OP 示例中发生的情况,并没有尝试修复它。
  • 啊,那请忽略我的评论。我的印象是您提供了不同的解决方法,而不是说明问题。那么一切都好:)
  • 它确实看起来像这样,但这并不是我一直在寻找的为什么。我知道创建了一个新的支持字段,但这似乎与 virtual 关键字的语义不一致。
  • @Thorarin,是的,最终结果是一样的。 (可以说)虚拟事件的合法用途;例如,您可能希望通过不订阅基类事件来允许派生类“禁用”事件。但是我同意也许应该禁止用类似字段的事件覆盖事件;在覆盖中强制执行访问器的显式声明可能会更好,以确保您知道自己在做什么。但不幸的是它不会改变,因为它会破坏现有的代码(即使它可能已经被破坏了......)
猜你喜欢
  • 2012-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-24
  • 1970-01-01
  • 2019-04-05
  • 2011-12-21
相关资源
最近更新 更多