再看委托与事件

来源:互联网 发布:柬埔寨网络推广靠谱吗 编辑:程序博客网 时间:2024/04/28 16:27

 

之前接触委托也好几次了,至不过每次都只是拿来用用而已,基本只在事件、多线程更新问题处才会用到,一直对其概念都不是很清楚。

初识其只知.NET通过委托提供了了一种函数回调机制,在C/C++中非成员函数的地址只是一个内存地址,仅仅是一个地址而已,不包含其他的额外信息,因为只有方法地址,并缺少参数、特别是参数类型和返回值的类型描述,故其在C/C++中是一种不安全的类型,不过因为这些原因,非托管的函数回调效率较高。

委托就是一个代理,它负责处理跟他具有类似形态的方法,而不需要调用者亲自动手,由委托来代替执行。

1、一个简单的委托:

Publicdelegate void MyEventHandle(string value);//委托

Private voidOnAddToDelegate(string value) //交给委托执行的方法

{

         MessageBox.Show(value);

}

//委托要跟呗委托的方法保持类似的形态(参数)

 

Public voidDo()//执行函数

{

         MyEventHandle PHandle=newMyEventHandle(OnAddToDelegate);

PHandle(“Test”);

}

 

2、委托的两个特性

1)协变形协变形是指方法返回的类型可以是原本返回类型的派生类:

也就是说如果你的委托定义一个返回值类型是object,那么委托所调用的方法可以返回任何一个类。(void类型没有返回值,除外:所有值类型的也不可以)

Publicdelegate object MyEventHandle(string value);

Private stringOnAddToDelegate(string value)

{

         MessageBox.Show(value);

}

String 类型派生自object,故string类型可以作为委托返回值的类型。

2)逆变性是说方法获取的参数可以是委托参数类型的基类

同样用上面的例子说明:

Publicdelegate object MyEventHandle(string value);

Privatestring OnAddToDelegate(object value) //方法获取道的参数可以是委托参数value的基类,不能是派生类

{

         MessageBox.Show(value);

}

均只适用于引用类型的参数,void值类型不使用

在定义了delegate后,如果想看看编译器到底是怎么实现具体委托的,可以用IL查看一下源码,里面能看到具体的处理。

 

委托与事件

事件是基于委托实现的,因为在一个类中想要调用另外一个类内部的私有方法就要用到事件,而他自己本身无这样处理,故,把这部分处理交由第三方待处理,也就是委托了。

定义一个简单的事件很简单,在这里我想说的是,事件是既有委托实现的,如果没有委托的话事件这个机制也就没意义了,既然这样子,委托作为核心,那可不可以抛弃事件,自己利用委托实现一个类似事件的机制。想到这就联想到flex了,flex属于事件驱动编程语言,完全靠事件驱动,既然这样子,我就模仿一下flex

整理思路:

所有的事件必须首先注册,注册到一个集合中,注册过的事件在特定的条件下分发下去,在这里没有顺序科研,不是先注册那个分发那个,按需分配。对于分发过的事件,必须要监听事件,只有监听过的事件才能在分发后相应。

实现:

public classEventDiapatcher

    {

        /// <summary>

        /// 字典存储所有事件注册静态变量为了所有类都可以访问

        /// </summary>

        private static Dictionary<string,Delegate> m_EventList = new Dictionary<string, Delegate>();

 

        /// <summary>

        /// 注册事件监听

        /// </summary>

        /// <paramname="PEventName">事件名称</param>

        /// <paramname="PMethod">处理事件的委托方法</param>

        public void AddEventListen(stringPEventName,Delegate PMethod)

        {

            bool Plock = false;

            Monitor.Enter(m_EventList, refPlock);

            if (Plock)

            {

                Delegate POut = null;

                if(m_EventList.TryGetValue(PEventName, out POut))

                {

                    m_EventList[PEventName] =Delegate.Combine(POut, PMethod);

//支持委托连

                }

                else

                {

                    m_EventList.Add(PEventName,PMethod);

                }

            }

            Monitor.Exit(m_EventList);

        }

 

        /// <summary>

        /// 分发事件在字典中查找注册到

        /// </summary>

        /// <paramname="PEventName"></param>

        /// <paramname="Sender"></param>

        /// <paramname="e"></param>

        public void DispatchEvent(stringPEventName,object Sender,object e)

        {

            bool Plock = false;

            Monitor.Enter(m_EventList, refPlock);

            if (Plock)

            {

                Delegate POut = null;

                if(m_EventList.TryGetValue(PEventName, out POut))

                {

                    POut.DynamicInvoke(newobject[] { Sender, e });

                }

            }

            Monitor.Exit(m_EventList);

        }

 

        /// <summary>

        /// 删除事件

        /// </summary>

        /// <paramname="PEventName"></param>

        /// <paramname="PMethod"></param>

        public void RemoveEventLIsten(stringPEventName, Delegate PMethod)

        {

            bool Plock = false;

            Monitor.Enter(m_EventList, refPlock);

            if (Plock)

            {

                Delegate POut = null;

                POut =Delegate.Remove(POut,PMethod);

                if (POut == null)

                {

                    m_EventList.Remove(PEventName);

                }

                else

                {

                    m_EventList[PEventName] =POut;

                   // POut.Target

                }

            }

            Monitor.Exit(m_EventList);

        }

    }

 

Monitor 类提供了线程安全管理,Enter给枷锁,exit解锁。可以保证多线程调用下不会冲突。具体看msdn

测试:

此类为一个积累,凡是他的派生类都可以利用委托跳过定义事件的繁琐,很像flex

   定义2个类测试:

 public class DispathObject : EventDiapatcher

    {

        public static readonly string AddEvent= "Add";

    }

    public class Test:EventDiapatcher

    {

    }

//事件参数

    public class FeedArgs:EventArgs

    {      

        public string Name { get; set; }

        public FeedArgs(string PName)

        {

            Name = PName;

        }

    }

定义两个全局变量

        DispathObject dis = newDispathObject();

        Test t = new Test();

在窗体加载里为t注册事件监听:

t.AddEventListen(DispathObject.AddEvent,new EventHandler<FeedArgs>(dis_OnAddEvent));

然后再一个按钮的事件中分发事件:

dis.DispatchEvent(DispathObject.AddEvent,sender, new FeedArgs("123"));

        void dis_OnAddEvent(object sender,FeedArgs e)

        {    }//加入断点,进行调试

 

 

亲测可行,并支持委托连。

最后,比起定义一个事件而言,这样快速多了。平常很多人写事件都会自定义委托,我想说,委托其实都那样何必呢,需要传参数用参数对象就好了(当然不排除特殊情况)。尽量使用自带的委托,这样代码都会清晰很多。。。。。