Events aren't fields - I don't get it

后端 未结 5 1801
清酒与你
清酒与你 2020-12-23 19:22

In C# in depth (an excellent book thus far), Skeet explains events aren\'t fields. I read this section many times and I don\'t understand why the distinction makes

相关标签:
5条回答
  • 2020-12-23 19:47

    The other answers are basically correct, but here's another way to look at it:

    I am one of those developers that confuse events and delegate instances. In my mind, they are the same.

    An old saying about not seeing the forest for the trees comes to mind. The distinction that I make is that events are at a higher "semantic level" than a field of delegate instance. An event tells the consumer of the type "hi there, I am a type that likes to tell you when something happens". The type sources an event; that's part of its public contract.

    How, as an implementation detail, that class chooses to keep track of who is interested in listening to that event, and what and when to tell the subscribers that the event is happening, is the business of the class. It happens to typically do so with a multicast delegate, but that's an implementation detail. It is such a common implementation detail that it is reasonable to confuse the two, but we really do have two different things: a public surface, and a private implementation detail.

    Similarly, properties describe the semantics of an object: a customer has a name, so a Customer class has a Name property. You might say that "their name" is a property of a customer, but you would never say that "their name" is a field of a customer; that's an implementation detail of a particular class, not a fact about the business semantics. That a property is typically implemented as a field is a private detail of the class mechanics.

    0 讨论(0)
  • 2020-12-23 19:49

    An event is an accessor for a delegate. Just like a property is an accessor for a field. With the exact same utility, it prevents code from messing with the delegate object. Like a property has a get and set accessor, an event has the add and remove accessor.

    It does behave somewhat different from a property, if you don't write the add and remove accessors yourself then the compiler auto-generates them. Including a private backing field that stores the delegate object. Similar to an automatic property.

    You don't do this often but it is certainly not unusual. The .NET framework pretty commonly does so, for example the events of the Winforms controls are stored in an EventHandlerList and the add/remove accessors manipulate that list through its AddHandler() and RemoveHandler() methods. With the advantage that all the events (there are many) require only a single field in the class.

    0 讨论(0)
  • 2020-12-23 20:02

    I can add to the former answers that delegates can be declared inside a namespace scope (outside a class) and events can be declared only inside a class. This is because delegate is a class!

    Another distinction is that , for events, the containing class is the only one that can fire it. You can subscribe/unsubscribe to it via the containing class, but can't fire it (in contrast to delegates). So maybe you can understand now why the convention is to wrap it inside a protected virtual OnSomething(object sender, EventArgs e). It is for the descendants to be able to override the implementation of the firing.

    0 讨论(0)
  • 2020-12-23 20:07

    Properties aren't fields either, although they feel like them. They are actually a pair of methods (getter and setter) with special syntax.

    Events are similarly a pair of methods (subscribe and unsubscribe) with special syntax.

    In both cases, you usually have a private "backing field" inside your class, which holds the value manipulated by the getter/setter/subscribe/unsubscribe methods. And there's an auto-implemented syntax for both properties and events where the compiler generates the backing field and accessor methods for you.

    The purpose is also the same: Properties provide restricted access to a field, where some validation logic is run before storing a new value. And an event provides restricted access to a delegate field, where consumers can only subscribe or unsubscribe, not read the list of subscribers, nor replace the whole list at once.

    0 讨论(0)
  • 2020-12-23 20:11

    Let's consider the two ways to declare events.

    Either you declare an event using an explicit add/remove method, or you declare an event without such methods.

    In other words, you declare the event like this:

    public event EventHandlerType EventName
    {
        add
        {
            // some code here
        }
        remove
        {
            // some code here
        }
    }
    

    or you declare it like this:

    public event EventHandlerType EventName;
    

    The thing is, in some ways they're the same thing, and in other ways, they're completely different.

    From the perspective of outside code, that is ... code outside of the class publishing the event, they're the exact same thing. To subscribe to an event, you call a method. To unsubscribe, you call a different method.

    The difference is that in the second example code above, those methods will be provided by the compiler for you, however, that's still how it's going to be. To subscribe to the event, you call a method.

    The syntax to do so, in C#, however, is the same, you do either:

    objectInstance.EventName += ...;
    

    or:

    objectInstance.EventName -= ...;
    

    So from the "outside perspective", the two ways are no different at all.

    However, inside the class, there is a difference.

    If you try to access the EventNameidentifier inside the class, you're actually referring to the field that backs the property, but only if you use the syntax that doesn't explicitly declare an add/remove method.

    A typical pattern is like this:

    public event EventHandlerType EventName;
    
    protected void OnEventName()
    {
        var evt = EventName;
        if (evt != null)
            evt(this, EventArgs.Empty);
    }
    

    In this case, when you're referring to EventName, you're actually referring to the field that holds the delegate of type EventHandlerType.

    However, if you've explicitly declared the add/remove methods, referring to the EventName identifier inside the class will be just like outside of the class, since the compiler cannot guarantee that it knows the field, or any other mechanism, in which you store the subscription.

    0 讨论(0)
提交回复
热议问题