Delegate委托 和 Event事件

一:事件

事件其实就是一个特殊的委托。MSDN中的C#参考里这样描述:事件是特殊类型的多路广播委托(一个事件可以搭载多个方法),仅可从声明它们的类或结构(发行者类)中调用。如果其他类或结构订阅了该事件,则当发行者类引发该事件时,会调用其事件处理程序方法。
对象一是事件的产生者,或者发送者;对象二是事件的接收者或者订阅者。对象一产生某种消息,需要对象二响应并处理这给消息,这就是事件的本质。

二:事件的好处

1:如果事件的发送者(B,发布者)直接调用事件接受者(A,响应和处理者)提供的处理函数,这样造成B对A具用很强的依赖性。如果此时,对象C也要响应B事件,那么这是就需要修改B的代码,达到调用C的目的。造成代码依赖性强,修改和扩展麻烦,系统复杂,维护性差。

2:真正的订阅(A,C)。发布者(B)不需要关心订阅者。订阅者提供了对事件响应的注册和反注册功能。订阅和撤销完全是事件接受方的行为。过程如下:事 件发布者(B)定义一个委托类型;事件发布者(B)定义一个事件,并且关联到已经定义的委托上;事件订阅者(A,C)需要产生一个委托实例,并把它添加到 委托列表。

三:Invoke,BeginInvoke,EndInvoke
1:对于Windows的消息机制
    while (GetMessage (&msg, NULL, 0, 0))//PeekMessage
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
   对于DoNet来说:
    Form f = new Form();
    Application.Run(f);//消息循环机制

2:由于如果从另外一个线程操作windows窗体上的控件,就会和主线程产生竞争,造成不可预料的结果,甚至死锁。因此windows GUI编程有一个规则,就是只能通过创建控件的线程来操作控件的数据,否则就可能产生不可预料的结果。因此,dotnet里面,为了方便地解决这些问 题,Control类实现ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来提供让其它线程更新GUI界面控 件的机制。
    public interface ISynchronizeInvoke
    {
         [HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]
        IAsyncResult BeginInvoke(Delegate method, object[] args);
        object EndInvoke(IAsyncResult result);
        object Invoke(Delegate method, object[] args);
        bool InvokeRequired { get; }
    }
    如果从线程外操作windows窗体控件,那么就需要使用Invoke或者BeginInvoke方法,通过一个委托把调用封送到控件所属的线程上执行。

3:在Windows中,进程和线程之间可以通过PostMessage(BeginInvoke异步,不开辟新线程,而是让控制界面的线程完成更新)或 SendMessage(Invoke同步)来通过另外一个进程或线程更新界面。Invoke或者BeginInvoke方 法都需要一个委托对象作为参数。委托类似于回调函数的地址,因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了 更改控件状态的代码,那么由于最终执行这个方法的是界面线程,从而避免了竞争条件,避免了不可预料的问题。如果其它线程直接操作界面线程所属的控件,那么 将会产生竞争条件,造成不可预料的结果。

4:BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。
    private static int newTask(int ms)
    {
        Console.WriteLine("任务开始");
        Thread.Sleep(ms);
        Random random = new Random();
        int n = random.Next(10000);
        Console.WriteLine("任务完成");
        return n;
    }
    private delegate int NewTaskDelegate(int ms);
    static void Main(string[] args)
    {
        NewTaskDelegate task = newTask;
        IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
        // EndInvoke方法将被阻塞2秒
        int result = task.EndInvoke(asyncResult);           
        Console.WriteLine(result);
    }
    另一种等待异步结束的方法
    static void Main(string[] args)
    {
        NewTaskDelegate task = newTask;
        IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
        while (!asyncResult.IsCompleted)
        {
        Console.Write("*");
        Thread.Sleep(100);
    }
    // 由于异步调用已经完成,因此, EndInvoke会立刻返回结
    int result = task.EndInvoke(asyncResult);           
    Console.WriteLine(result);
    }
    另一种等待异步结束的方法
    static void Main(string[] args)
    {
        NewTaskDelegate task = newTask;
        IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
        while (!asyncResult.AsyncWaitHandle.WaitOne(100, false))
        {
            Console.Write("*");
        }
        int result = task.EndInvoke(asyncResult);
        Console.WriteLine(result);
    }

四:Delegate和Event,

1:Delegate概念:C#中的委托类似于C或C++中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可 调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。

2:Delegate和指针的区别
    1) 一个 delegate对象一次可以搭载多个方法(methods),而不是一次一个。当我们唤起一个搭载了多个方法(methods)的delegate,所有方法以其“被搭载到delegate对象的顺序”被依次唤起。
    2) 一个delegate对象所搭载的方法(methods)并不需要属于同一个类别。一个delegate对象所搭载的所有方法(methods)必须具有 相同的原型和形式。然而,这些方法(methods)可以即有static也有non-static,可以由一个或多个不同类别的成员组成。
    3) 一个delegate type的声明在本质上是创建了一个新的subtype instance,该 subtype 派生自 .NET library framework 的 abstract base classes Delegate 或 MulticastDelegate,它们提供一组public methods用以询访delegate对象或其搭载的方法(methods) ,与函数指针不同,委托是面向对象、类型安全并且安全的。

3:实现一个Delegate,有以下三步
    1)声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
        public delegate void MyDelegate(string name);
        public static void MyDelegateFunc(string name)
        {
            Console.WriteLine("Hello, {0}", name);
        }
    2)创建delegate对象,并将你想要传递的函数作为参数传入。
        MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
    3)在要实现异步调用的地方,通过上一步创建的对象来调用方法。
        md("sam1111");

4:Event
1:事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
    public delegate void MyEventHandler(object sender, MyEventArgs e);
2:结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
    1)定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
    2)定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数这一步可以省略。
    3)定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
    4)用event关键字定义事件对象,它同时也是一个delegate对象。
    5)用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
    6)在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
    7)在适当的地方调用事件触发方法触发事件。
    public class EventTest
    {
        // 步骤1,定义delegate对象
        public delegate void MyEventHandler(object sender, System.EventArgs e);
        // 步骤2省略
        public class MyEventCls
        {
            // 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类// 型
            public void MyEventFunc(object sender, System.EventArgs e)
            {
                Console.WriteLine("My event is ok!");
            }
        }
        // 步骤4,用event关键字定义事件对象
        private event MyEventHandler myevent;
        private MyEventCls myecls;
        public EventTest()
        {
            myecls = new MyEventCls();
            // 步骤5,用+=操作符将事件添加到队列中
            this.myevent += new MyEventHandler(myecls.MyEventFunc);
        }
        // 步骤6,以调用delegate的方式写事件触发函数
        protected void OnMyEvent(System.EventArgs e)
        {
            if (myevent != null)
                myevent(this, e);
        }
        public void RaiseEvent()
        {
            EventArgs e = new EventArgs();
            // 步骤7,触发事件
            OnMyEvent(e);
        }
        public static void Main()
        {
            EventTest et = new EventTest();
            Console.Write("Please input 'a':");
            string s = Console.ReadLine();
            if (s == "a")
            {
                et.RaiseEvent();
            }
            else
            {
                Console.WriteLine("Error");
            }
        }
    }

5:Event和Delegate的关系:若要在类内声明事件,首先必须声明该事件的委托类型。
    //步骤三
    private void button1_Click(object sender, EventArgs e)//回调函数
    {
    }
    //步骤五
    this.button1.Click += new System.EventHandler(this.button1_Click);
    //步骤一
    public delegate void EventHandler(object sender, EventArgs e);
    public class Control
    {
        //步骤四
        public event EventHandler Click;
    }

6:回调函数:回调函数就是把一个方法的传给另外一个方法去执行。
    public delegate string ProcessDelegate(string s1, string s2);
    class Program
    {
        static void Main(string[] args)
        {
            /* 调用方法 */
            Test t = new Test();
            string r1 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process1));
            string r2 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process2));
            string r3 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process3));

            Console.WriteLine(r1);
            Console.WriteLine(r2);
            Console.WriteLine(r3);
        }
    }
    public class Test
    {
        public string Process(string s1,string s2,ProcessDelegate process)
        {
            return process(s1, s2);
        }
        public string Process1(string s1, string s2)
        {
            return s1 + s2;
        }
        public string Process2(string s1, string s2)
        {
            return s1 + Environment.NewLine + s2;
        }
        public string Process3(string s1, string s2)
        {
            return s2 + s1;
        }
    }

五:设计一个电子邮件程序,当收到电子邮件时,希望将该消息转发到传真机(Fax)和手机(CallPhone);

1:需要传递消息则需要定义事件传递的消息类
    /// <summary>
    /// 事件传递的消息定义,来自:发到:主题:内容
    /// </summary>
    public class MailMsgEventArgs:EventArgs
    {
        public readonly string from, to, subject, body;
        public MailMsgEventArgs(string from, string to, string subject, string body)
        {
            this.from = from;
            this.to = to;
            this.subject = subject;
            this.body = body;
        }
    }

2:定义委托及事件
    //定义一委托
    public delegate void MailMsgEventHandler(object sender,MailMsgEventArgs e);
    public class MailManager
    {
        public event MailMsgEventHandler MailMsg;  //委托类型的事件

        /// <summary>
        /// 激发事件
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnMailMsg(MailMsgEventArgs e)
        {
            if (this.MailMsg != null)
            {
                MailMsg(this, e);
            }
        }

        //通过事件传递消息
        public void SimulateArrivingMsg(string from, string to, string subject, string body)
        {
            MailMsgEventArgs e = new MailMsgEventArgs(from, to, subject, body);
            OnMailMsg(e);
        }
    }

3:传真和手机的定义:
    /// <summary>
    /// 传真机
    /// </summary>
    public class Fax
    {
        private TextBox _tBox;
        public Fax(MailManager mm, TextBox tBox)
        {
            //监听事件 这里的FaxMsg,指的是符合MailMsgEventHandler委托的方法,也就是激发事件后所执行的方法
            mm.MailMsg += new MailMsgEventHandler(FaxMsg);
            _tBox = tBox;
        }

        private void FaxMsg(Object sender, MailMsgEventArgs e)
        {
            _tBox.Text += string.Format("消息到传真:{4}来自:{0}{4}发到:{1}{4}主题:{2}{4}内容:{3}{4}{4}", e.from, e.to, e.subject, e.body, Environment.NewLine);
        }

        public void Register(MailManager mm)
        {
            mm.MailMsg += new MailMsgEventHandler(FaxMsg);
        }
        //取消邦定
        public void UnRegister(MailManager mm)
        {
            //注销事件
            mm.MailMsg -= new MailMsgEventHandler(FaxMsg);
        }
    }

    public class CallPhone
    {
        private TextBox _tBox;
        public CallPhone(MailManager mm, TextBox tBox)
        {
            mm.MailMsg += new MailMsgEventHandler(CellPhoneMsg);
            _tBox = tBox;
        }

        private void CellPhoneMsg(Object sender, MailMsgEventArgs e)
        {
            _tBox.Text += string.Format("消息到手机:{4}来自:{0}{4}发到:{1}{4}主题:{2}{4}内容:{3}{4}{4}", e.from, e.to, e.subject, e.body,Environment.NewLine);
        }

        public void Register(MailManager mm)
        {
            mm.MailMsg += new MailMsgEventHandler(CellPhoneMsg);
        }
        //取消邦定
        public void UnRegister(MailManager mm)
        {
            mm.MailMsg -= new MailMsgEventHandler(CellPhoneMsg);
        }
    }

4:实现
    public partial class Form1 : Form
    {
        private Fax fax = null;
        private CallPhone cell = null;
        private MailManager mm = null;
        public Form1()
        {
            InitializeComponent();
            //声明委托,及事件
            mm = new MailManager();

            //邦定
            fax = new Fax(mm, txtReceiver);
            cell = new CallPhone(mm, txtReceiver);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            //SimulateArrivingMsg --〉OnMailMsg--〉触发MailMsg事件--〉执行相关邦定函数
            //CellPhoneMsg + FaxMsg
            mm.SimulateArrivingMsg(txtFrom.Text, txtTo.Text, txtSubject.Text, txtBody.Text);
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            this.txtReceiver.Text = "";
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值