玩 C# 也快 3 个月了 对委托 事件 做个小总结 互相交流下 如果你是高手(就不用看了,很菜,如果你愿意指导下小弟,感激不尽!)
废话少说 (花了我 3个小时 +1)
1、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 委托 { class ABCD { //... A public void EnglishGreeting(string name) { Console.WriteLine("Morning," + name); } public void GreetPeople(string name) { EnglishGreeting(name); // 做某些额外的事情,比如初始化之类,此处略 } //.... B public void ChineseGreeting(string name) { Console.WriteLine("早上好, " + name); } public enum Language { English, Chinese } public void GreetPeople(string name, Language lang) { switch (lang) { //做某些额外的事情,比如初始化之类,此处略 case Language.English: EnglishGreeting(name); break; case Language.Chinese: ChineseGreeting(name); break; } } } }
2、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 委托 //方法调用方法 { public delegate void GreetingDelegate(string name); // static class GreetingDelegateMethod // 定义一个静态类 class GreetingDelegateMethod { // static public void GreetPeople(GreetingDelegate MakeGreeting, string name);//委托(MakeGreeting)定义的参数 是一个方法(静态) public void GreetPeople(GreetingDelegate MakeGreeting, string name)//委托定义的参数 是一个方法 { MakeGreeting(name);//name 是方法的参数 } public static void EnglishGreeting(string name) { Console.WriteLine(name+" Morning," ); } public static void ChineseGreeting(string name) { Console.WriteLine(name + " , 早上好"); } } }
3、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 委托 { class Program { static void Main(string[] args) { //1、设置到 ABCD 类 /* ABCD a = new ABCD(); a.GreetPeople("Hui Jiang",ABCD.Language.English); Console.ReadKey(); */ //2、委托调用 设置 GreetingDelegateMethod /*GreetingDelegateMethod.GreetPeople(GreetingDelegateMethod.EnglishGreeting, "Hui Jiang "); GreetingDelegateMethod.GreetPeople(GreetingDelegateMethod.ChineseGreeting, " 蒋 辉"); */ GreetingDelegateMethod gdm = new GreetingDelegateMethod(); /* gdm.GreetPeople(GreetingDelegateMethod.ChineseGreeting, "蒋辉"); gdm.GreetPeople(GreetingDelegateMethod.EnglishGreeting, "Hui Jiang"); */ //而既然委托GreetingDelegate 和 类型 string 的地位一样,都是定义了一种参数类型,那么,我是不是也可以这么使用委托? // A /* string name1, name2; name1 = "Hui Jiang "; name2 = " 蒋辉 "; gdm.GreetPeople(GreetingDelegateMethod.EnglishGreeting,name1); gdm.GreetPeople(GreetingDelegateMethod.ChineseGreeting,name2); */ // B 而既然委托GreetingDelegate 和 类型 string 的地位一样,都是定义了一种参数类型,那么, //我是不是也可以这么使用委托?以下为引用的内容: /* GreetingDelegate delegate1, delegate2; delegate1 = GreetingDelegateMethod.EnglishGreeting; delegate2 = GreetingDelegateMethod.ChineseGreeting; gdm.GreetPeople(delegate1,"Hui Jiang "); gdm.GreetPeople(delegate2, " 蒋辉 "); */ /*如你所料,这样是没有问题的,程序一如预料的那样输出。这里,我想说的是委托不同于string的一个特性: 可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候, 将依次调用其所绑定的方法。在这个例子中,语法如下: 以下为引用的内容:*/ /* GreetingDelegate delegate1; delegate1 =GreetingDelegateMethod.EnglishGreeting; // 先给委托类型的变量赋值 delegate1 +=GreetingDelegateMethod.ChineseGreeting; // 给此委托变量再绑定一个方法 // 将先后调用 EnglishGreeting 与 ChineseGreeting 方法 gdm.GreetPeople(delegate1,"Hui Jiang "); */ /* 实际上,我们可以也可以绕过GreetPeople方法,通过委托来直接调用EnglishGreeting和ChineseGreeting: 以下为引用的内容:*/ /* GreetingDelegate delegate1;//delegate1 其实就是一个方法 delegate1 =GreetingDelegateMethod. EnglishGreeting; // 先给委托类型的变量(一个方法)赋值 delegate1 +=GreetingDelegateMethod. ChineseGreeting; // 给此委托变量再绑定一个方法 */ // 将先后调用 EnglishGreeting 与 ChineseGreeting 方法 // delegate1("Hui Jiang "); /* 注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”, 将出现“使用了未赋值的局部变量”的编译错误。 * 我们也可以使用下面的代码来这样简化这一过程: */ /* GreetingDelegate delegate1 = new GreetingDelegate(GreetingDelegateMethod.EnglishGreeting); delegate1 += GreetingDelegateMethod.ChineseGreeting; // 给此委托变量再绑定一个方法 delegate1("Hui Jiang "); */ // 取消绑定 在这里就不讲了 “ -+ ” /* 尽管这样达到了我们要的效果,但是似乎并不美气, 光是第一个方法注册用“=”,第二个用“+=”就让人觉得别扭。此时,轮到Event出场了, * C# 中可以使用事件来专门完成这项工作, 那么我们就来定义 GreetingManager 类,它变成了这个样子: * 接下来就是 event(事件) 如果你不知道什么是事件,可以先去了解下事件的 最基本知识!*/ //设置到 GreetingManager 类 /*很容易注意到:MakeGreet 事件的声明与之前委托变量delegate1的声明唯一的区别是多了一个event关键字。看到这里,你差不多明白到: * 事件其实没什么不好理解的,声明一个事件不过类似于声明一个委托类型的变量而已。 */ Console.ReadKey(); } } }
对于 事件 在这里 只能说 时间真的很晚了 下次总结
今天 花了点时间 把事件 解决掉了 废话不多说 上代码!
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 事件 { class Program { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber sub = new Subscriber(); pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged); pub.DoSomething(); // 应该通过DoSomething()来触发事件 // pub.NumberChanged(100); // 但可以被这样直接调用,对委托变量的不恰当使用 Console.ReadKey(); } } public delegate void NumberChangedEventHandler(int count); // 定义委托 public class Publishser // 定义事件发布者 { private int count; // public NumberChangedEventHandler NumberChanged; // 声明委托变量 public event NumberChangedEventHandler NumberChanged; // 声明一个事件 public void DoSomething() { if (NumberChanged != null) // 在这里完成一些工作 ... { //满足某种条件 触发事件 count++; NumberChanged(count); NumberChanged(100);//publishser内部调用 } } } public class Subscriber // 定义事件订阅者 { public void OnNumberChanged(int count) { Console.WriteLine("Subscriber notified: count = {0}", count); } } }
PS:
// ************************************************************************
上面代码定义了一个NumberChangedEventHandler 委托,然后我们创建了事件的发布者Publisher 和订阅者Subscriber。当使用委托变量时,客户端可以直接通过委托变量触发事件,也就是直接调用pub.NumberChanged(100),这将会影响到所有注册了该委托的订阅者。而事件的本意应该为在事件发布者在其本身的某个行为中触发,比如说在方法DoSomething()中满足某个条件后触发。通过添加event 关键字来发布事件,事件发布者的封装性会更好,事件仅仅是供其他类型订阅,而客户端不能直接触发事件(语句pub.NumberChanged(100)无法通过编译),事件只能在事件发布者Publisher 类的内部触发(比如在方法pub.DoSomething()中),换言之,就是NumberChanged(100)语句只能在Publisher 内部被调用。
大家可以尝试一下,将委托变量的声明那行代码注释掉,然后取消下面事件声明的注释。此时程序是无法编译的,当你使用了event 关键字之后,直接在客户端触发事件这种行为,也就是直接调用pub.NumberChanged(100),是被禁止的。事件只能通过调用DoSomething()来触发。这样才是事件的本意,事件发布者的封装才会更好。
就好像如果我们要定义一个数字类型,我们会使用int 而不是使用object 一样,给予对象过多的能力并不见得是一件好事,应该是越合适越好。尽管直接使用委托变量通常不会有什么问题,但它给了客户端不应具有的能力,而使用事件,可以限制这一能力,更精确地对类型进行封装。
说 明:这里还有一个约定俗称的规定,就是订阅事件的方法的命名,通常为“On 事件名”,比如这里的OnNumberChanged。