至今最大之敌:委托和事件(重中之重)
Posted fangshiyuanzhucheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了至今最大之敌:委托和事件(重中之重)相关的知识,希望对你有一定的参考价值。
1.委托的定义
delegate(委托)是表示将方法作为参数传递给其他方法。 委托类似于函数指针,但与 函数指针不同的是,委托是面向对象的,类型安全的和保险的。 委托既能引用静态方法, 也能引用实例方法
2.委托的引入
在引入委托之前,我们先来看一段代码:
class HelloWorld{ public void GreetPeople(string name){ EnglishGreeting(name); } public void EnglishGreeting(string name){ Console.WriteLine(“Morning, ” + name); } }
假设以后这段代码需要全球化,加入中国人问候的方法。 -- 首先需要添加中国人问候的方法修改代码如下:
public void ChineseGreeting(string name){ Console.WriteLine(“早上好”, + name); }
然后添加枚举来区分语言,修改代码如下:
enum Language{ English, Chinese }
为调用 ChineseGreeting()这个方法我们同样需要修改 GreetingPeople() 这个方法:
public void GreetPeople(string name, Language language){ if(language == Language.Chinese){ ChineseGreeting(name); }else if(language == Language.English){ EnglishGreeting(name); } }
最终代码如下:
class HelloWorld{ public void GreetPeople(string name, Language language){ //这里的 Language 为上文提到的枚举 if(language == Language.Chinese){ ChineseGreeting(name); }else if(language == Language.English){ EnglishGreeting(name); } } public void EnglishGreeting(string name){ Console.WriteLine(“Morning, ” + name); } public void ChineseGreeting(string name){ Console.WriteLine(“早上好”, + name); } }
这个小程序真的做好了吗? -- 上面的方案大家很容易想到。 利用枚举去扩展语言。 但是这个解决方案 扩展性很 差。 假如日后我们需要加入 日语,韩语,拉丁语等。 那我们不得不反复修改 枚举添加新 的语言 和 GreetingPeople() 内部利用 if ... else 或者 switch 分支去根据传入的参数,判断 调用某个语言进行问好。
思考分析
我们看(string name),string 是参数类型,name 是参数变量。 我们根据传入的形参 去进行相应的赋值。 那么假如 GreetPeople() 方法可以接受另一个参数变量,这个参数变 量可以代表某个方法, 当我们调用 GreetPeople() 的时候就不要在内部进行判断,我们直接 告诉它,喂你帮我调用 xxx 方法,问题不就解决了吗? ++++实际上我们只需要修改成这样: GreetPeople(string name, **** method), 那么现在的 问题就是 method 参数的这个形参的具体类型是什么。 ++++没错,method 的类型就是委托。 这里的 method 参数能够确定方法的种类,进一步讲, method 代表方法的参数类型和返回类型。
声明并定义委托
class HelloWorld{ //声明委托 public delegate void GreetingDelegate(string name); ...... }
void 表示委托代表的方法的返回值类型。
string name 表示委托代表的方法的参数类型。
有了委托之后我们可以修改 GreetPeople(string name, **** method),代码如下:
public void GreetingPeople(string name, GreetingDelegate method){ //委托的调用方法同方法调用一样 method(name); }
-这里的 GreetingDelegate 就是 method 的类型,或者叫类。 需要注意的是委托的声明 方式和类却完全不同。 实际上,委托在编译的时候确实会编译成类。 因为 Delegate 是一 个类,所以在任何可以声明类的地方都可以声明委托。
利用委托实现最终代码效果
class HelloWorld{ public delegate void GreetingDelegate(string name); public static void EnglishGreeting(string name){ Console.WriteLine(“hello, ” + name); } public static void ChineseGreeting(string name){ Console.WriteLine(“你好,” + name); } public void GreetingPeole(string name, GreetingDelegate method){ method(name); } } //测试运行代码 class Program{ static void Main(string[] args){ HelloWorld hw = new HelloWorld(); hw.GreetingPeople(“中国人”, HelloWorld.ChineseGreeting); Console.ReadKey(); } }
总结
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传 递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用 If-Else(Switch)语句, 同时使得程序具有更好的可扩展性。
将方法绑定到委托
既然委托同 string 都是类型,那我们也可以利用委托声明类似 name 这样的委托变量。 修改代码如下:
class Program{ static void Main(string[] args){ HelloWorld hw = new HelloWorld(); HelloWorld.GreetingDelegate delegate1, delegate2; delegate1 = HelloWorld.EnglishGreeting; delegate2 = HelloWorld.ChineseGreeting; hw.GreetingPeople(“中国人”, delegate1); hw.GreetingPeople(“yanlz”, delegate2); Console.ReadKey(); } }
如你所料,这样是没有问题的。 委托不同于 string 的一个特征: 可以将多个方法赋给 同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其 所绑定的方法。 如下:
HelloWorld.GreetingDelegate delegate1; delegate1 = HelloWorld.EnglishGreeting; delegate1 += HelloWorld.ChineseGreeting; hw.GreetingPeople(“yanlz”, delegate1);
实际上我们可以绕过调用 GreetingPeople(),通过委托来直接调用 EnglishGreeting 和 ChineseGreeting:
HelloWorld.GreetingDelegate delegate1; delegate1 = HelloWorld.EnglishGreeting; delegate1(“yanlz”);
委托绑定方法时需要注意(多播委托), 注意这里,第一次用的“=”,是赋值的语法; 第二次,用的是“+=”,是绑定的语法。 如果第一次就使用“+=”,将出现“使用了未赋 值的局部变量”的编译错误。 既然委托属于类,我们也可以利用这一特性直接 new 出委托实例。
HelloWorld.GreetingDelegate delegate1 = new HelloWorl.GreetingDelegate(HelloWorld.ChineseGreeting); delegate1 += HelloWorld.EnglishGreeting; delegate1(“yanlz”);
既然给委托可以绑定一个方法,那么也应该有办法取消对方法的绑定。 很容易想到, 利用 -=,代码如下:
HelloWorld.GreetingDelegate delegate1 = new HelloWorld.GreetingDelegate(HelloWorld.ChineseGreeting); delegate1 += HelloWorld.EnglishGreeting; delegate1(“yanlz”); delegate1 -= HelloWorld.ChineseGreeting; delegate1(“yanlz”);
匿名函数
以上是关于至今最大之敌:委托和事件(重中之重)的主要内容,如果未能解决你的问题,请参考以下文章
对“xxx”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。 错误解决一例。(代码片段