再看事件(内含多播委托)

csdn学生大本营关掉啦  俺赶快转blog啊..

今天猫难得得空了一天呀 于是翻开c#本质论又看了章;;

要看事件 先来看看多播委托

上次俺已写了篇再看委托的笔记 小提了下窗体间用委托进行刷新的方法 

多播委托:一个委托变量可以引用一系列委托,在这一系列委托中,每个委托都顺序指向一个后续的委托,从而形成了一个委托链

说白了就是给一个委托变量绑定多个方法名..

拿上次那个窗体来讲 我改了一下代码

有两个窗体 我要在第二个窗体里改变第一个窗体里几个TextBox的Text属性 我把修改每一个Text的属性都写在了一个方法中 并将它们赋值给了另一个窗体的相应的委托

form1代码

Code:
  1. public partial class Form1 : Form  
  2.    {  
  3.        public Form1()  
  4.        {  
  5.            InitializeComponent();  
  6.        }  
  7.   
  8.        private void Form1_Load(object sender, EventArgs e)  
  9.        {  
  10.   
  11.        }  
  12.   
  13.        private void button1_Click(object sender, EventArgs e)  
  14.        {  
  15.            Form2 f2=new Form2();  
  16.            f2.RefreshTest += RefreshTextBox1;  
  17.            f2.RefreshTest += RefreshTextBox2;  
  18.            f2.RefreshTest += RefreshTextBox3;  
  19.            f2.ShowDialog();  
  20.   
  21.        }  
  22.   
  23.        public void RefreshTextBox1()  
  24.        {  
  25.   
  26.            this.textBox1.Text = "Catsoul!";  
  27.        }  
  28.   
  29.        public void RefreshTextBox2()  
  30.        {  
  31.   
  32.   
  33.            this.textBox2.Text = "Monsoul!";  
  34.        }  
  35.   
  36.        public void RefreshTextBox3()  
  37.        {  
  38.   
  39.            this.textBox3.Text = "Catsoul love Monsoul forever!";  
  40.        }  
  41.    }  

form2代码

Code:
  1. public partial class Form2 : Form  
  2.     {  
  3.         public delegate void RefreshData();  
  4.   
  5.         public RefreshData RefreshTest { getset; }  
  6.           
  7.         public Form2()  
  8.         {  
  9.             InitializeComponent();  
  10.         }  
  11.   
  12.         private void button1_Click(object sender, EventArgs e)  
  13.         {  
  14.             this.RefreshTest();  
  15.         }  
  16.   
  17.         private void Form2_Load(object sender, EventArgs e)  
  18.         {  
  19.   
  20.         }  
  21.     }  

结果是这样滴:

216968_1306845898GquG.jpg

主要是这段代码

Code:
  1. Form2 f2=new Form2();  
  2.          f2.RefreshTest += RefreshTextBox1;  
  3.          f2.RefreshTest += RefreshTextBox2;  
  4.          f2.RefreshTest += RefreshTextBox3;  
  5.          f2.ShowDialog();  

我实例化了一个Form2窗体 并给它的RefreshTest委托添加了三个方法

+=向RefreshTest委托添加(注册)了三个方法(订阅者)

上次我说过观察者模式理解成被观察者通知观察者 其实这里就叫发布者向订阅者通知消息

可以通过-=从委托链中中删除委托 而且可以通过+和-与合并,清除委托  就是委托支持+= -=  + -  =  等运算符 从字面上就能理解出这些玩意功能了.!

多播委托有很多地方存在一些隐患

第一 我们很容易把+=写成=!

比如那个窗体里的代码 我们很容易写成这样!

Code:
  1. f2.RefreshTest = RefreshTextBox1;  
  2.          f2.RefreshTest = RefreshTextBox2;  
  3.          f2.RefreshTest = RefreshTextBox3;  

这样我们最后只能执行RefreshTextBox3()这个方法了!

第二 空值问题

如果我们没有给RefreshTest委托添加方法时,咱们调用它,就会出现异常!

216968_1306847134xe36.jpg

提示引用对象米有实例!

第三 异常问题

假如委托链中一个方法引发了异常 链中后续的方法就不会执行了

由此 我们要这么写它的代码

Code:
  1. //遍历委托链  
  2.            foreach (RefreshData refreshData in this.RefreshTest.GetInvocationList())  
  3.            {  
  4.                try  
  5.                {  
  6.                    refreshData();  
  7.                }  
  8.                catch (Exception)  
  9.                {  
  10.                    throw;  
  11.                }  
  12.            }  

咱们必须遍历它的委托链,把异常捕捉掉! 这样才能保证它能完整地执行完!

第四 方法返回值和传引用

所有方法都必须使用与委托相同的方法签名 如果每个方法都都要返回一个值 这里就出现了问题!

所以这里我们必须像刚刚那个异常处理一样 遍历委托链 获取每个单独的返回值

ref和out也是一样的道理!

对于这里里面的几个缺陷 使用事件可以解决两个问题!

 

先撇开事件的编程规范来写一个简易事件! 

 

首先我把Form2代码改了一行! 对, 就一行!

Code:
  1. public partial class Form2 : Form  
  2.    {  
  3.        public delegate void RefreshData();  
  4.   
  5.   //     public RefreshData RefreshTest { get; set; }  
  6.   
  7.        public event RefreshData RefreshTest = delegate { };   
  8.   
  9.        public Form2()  
  10.        {  
  11.            InitializeComponent();  
  12.        }  
  13.   
  14.        private void button1_Click(object sender, EventArgs e)  
  15.        {  
  16.            this.RefreshTest();  
  17.   
  18.        }  
  19.   
  20.        private void Form2_Load(object sender, EventArgs e)  
  21.        {  
  22.   
  23.        }  
  24.    }  

我把原委托属性改成了一个事件

这里不用担心直接设为public而破坏了封装 ! 请往下看!

咱们来看看CIL对应的c#代码

Code:
  1. public class Form2 : Form  
  2. {  
  3.     // Fields  
  4.     private Button button1;  
  5.     private IContainer components = null;  
  6.  
  7. 这里! 
    ---------------------------------------------------------- 
  8.     private RefreshData RefreshTest = delegate {  
  9.     };  
  10. ------------------------------------------------------------
  11.   
  12. 下面也是重点!注意add和remove!

  13.     // Events  
  14.     public event RefreshData RefreshTest  
  15.     {  
  16.         add  
  17.         {  
  18.             RefreshData data2;  
  19.             RefreshData refreshTest = this.RefreshTest;  
  20.             do  
  21.             {  
  22.                 data2 = refreshTest;  
  23.                 RefreshData data3 = (RefreshData) Delegate.Combine(data2, value);  
  24.                 refreshTest = Interlocked.CompareExchange<RefreshData>(ref this.RefreshTest, data3, data2);  
  25.             }  
  26.             while (refreshTest != data2);  
  27.         }  
  28.         remove  
  29.         {  
  30.             RefreshData data2;  
  31.             RefreshData refreshTest = this.RefreshTest;  
  32.             do  
  33.             {  
  34.                 data2 = refreshTest;  
  35.                 RefreshData data3 = (RefreshData) Delegate.Remove(data2, value);  
  36.                 refreshTest = Interlocked.CompareExchange<RefreshData>(ref this.RefreshTest, data3, data2);  
  37.             }  
  38.             while (refreshTest != data2);  
  39.         }  
  40.     }  
  41.   
  42.     // Methods  
  43.     public Form2()  
  44.     {  
  45.         this.InitializeComponent();  
  46.     }  
  47.   
  48.     private void button1_Click(object sender, EventArgs e)  
  49.     {  
  50.         this.RefreshTest();  
  51.     }  
  52.   
  53.     protected override void Dispose(bool disposing)  
  54.     {  
  55.         if (disposing && (this.components != null))  
  56.         {  
  57.             this.components.Dispose();  
  58.         }  
  59.         base.Dispose(disposing);  
  60.     }  
  61.   
  62.     private void Form2_Load(object sender, EventArgs e)  
  63.     {  
  64.     }  
  65.   
  66.     private void InitializeComponent()  
  67.     {  
  68.         this.button1 = new Button();  
  69.         base.SuspendLayout();  
  70.         this.button1.Location = new Point(0x6a, 0x84);  
  71.         this.button1.Name = "button1";  
  72.         this.button1.Size = new Size(0x4b, 0x17);  
  73.         this.button1.TabIndex = 0;  
  74.         this.button1.Text = "button1";  
  75.         this.button1.UseVisualStyleBackColor = true;  
  76.         this.button1.Click += new EventHandler(this.button1_Click);  
  77.         base.AutoScaleDimensions = new SizeF(6f, 13f);  
  78.         base.AutoScaleMode = AutoScaleMode.Font;  
  79.         base.ClientSize = new Size(0x11c, 0x106);  
  80.         base.Controls.Add(this.button1);  
  81.         base.Name = "Form2";  
  82.         this.Text = "Form2";  
  83.         base.Load += new EventHandler(this.Form2_Load);  
  84.         base.ResumeLayout(false);  
  85.     }  
  86.   
  87.     // Nested Types  
  88.     public delegate void RefreshData();  
  89. }  

这里发现它把事件生成了一个private的委托! 所以在类外部是调用不到它的!所以它是不存在破坏封装的!

再看下面事件被生成了一个包含add和remove的代码块 很类似于属性吧!

属性在CLR中会生成两个方法 一个是get_<propertyname>一个是set_<propertyname>   就和事件的add_OnTemperatureChange和remove_OnTemperatureChange类似

如果我们要定义自定义的事件 就可以手动写这些代码 我们可以把private的委托改成其它访问权限 在add和remove(该叫访问器?)中添加自己想要的逻辑

事件还解决了一个多播委托的问题 就是 +=容易写成=

事件限制外部类只能用+=和-=进行操作!

书中还有讲事件的编程规范和c#2.0及以上版本使用EventHandler<T>泛型委托时无需声明一个自定义委托的内容

公猫催死啦 猫先下了! 下回再写.!

转载于:https://www.cnblogs.com/qzwex2006/archive/2011/06/05/2073006.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值