What are some cases where it would be advantageous

2019-06-14 14:03发布

I'm reading this article by Jon Skeet as part of my quest to get a deep understanding of delegates and events.

In the article he demonstrates an event that isn't backed by a delegate variable and states that...

...there are times when you don't want to back an event with a simple delegate variable. For instance, in situations where there are lots of events but only a few are likely to be subscribed to, you could have a map from some key describing the event to the delegate currently handling it. This is what Windows Forms does - it means that you can have a huge number of events without wasting a lot of memory with variables which will usually just have null values.

I don't fully understand what he is saying. Can someone flesh out the examples? For instance, what does he mean by having a "map from some key describing the event to the delegate currently handling it"? How does Windows Forms do this?

Thanks!

3条回答
别忘想泡老子
2楼-- · 2019-06-14 14:34

You can use the same type yourself - EventHandlerList. Suppose you have 100 events - that would normally mean having 100 variables, which would take up space even if no-one ever subscribed to the event. Instead of that, EventHandlerList is a bit like a Dictionary<object, EventHandler> - it only creates an entry in its internal data structures when you first subscribe to a particular event.

So you might have something like:

// Actual values don't matter; they're just keys
private const string FirstEventKey = "FirstEvent";
private const string SecondEventKey = "SecondEvent";

private readonly EventHandlerList events = new EventHandlerList();

public event EventHandler FirstEvent
{
    add { events.AddHandler(FirstEventKey, value); }
    remove { events.RemoveHandler(FirstEventKey, value); }
}

public event EventHandler SecondEvent
{
    add { events.AddHandler(SecondEventKey, value); }
    remove { events.RemoveHandler(SecondEventKey, value); }
}

public void OnFirstEvent(EventArgs e)
{
    EventHandler handler = (EventHandler) events[FirstEventKey];
    if (handler != null)
    {
        handler(this, e);
    }
}

// Similarly for OnSecondEvent
查看更多
Explosion°爆炸
3楼-- · 2019-06-14 14:35

I'd like to add that this is something I've seen quite often

// bad code
class MyControl : Control {
    public event EventHandler ValueChanged;

    private CheckBox checked;
    // ...

    private void InitializeComponent() {
        // ...
        checked.CheckedChanged += checked_CheckedChanged;
        // ...
    }

    private void checked_CheckedChanged(object sender, EventArgs e) {
        if (ValueChanged != null) {
            ValueChanged(sender, e);
        }
    }
}

I consider this an anti-pattern because this way is quicker, takes less memory and is overall simpler I think:

class MyControl : Control {
    public event EventHandler ValueChanged {
        add {
            checked.CheckChanged += value;
        }
        remove {
            checked.CheckChanged -= value;
        }
    }

    private CheckBox checked;
    // ...
}
查看更多
Juvenile、少年°
4楼-- · 2019-06-14 14:44

Another situation is the trivial case, where a base class or interface provides an event for something that will never happen in certain derived types. For example, a read-only observable-collection interface might provide a CollectionChangedEvent. An entity holding an interface variable wouldn't be able to use it to change the collection, but might be interested in knowing if/when something else changes the collection. Such an entity should be able to use an immutable collection just as well as a mutable one; from its perspective, an immutable collection should be just like a mutable collection that nobody bothers to mutate while it's watching.

The most logical implementation of the CollectionChangedEvent would be to have add- and remove-handler methods that do nothing, with no backing delegate field. An outside entity that calls the add-handler method is essentially saying "Call me if this collection changes". When it calls the remove-handler method, it's essentially saying "I no longer need to know if this collection changes". If the collection is never ever going to change, those requests can be honored by simply doing nothing.

查看更多
登录 后发表回答