C#高级编程之委托

看C#高级编程第八版看到委托,有人说它和Java中的代理有些相似,但我认为这是一个C#与其他编程语言不同的地方,这也应该很重要,不然书上也不会大篇幅的讲委托的概念和应用。我在网上查阅了关于委托的资料,发现它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里憋得慌,混身不自在。

那初次看见“委托”这个词,我脑海中出现的第一个疑问就是:

1.什么是委托?

我看完整这个章节,依然是一知半解的,功夫不负有心人,通过强大的网路,我寻找到了答案,那到底什么是委托呢?

委托就是可以把方法作为参数传递,也就是说通过委托可以像操作变量一样的操作方法参数。看到这里我发现这并不是C#首创的这个概念,因为在C++中就有函数指针的概念和应用,我理解这其实就是微软把C++中的函数指针作为原型而衍生出来的,经过封装的这么一个特殊概念,其实现在我看来也不是很特别吗,毕竟在C++中曾经学过呀。

现在我已经知道了委托的概念了,那么我第二个问题又来了。

2.这个委托怎么用呢?

我觉得怎么用,还是要拿Demo来进行讲解会比较方便理解和对比。下面是我参看网络上的大神们所用到代码,我们不讨论所给Demo的代码是否有实际意义,只是为了让读者能够更加方便的理解什么叫委托。代码如下:

a.现在要模仿人打招呼。那么我们需要知道他的名字方法如下:

public static void ChineseGreeting(string name){
    Console.WriteLine("早上好!"+name);
    }

static void Main(string[] args) {
    ChineseGreeting("李雷");
    Console.ReadKey();
    }

b.要是现在要给一个老外(不懂中文的)打招呼,那要这怎么办呢?是不是应该知道给老外说英文呢?单独创建一个方法呢?不过在创建方法之前,我们需要先用一个枚举类型去判读是外国人还是中国人。

代码如下:

public enum Language{
    Chinese,English
    }
public void EnglishGreeting(string name){
    Console.WriteLine("Morning !"+name);
    }
 public GreatPeople(string name,Language language){
     switch(language)
     {
         case language.English:EnglishGreeting(name);break;
         case language.Chinese:ChineseGreeting(name);break;
      }
  }

只要有些编程经验的人就知道,上面的解决方案不好,如果下次有日本人,有韩国人,非洲人等等,那么我们是不是就又要去添加枚举元素,添加问候方法!上面的方法不利于扩展。所以我们就想有没有什么可以不用这么麻烦而且能很好的扩展呢?我现在分析上面的解决方案是如何做到的。

首先添加一个对应语言的问候的方法是不可少的,枚举元素也是必须的,枚举元素是为了判断调用哪个语言的问候方法去问候的标志,那么既然我们前面提到了方法可以做参数那为什么不直接传方法名称呢?下面就是利用代理实现上面方案的代码如下:

 public delegate void GreetingDelegate(string name);
    class Program
    {
        public static void EnglishGreeting(string name)
        {
            Console.WriteLine("Morning!"+name);
        }
        public static void ChineseGreeting(string name)
        {
            Console.WriteLine("早上好!" + name);
        }

        //这是一个
        public static void GreetingPeople(string name, GreetingDelegate greetingmethod)
        {
            
            greetingmethod(name);//
            
        }
        static void Main(string[] args)
        {
            GreetingPeople("Mike", EnglishGreeting);
            GreetingPeople("李雷", ChineseGreeting);
            Console.ReadKey();
        }
    }

看上面的代码,是不是感觉舒服多了我们不用使用枚举了,也不用进行选择(switch)了。

请注意:public static void GreetingPeople(string name, GreetingDelegate greetingmethod)这个方法中使用了一个委托类型的参数传递使用的问候方法。是不是感觉很神奇。

看到上面的Demo你是不是对委托有些感觉了呢?现在我的问题又来了。

3.方法是如何绑定到委托的?

这个问题我也需要借用上面的Demo进行修改一下,以便于显示效果。Demo代码如下:

public delegate void GreetingDelegate(string name);
    class Program
    {
        public static void EnglishGreeting(string name)
        {
            Console.WriteLine("Morning,"+name);
        }
        public static void ChineseGreeting(string name)
        {
            Console.WriteLine("早上好!" + name);
        }

        public static void GreetingPeople(string name, GreetingDelegate greetingmethod)
        {
            Console.WriteLine("问候开始...");
            greetingmethod(name);
            Console.WriteLine("问候结束!");
        }
        static void Main(string[] args)
        {
            GreetingDelegate delegate1;//委托实例
            delegate1 = EnglishGreeting;
            delegate1 += ChineseGreeting;
            GreetingPeople("Mike", EnglishGreeting);
            GreetingPeople("李雷", ChineseGreeting);
            GreetingPeople("John", delegate1);
            Console.ReadKey();
        }
    }

结果如下:

多个方法可以被绑定到一个委托实例中去。

注释:delegate1 = EnglishGreeting;这个操作符"="是初始化委托实例,那么"+="是绑定语法。那么到底

初始化和绑定还可以这样做:

            GreetingDelegate delegate1;
            delegate1 = EnglishGreeting;
            delegate1 += ChineseGreeting;
            GreetingPeople("John", delegate1);

            GreetingDelegate delegate2 = new GreetingDelegate(EnglishGreeting);
            delegate2 += ChineseGreeting;
            GreetingPeople("LiLy", delegate2);
            Console.ReadKey();

这样的看,委托是不是和类非常的相似!你先下面这段代码和信息你会发现越发的像。

 GreetingDelegate delegate3 = new GreetingDelegate();//报错,不含有0个参数的构造函数
 delegate3 += EnglishGreeting;
 Console.ReadKey();

说明编译器在编译的时候会把委托当做类进行编译。这一点也是函数指针比不了的。

总结:

GreetingDelegate
    1.委托定义

委托的声明原型是 
    delegate <函数返回类型> <委托名> (<函数参数>)
    例子:public delegate void GreetingDelegate(string name);//定义了一个委托它可以注册返回void类型且有一个int作为参数的函数这样就定义了一个委托,但是委托在.net内相当于声明了一个类,类如果不实例化为对象,很多功能是没有办法使用的,委托也是如此.

2.委托实例化

委托实例化的原型是
<委托类型> <实例化名>=new <委托类型>(<注册函数>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//用函数CheckMod实例化上面的CheckDelegate 委托为_checkDelegate
在.net 2.0开始可以直接用匹配的函数实例化委托:
<委托类型> <实例化名>=<注册函数>
例子:CheckDelegate _checkDelegate=CheckMod;//用函数CheckMod实例化上面的CheckDelegate 委托为_checkDelegate现在我们就可以像使用函数一样来使用委托了,在上面的例子中现在执行_checkDelegate()就等同于执行CheckMod(),最关键的是现在函数CheckMod相当于放在了变量当中,它可以传递给其它的CheckDelegate引用对象,而且可以作为函数参数传递到其他函数内,也可以作为函数的返回类型

3.用匿名函数初始化委托

上面为了初始化委托要定义一个函数是不是感觉有点麻烦,另外被赋予委托的函数一般都是通过委托实例来调用,很少会直接调用函数本身。

在.net 2.0的时候考虑到这种情况,于是匿名函数就诞生了,由于匿名函数没有名字所以必须要用一个委托实例来引用它,定义匿名函数就是为了初始化委托

匿名函数初始化委托的原型:

<委托类型> <实例化名>=new <委托类型>(delegate(<函数参数>){函数体});

当然在.net 2.0后可以用:

<委托类型> <实例化名>=delegate(<函数参数>){函数体};

delegate void Func1(int i);
        delegate int Func2(int i);

        static Func1 t1 =new Func1(delegate(int i)
        {
            Console.WriteLine(i);
        });

        static Func2 t2;

        static void Main(string[] args)
        {
            t2 = delegate(int j)
            {
                return j;
            };
            t1(2);
            
            Console.WriteLine(t2(1));
            
        }

当然在.net 3.0的时候又有了比匿名函数更方便的东西lambda表达式,这儿就不说了。

4.泛型委托
    委托也支持泛型的使用
    泛型委托原型:
    delegate <T1> <委托名><T1,T2,T3...> (T1 t1,T2 t2,T3 t3...)
    例子:
    delegate T2 A<T1,T2>(T1 t);//定义有两个泛型(T1,T2)的委托,T2作为委托函数返回类型,T1   作为委托函数参数类型

static int test(int t)
    {
      return t;
    }

static void Main(string[] args)
    {
      A<int, int> a =test;//将泛型委托委托<T1,T2>实例化为<int,int>,即表示有一个int类型            参数且返回类型是int的函数,所以将test用来实例化委托
      Console.WriteLine(a(5));//输出5
    }

5.委托的多播性
    在上面实例化委托的时候看到:必须将一个匹配函数注册到委托上来实例化一个委托对象,但是一个实例化委托不仅可以注册一个函数还可以注册多个函数,注册多个函数后,在执行委托的时候会根据注册函数的注册先后顺序依次执行每一个注册函数
函数注册委托的原型:
<委托类型> <实例化名>+=new <委托类型>(<注册函数>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//将函数CheckMod注册到委托实例_checkDelegate上
在.net 2.0开始可以直接将匹配的函数注册到实例化委托:
<委托类型> <实例化名>+=<注册函数>
例子:CheckDelegate _checkDelegate+=CheckMod;//将函数CheckMod注册到委托实例_checkDelegate上
之后我们还可以注册多个函数到委托上:
例子:_checkDelegate+=CheckPositive;//将函数CheckPositive注册到委托实例_checkDelegate上
        _checkDelegate();//执行这个委托实例会先执行CheckMod()再执行CheckPositive()

实际上使用+=符号的时候会判断
如果此时委托还没有实例化(委托实例为null),它会自动用+=右边的函数实例化委托
如果此时委托已经实例化,它会只把+=右边的函数注册到委托实例上
另外有一点需要注意的是,如果对注册了函数的委托实例从新使用=号赋值,相当于是重新实例化了委托,之前在上面注册的函数和委托实例之间也不再产生任何关系,后面的例子会讲到这点!

当然有+=注册函数到委托,也有-=解除注册
例子:_checkDelegate-=new CheckDelegate(CheckPositive);//解除CheckPositive对_checkDelegate的注册
        _checkDelegate-=CheckPositive;//.net 2.0开始可以用这种方式解除注册

时间: 2024-10-22 14:35:39

C#高级编程之委托的相关文章

《C#高级编程》委托、事件的示例代码

运行结果: Program.cs 1 using System; 2 3 namespace Wrox.ProCSharp.Delegates 4 { 5 class Program 6 { 7 static void Main() 8 { 9 var dealer = new CarDealer(); 10 11 var michael = new Consumer("Michael"); 12 dealer.NewCarInfo += michael.NewCarIsHere; 1

c#高级编程笔记----委托

因为定义委托基本上是定义一个新类,所以可以在定义类的任何相同地方定义委托,也就是说,可以在另一个类的内部定义,也可以在任何类的外部定义,还可以在名称空间中把委托定义为顶层对象.根据定义的可见性,和委托的作用域,可以在委托的定义上应用任意常见的访问修饰符:public.private.protected等: 实际上,“定义一个委托”是指“定义一个新类”.委托实现为派生自基类System.MulticastDelegate的类,System.MulticastDelegate又派生自基Syetem.

C#高级编程(第8版)——委托声明、使用(第一节)

首先,声明一个Currency的结构.Currency结构有自己的ToString()重载方法和一个与GetCurrencyUnit()的签名相同的静态方法.这样,就可以用同一个委托变量调用这些方法了: struct Currency { public uint Dollars; public ushort Cents; public Currency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents

《C#高级编程》读书笔记

<C#高级编程>读书笔记 C#类型的取值范围 名称 CTS类型 说明 范围 sbyte System.SByte 8位有符号的整数 -128~127(−27−27~27−127−1) short System.Int16 16位有符号的整数 -32 768~32 767(−215−215~215−1215−1) int System.Int32 32位有符号的整数 -2 147 483 648~2 147 483 647(−231−231~231−1231−1) long System.Int

读javascript高级编程11-事件

一.事件流 事件流指从页面中接收事件的顺序. 1.事件冒泡(常用) IE中采用的事件流是事件冒泡,先从具体的接收元素,然后逐步向上传播到不具体的元素. 2.事件捕获(少用) Netscapte采用事件捕获,先由不具体的元素接收事件,最具体的节点最后才接收到事件. 3.DOM事件流 DOM2级事件包括三个阶段:事件捕获阶段.处于目标阶段和事件冒泡阶段. 二.事件处理程序 事件处理程序就是响应某些事件的函数,如onclick等. 1. DOM0级事件处理程序 每个元素都有自己的事件处理程序属性,如o

C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)

译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# 6 and .NET Core 1.0>.水平有限,各位阅读时仔细分辨,唯望莫误人子弟. 附英文版原文:Professional C# 6 and .NET Core 1.0 - 38 Entity Framework Core ------------------------------- 本章内容 En

iOS网络高级编程:iPhone和iPad的企业应用开发之错误处理

本章内容 ●    iOS应用中的网络错误源 ●    检测网络的可达性 ●    错误处理的经验法则 ●    处理网络错误的设计模式 到目前为止,我们所介绍的iPhone与其他系统的网络交互都是基于一切正常这个假设.本章将会放弃这个假设,并深入探究网络的真实世界.在真实世界中,事情是会出错的,有时可能是非常严重的错误:手机进入与离开网络.包丢掉或是延迟:网络基础设施出错:偶尔用户还会出错.如果一切正常,那么编写iOS应用就会简单不少,不过遗憾的是现实并非如此.本章将会探讨导致网络操作失败的几

《C#高级编程》笔记系列第三弹

我们在开发WinForm时,经常会看到partial关键字,比如,我们新建一个Windows Form时,后台代码自动添加如下: 1 public partial class Form1 : Form2 {3     public Form1()4     {5         InitializeComponent();6     }7 } <C#高级编程>书中说明:partial关键字允许把类.结构或接口放在多个文件中.一般情况下,一个类存储在单个文件中,但有时,多个开发人员需要访问同一个

IOS高级编程之一:多点触摸与手势验证

前段时间学习了IOS基础的一些控件的使用作为基础,现在开始学习一些高级编程的东西,手势处理器.文件I/O.定位.网络通信.多线程这些,分享一些学习的重点,还是很实用的. 今天就先介绍个简单点得,手势处理器: 一.首先,应该清楚ios事件相应的顺序,即响应者链. 只要继承了UIResponder的对象就可以作为响应者,实际上UIControl:UIView:UIResponder,由此可见,所有的对象都可以作为事件的相应者. 当用户与某个控件交互时,该控件将作为第一响应者(First Respon