代码改变世界

C# Delegates And Events in Depth[翻译加笔记]【Event】【2】

2011-08-15 06:52  一一九九  阅读(373)  评论(0编辑  收藏  举报

http://www.ikriv.com/en/prog/info/dotnet/Delegates.html

Events

      概念上, Event是一对方法: Add accessor 和Remove accessor. 每一个方法都接受一个具体的Delegate Type的参数。Add accessor 通过 “e+ = d”来调用,remove accessor 通过 “e-=d”来调用。

       Event可以被明确的如下声明:

class MyClass
{
    public event MyDelegate GoodNews
    {
        add
        {
            // add accessor code here
        }
        
        remove
        {
            // remove accessor code here
        }
     }
}

Event有点像delegate(实际上不是),比如同时有add和remove两个accessor.

注意Event 并没有提供一个调用或者激发的方法。这是因为在生命的Class外部,Event是不能被激活的,也就是说,Event并不提供激发的接口,仅仅是一个实现的细节。

Using Events

        只能通过+=和-=来订阅和取消时间。如何来计划是由Class的作者来决定的。

Implementing Events
   
自己定义的实现方式如下所示:

class MyClass
{
    public event MyDelegate GoodNews
    {
        add
        {
            lock(this)
            {
                _goodNews += value;
            }
        }
        
        remove
        {
            lock(this)
            {
                _goodNews -= value;
            }
        }
    }
        
    private void OnGoodNews()
    {
        if (_goodNews != null)
        {
            _goodNews();
        }
    }
    
    private MyDelegate _goodNews;
}

     这种实现方式并不是唯一的一种实现方式。让我们考虑另外一种情况,加入我们的类定义了很多的Event, 而且只有若干个Event实际上被某个类的实例来Hook Up的,在这种情况下,存储大量的私有的Delegate变量将会浪费很多的空间,每一个类的实例都会保存所有的Delegates, 并且只有少数几个才能够被使用。对于这种情况的解决方式是在每一个实例中设置一个Container来存储实际上使用的Delegates. 很多Control就是这样的实现的。

       反编译Control单元,可以看到如下的一段话:
       

    [SRDescription("ControlOnBackColorChangedDescr"), SRCategory("CatPropertyChanged")]
    public event EventHandler BackColorChanged
    {
        add
        {
            base.Events.AddHandler(EventBackColor, value);
        }
        remove
        {
            base.Events.RemoveHandler(EventBackColor, value);
        }
    }
    [SRDescription("ControlOnBackgroundImageChangedDescr"), SRCategory("CatPropertyChanged")]
    public event EventHandler BackgroundImageChanged
    {
        add
        {
            base.Events.AddHandler(EventBackgroundImage, value);
        }
        remove
        {
            base.Events.RemoveHandler(EventBackgroundImage, value);
        }
    }

而Events是一个EventHandlerList。

protected EventHandlerList Events
{
    get
    {
        if (this.events == null)
        {
            this.events = new EventHandlerList(this);
        }
        return this.events;
    }
}
 

访问的时候是通过如下的方式来访问的:

[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnDockChanged(EventArgs e)
{
    EventHandler handler = base.Events[EventDock] as EventHandler;
    if (handler != null)
    {
        handler(this, e);
    }
}

其内部定义了一个默认的数组值来处理:

private static readonly object EventDock;

EventHandlerList的代码如下,这是个Hash类似的结构:

[HostProtection(SecurityAction.LinkDemand, SharedState=true)]
public sealed class EventHandlerList : IDisposable
{
    // Fields
    private ListEntry head;
    private Component parent;
    // Methods
    public EventHandlerList();
    internal EventHandlerList(Component parent);
    public void AddHandler(object key, Delegate value);
    public void AddHandlers(EventHandlerList listToAddFrom);
    public void Dispose();
    private ListEntry Find(object key);
    public void RemoveHandler(object key, Delegate value);
    // Properties
    public Delegate this[object key] { get; set; }
    // Nested Types
    private sealed class ListEntry
    {
        // Fields
        internal Delegate handler;
        internal object key;
        internal EventHandlerList.ListEntry next;
        // Methods
        public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next);
    }
}

Field-like Events

    编译器在默认的Event声明后面自动的生成默认的accessors,添加了一个匿名的私有字段来存储要激活的List。所以此时如果继承一个Class,想要访问父类中的Event是访问不到的。
     可以将Event声明为Virtual方式的供子类调用。