简体   繁体   English

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

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

Why can´t we raise an event with a custom implementation, while it is possible without them?为什么我们不能用自定义实现来引发事件,而没有它们却是可能的? See this code:看到这个代码:

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

You see both events are declared within my class.您会看到这两个事件都在我的班级中声明。 While target.AnotherEvent(...) compiles just fine, target.MyEvent(...) does not:虽然target.AnotherEvent(...)编译得很好,但target.MyEvent(...)不会:

The Event MyEvent can only appear on the left hand side of += or -=. Event MyEvent 只能出现在 += 或 -= 的左侧。

I Know an event is just a delegate with an add- and remove-method.我知道一个事件只是一个带有添加和删除方法的委托。 So AnotherEvent is translated by the compiler to an add- and a remove-method:因此, AnotherEvent被编译器转换为 add- 和 remove-method:

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

So I assume the call to AnotherEvent is replaced by the compiler to a call to the private delegate, which was _AnotherEvent(...) .所以我假设对AnotherEvent的调用被编译器替换为对私有委托的调用,即_AnotherEvent(...)

Did I get this right?我做对了吗? Are there any docs about why the second call works while the former does not?有没有关于为什么第二个调用有效而前者无效的文档? Or at least any description about what the compiler does here?或者至少有关于编译器在这里做什么的任何描述?

When an auto event is used public event EventHandler AnotherEvent;使用自动事件时public event EventHandler AnotherEvent; . . The compiler will create a field (and some methods) for it and invoking is done on that field.编译器将为它创建一个字段(和一些方法),并在该字段上完成调用。 So the public event does not exists anymore.所以public event不再存在。 It's syntactic sugar.这是语法糖。

So invoking a non-auto event is not possible.因此调用非自动事件是不可能的。 Because it isn't found in the compiled code.因为在编译的代码中找不到它。 It's replaced by add_ , remove_ methods.它被add_remove_方法取代。 You can only invoke on the private field (which is generated)您只能在私有字段(生成的)上调用

This explains why you cannot invoke an event outside the class instance.这解释了为什么不能在类实例之外调用事件。

It doesn't work because there is simply now way to get the actual invokeable event handler.它不起作用,因为现在有简单的方法来获取实际的可调用事件处理程序。 As you have noted, there is just an add and remove , not a get .正如您所指出的,只有addremove ,而不是get

The generated code for the event handler is:事件处理程序生成的代码是:

.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

It adds two method references, one for add and one for remove .它添加了两个方法引用,一个用于add ,一个用于remove If you look at it, how would it know what method to invoke?如果你看它,它怎么知道调用什么方法? What if add and remove are much more complex than they are now?如果addremove比现在复杂得多怎么办? There is just no way to know for sure what event handler to call.只是无法确定要调用哪个事件处理程序。

It's syntactical sugar.这是语法糖。 That you can call AnotherEvent like the backing field is a convenience provided by the compiler ( AnotherEvent is a so-called field-like event ).您可以像支持字段一样调用AnotherEvent是编译器提供的便利( AnotherEvent是所谓的类似字段的事件)。 Once you add your own accessors, the event declaration ceases to be a field-like event and has to be invoked through its backing field.一旦你添加了你自己的访问器,事件声明就不再是一个类似字段的事件,必须通过它的支持字段来调用。

See the relevant part of the C# Language Specification :请参阅C# 语言规范的相关部分:

Field-like events类场事件

Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields.在包含事件声明的类或结构的程序文本中,某些事件可以像字段一样使用。 To be used in this way, an event must not be abstract or extern, and must not explicitly include event_accessor_declarations.要以这种方式使用,事件不能是抽象的或外部的,并且不能显式包含 event_accessor_declarations。 Such an event can be used in any context that permits a field.此类事件可用于允许字段的任何上下文中。 The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event.该字段包含一个委托(Delegates),它指的是已添加到事件中的事件处理程序列表。 If no event handlers have been added, the field contains null.如果未添加任何事件处理程序,则该字段包含 null。

(emphasis mine) (强调我的)

It is recommended that you lock the event before you add or remove a new event handler method.建议您在添加或删除新的事件处理程序方法之前锁定事件。

saying that, have a look on this piece of code:说到这里,看看这段代码:

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

The reason public event EventHandler AnotherEvent;原因public event EventHandler AnotherEvent; works is because When no custom event accessors are supplied in your code, the compiler will add them automatically.有效是因为当您的代码中没有提供自定义事件访问器时,编译器会自动添加它们。

Follow this doc, How to: Implement Custom Event Accessors in order to get more details about the proper implementation and this post for another source.遵循此文档, 如何:实现自定义事件访问器以获得有关正确实现的更多详细信息以及其他来源的这篇文章

Regarding the implementation:关于实施:

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

Edit to explain the implementation:编辑以解释实现:

var myEvent =  target.MyEvent;

With an explicit event, you have to provide your own backing store - either a delegate field or something like EventHandlerList , so we just go with var here.对于显式事件,您必须提供自己的后备存储 - 委托字段或EventHandlerList东西,所以我们在这里只使用var

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

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