至今最大之敌:委托和事件(重中之重)

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”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。 错误解决一例。(代码片段

难免的尴尬:代码依赖

jQuery代码优化:事件委托

C#委托与事件

part01.03 委托与 Lambda 表达式:委托