繁体   English   中英

为什么我们不能用访问器引发事件?

[英]Why can´t we raise event with accessors?

为什么我们不能用自定义实现来引发事件,而没有它们却是可能的? 看到这个代码:

public class Program
{
    private EventHandler myEvent;
    public event EventHandler MyEvent
    {
        add { myEvent += value; }
        remove { myEvent -= value; }
    }

    public event EventHandler AnotherEvent;

    public static void Main()
    {
        var target = new Program();
        target.MyEvent(null, null);       // ERROR CS0079
        target.AnotherEvent(null, null);  // compiles
    }
}

您会看到这两个事件都在我的班级中声明。 虽然target.AnotherEvent(...)编译得很好,但target.MyEvent(...)不会:

Event MyEvent 只能出现在 += 或 -= 的左侧。

我知道一个事件只是一个带有添加和删除方法的委托。 因此, AnotherEvent被编译器转换为 add- 和 remove-method:

private EventHandler _AnotherEvent;
public event EventHandler AnotherEvent
{ 
    add { _AnotherEvent += value; }
    remove { _AnotherEvent -= value; }
}

所以我假设对AnotherEvent的调用被编译器替换为对私有委托的调用,即_AnotherEvent(...)

我做对了吗? 有没有关于为什么第二个调用有效而前者无效的文档? 或者至少有关于编译器在这里做什么的任何描述?

使用自动事件时public event EventHandler AnotherEvent; . 编译器将为它创建一个字段(和一些方法),并在该字段上完成调用。 所以public event不再存在。 这是语法糖。

因此调用非自动事件是不可能的。 因为在编译的代码中找不到它。 它被add_remove_方法取代。 您只能在私有字段(生成的)上调用

这解释了为什么不能在类实例之外调用事件。

它不起作用,因为现在有简单的方法来获取实际的可调用事件处理程序。 正如您所指出的,只有addremove ,而不是get

事件处理程序生成的代码是:

.event [mscorlib]System.EventHandler MyEvent
{
  .addon instance void ConsoleApp1.Program::add_MyEvent(class [mscorlib]System.EventHandler)
  .removeon instance void ConsoleApp1.Program::remove_MyEvent(class [mscorlib]System.EventHandler)
} // end of event Program::MyEvent

它添加了两个方法引用,一个用于add ,一个用于remove 如果你看它,它怎么知道调用什么方法? 如果addremove比现在复杂得多怎么办? 只是无法确定要调用哪个事件处理程序。

这是语法糖。 您可以像支持字段一样调用AnotherEvent是编译器提供的便利( AnotherEvent是所谓的类似字段的事件)。 一旦你添加了你自己的访问器,事件声明就不再是一个类似字段的事件,必须通过它的支持字段来调用。

请参阅C# 语言规范的相关部分:

类场事件

在包含事件声明的类或结构的程序文本中,某些事件可以像字段一样使用。 要以这种方式使用,事件不能是抽象的或外部的,并且不能显式包含 event_accessor_declarations。 此类事件可用于允许字段的任何上下文中。 该字段包含一个委托(Delegates),它指的是已添加到事件中的事件处理程序列表。 如果未添加任何事件处理程序,则该字段包含 null。

(强调我的)

建议您在添加或删除新的事件处理程序方法之前锁定事件。

说到这里,看看这段代码:

public event EventHandler MyEvent
{
    add
    {
        lock (objectLock)
        {
            myEvent += value;
        }
    }
    remove
    {
        lock (objectLock)
        {
            myEvent -= value;
        }
    }
}

原因public event EventHandler AnotherEvent; 有效是因为当您的代码中没有提供自定义事件访问器时,编译器会自动添加它们。

遵循此文档, 如何:实现自定义事件访问器以获得有关正确实现的更多详细信息以及其他来源的这篇文章

关于实施:

 private EventHandler myEvent;
 public event EventHandler MyEvent
    {
        add
        {
            lock (objectLock)
            {
                myEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                myEvent -= value;
            }
        }
    }

    public event EventHandler AnotherEvent;

    public static void Main()
    {
        var target = new Program();
        var myEvent =  target.MyEvent;
        myEvent?.Invoke(EventArgs.Empty, EventArgs.Empty);      
        target.AnotherEvent(null, null); 
    }

编辑以解释实现:

var myEvent =  target.MyEvent;

对于显式事件,您必须提供自己的后备存储 - 委托字段或EventHandlerList东西,所以我们在这里只使用var

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM