csdn学生大本营关掉啦 俺赶快转blog啊..
今天猫难得得空了一天呀 于是翻开c#本质论又看了章;;
要看事件 先来看看多播委托呐
上次俺已写了篇再看委托的笔记 小提了下窗体间用委托进行刷新的方法
多播委托:一个委托变量可以引用一系列委托,在这一系列委托中,每个委托都顺序指向一个后续的委托,从而形成了一个委托链
说白了就是给一个委托变量绑定多个方法名..
拿上次那个窗体来讲 我改了一下代码
有两个窗体 我要在第二个窗体里改变第一个窗体里几个TextBox的Text属性 我把修改每一个Text的属性都写在了一个方法中 并将它们赋值给了另一个窗体的相应的委托
form1代码
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
- private void Form1_Load(object sender, EventArgs e)
- {
- }
- private void button1_Click(object sender, EventArgs e)
- {
- Form2 f2=new Form2();
- f2.RefreshTest += RefreshTextBox1;
- f2.RefreshTest += RefreshTextBox2;
- f2.RefreshTest += RefreshTextBox3;
- f2.ShowDialog();
- }
- public void RefreshTextBox1()
- {
- this.textBox1.Text = "Catsoul!";
- }
- public void RefreshTextBox2()
- {
- this.textBox2.Text = "Monsoul!";
- }
- public void RefreshTextBox3()
- {
- this.textBox3.Text = "Catsoul love Monsoul forever!";
- }
- }
form2代码
- public partial class Form2 : Form
- {
- public delegate void RefreshData();
- public RefreshData RefreshTest { get; set; }
- public Form2()
- {
- InitializeComponent();
- }
- private void button1_Click(object sender, EventArgs e)
- {
- this.RefreshTest();
- }
- private void Form2_Load(object sender, EventArgs e)
- {
- }
- }
结果是这样滴:
主要是这段代码
- Form2 f2=new Form2();
- f2.RefreshTest += RefreshTextBox1;
- f2.RefreshTest += RefreshTextBox2;
- f2.RefreshTest += RefreshTextBox3;
- f2.ShowDialog();
我实例化了一个Form2窗体 并给它的RefreshTest委托添加了三个方法
用+=向RefreshTest委托添加(注册)了三个方法(订阅者)
上次我说过观察者模式理解成被观察者通知观察者 其实这里就叫发布者向订阅者通知消息
可以通过-=从委托链中中删除委托 而且可以通过+和-与合并,清除委托 就是委托支持+= -= + - = 等运算符 从字面上就能理解出这些玩意功能了.!
多播委托有很多地方存在一些隐患
第一 我们很容易把+=写成=!
比如那个窗体里的代码 我们很容易写成这样!
- f2.RefreshTest = RefreshTextBox1;
- f2.RefreshTest = RefreshTextBox2;
- f2.RefreshTest = RefreshTextBox3;
这样我们最后只能执行RefreshTextBox3()这个方法了!
第二 空值问题
如果我们没有给RefreshTest委托添加方法时,咱们调用它,就会出现异常!
提示引用对象米有实例!
第三 异常问题
假如委托链中一个方法引发了异常 链中后续的方法就不会执行了
由此 我们要这么写它的代码
- //遍历委托链
- foreach (RefreshData refreshData in this.RefreshTest.GetInvocationList())
- {
- try
- {
- refreshData();
- }
- catch (Exception)
- {
- throw;
- }
- }
咱们必须遍历它的委托链,把异常捕捉掉! 这样才能保证它能完整地执行完!
第四 方法返回值和传引用
所有方法都必须使用与委托相同的方法签名 如果每个方法都都要返回一个值 这里就出现了问题!
所以这里我们必须像刚刚那个异常处理一样 遍历委托链 获取每个单独的返回值
ref和out也是一样的道理!
对于这里里面的几个缺陷 使用事件可以解决两个问题!
先撇开事件的编程规范来写一个简易事件!
首先我把Form2代码改了一行! 对, 就一行!
- public partial class Form2 : Form
- {
- public delegate void RefreshData();
- // public RefreshData RefreshTest { get; set; }
- public event RefreshData RefreshTest = delegate { };
- public Form2()
- {
- InitializeComponent();
- }
- private void button1_Click(object sender, EventArgs e)
- {
- this.RefreshTest();
- }
- private void Form2_Load(object sender, EventArgs e)
- {
- }
- }
我把原委托属性改成了一个事件
这里不用担心直接设为public而破坏了封装 ! 请往下看!
咱们来看看CIL对应的c#代码
- public class Form2 : Form
- {
- // Fields
- private Button button1;
- private IContainer components = null;
- 这里!
---------------------------------------------------------- - private RefreshData RefreshTest = delegate {
- };
- ------------------------------------------------------------
- 下面也是重点!注意add和remove!
- // Events
- public event RefreshData RefreshTest
- {
- add
- {
- RefreshData data2;
- RefreshData refreshTest = this.RefreshTest;
- do
- {
- data2 = refreshTest;
- RefreshData data3 = (RefreshData) Delegate.Combine(data2, value);
- refreshTest = Interlocked.CompareExchange<RefreshData>(ref this.RefreshTest, data3, data2);
- }
- while (refreshTest != data2);
- }
- remove
- {
- RefreshData data2;
- RefreshData refreshTest = this.RefreshTest;
- do
- {
- data2 = refreshTest;
- RefreshData data3 = (RefreshData) Delegate.Remove(data2, value);
- refreshTest = Interlocked.CompareExchange<RefreshData>(ref this.RefreshTest, data3, data2);
- }
- while (refreshTest != data2);
- }
- }
- // Methods
- public Form2()
- {
- this.InitializeComponent();
- }
- private void button1_Click(object sender, EventArgs e)
- {
- this.RefreshTest();
- }
- protected override void Dispose(bool disposing)
- {
- if (disposing && (this.components != null))
- {
- this.components.Dispose();
- }
- base.Dispose(disposing);
- }
- private void Form2_Load(object sender, EventArgs e)
- {
- }
- private void InitializeComponent()
- {
- this.button1 = new Button();
- base.SuspendLayout();
- this.button1.Location = new Point(0x6a, 0x84);
- this.button1.Name = "button1";
- this.button1.Size = new Size(0x4b, 0x17);
- this.button1.TabIndex = 0;
- this.button1.Text = "button1";
- this.button1.UseVisualStyleBackColor = true;
- this.button1.Click += new EventHandler(this.button1_Click);
- base.AutoScaleDimensions = new SizeF(6f, 13f);
- base.AutoScaleMode = AutoScaleMode.Font;
- base.ClientSize = new Size(0x11c, 0x106);
- base.Controls.Add(this.button1);
- base.Name = "Form2";
- this.Text = "Form2";
- base.Load += new EventHandler(this.Form2_Load);
- base.ResumeLayout(false);
- }
- // Nested Types
- public delegate void RefreshData();
- }
这里发现它把事件生成了一个private的委托! 所以在类外部是调用不到它的!所以它是不存在破坏封装的!
再看下面事件被生成了一个包含add和remove的代码块 很类似于属性吧!
属性在CLR中会生成两个方法 一个是get_<propertyname>一个是set_<propertyname> 就和事件的add_OnTemperatureChange和remove_OnTemperatureChange类似
如果我们要定义自定义的事件 就可以手动写这些代码 我们可以把private的委托改成其它访问权限 在add和remove(该叫访问器?)中添加自己想要的逻辑
事件还解决了一个多播委托的问题 就是 +=容易写成=
事件限制外部类只能用+=和-=进行操作!
书中还有讲事件的编程规范和c#2.0及以上版本使用EventHandler<T>泛型委托时无需声明一个自定义委托的内容
公猫催死啦 猫先下了! 下回再写.!