<center>C#委托与事件的总结</center>

1.概述

委托与事件是C# 1.0版本的新特性,与C# 1.0版本对应的是.NET Framework 1.0,发布于2002年1月,在这个版本中C#还提供了编程语言基础特性:

  • Classes:面向对象特性,支持类类型
  • Structs:结构
  • Interfaces:接口
  • Events:事件
  • Properties:属性,类的成员,提供访问字段的灵活方法
  • Delegates:委托,一种引用类型,表示对具有特定参数列表和返回类型的方法的引用
  • Expressions,Statements,Operators:表达式、语句、操作符
  • Attributes:特性,为程序代码添加元数据或声明性信息,运行时,通过反射可以访问特性信息
  • Literals:字面值(或理解为常量值),区别常量,常量是和变量相对的

本篇博文将通过一个事例来说明委托与事件,其中包括了委托的定义,委托的使用,事件是如何产生(在这之中将说明事件与委托的关系),以及委托与事件特性有什么优势,为什么要去定义这一特性,我们为什么要去使用这一特性。

本篇博文是对大牛们的博客与书籍的学习总结,可能会有雷同之处,如若侵权,烦请告知,立即删除。

本篇博文只是一名初学者的皮毛见解,仅供以学习交流,如有错误之处,烦请指出校正,如若希望更深入的学习或者得到更清晰的描述,请前往各位大牛的博客,链接地址将在参考目录中给出。

2.委托

2.1 什么是委托

在生活中我们遇到过许多委托,子学校里不想去上课,我们也许会委托室友答到,在法庭上,辩护人会委托律师为其进行辩护。生活中有许多这样的例子,单子啊C#中,委托是什么样子呢?

委托在C/C++中可以理解为函数指针,党调用这个指针时,便执行了这个指针所指向的函数,在C#中可以理解为函数的一个包装,调用委托时便执行了该函数。

C/C++通过函数指针获得了函数的入口地址,通过调用函数指针来实现对函数的操作

要怎樣去理解呢?我們以A同學委託B同學答到這一事例來模擬一下,A同學是一個類class A,有一個答到方法public void Dadao(string name),答到方法有一個參數name提供了A同學的名字,現在A同學想睡懶覺不想去上課了,他委託了室友B去答到,黨老師叫到A的時候,B便會說:“A,到!”(我們只用調用B便會得到調用A類中Dadao方法的結果),具體代碼參見代碼清單 2-1

public class A
{
public void Dadao(string name)
{
Console.WriteLine("A,到!");
}
} public delegate void DadaoHandler(); class MainEntry
{
static void Main()
{
DadaoHandler B =
new DadaoHandler(new A().Dadao); B("A");
}
}

代码清单 2-1

或許這個事例說明的有點彆扭,不妨換個事例,還是同學A與同學B之間的故事,B是我們高中班上的美女小組長,A是我們的猥瑣後桌。

我們每天上學要做的第一件事是什麼?對了,交作業!美麗的清晨,東方才微微泛出陽光,空氣中充滿了泥土的清香,美女小組長B跟猥瑣后桌B說:“那誰,emmm,帥哥,等下幫我收下作業唄,我去看體育生學長晨練了~”,那誰非常高興的答應了,老師來了說把作業收起來,我們的A哥便馬上屁顛屁顛的去收作業了,收的時候還不忘向你嘚瑟下。

這個事例中,美女小組長B委託了猥瑣后桌A收作業。B中有收作業的方法,當老師調用A時,便調用了B中的收作業方法。

2.2 委託的使用

下面將會通過模擬接待外賓時,廚師、服裝師所做的準備。我們有一個廚師類,一個服裝師類,廚師類有個烘焙蛋糕的方法,服裝師類有個穿正裝的方法,我們聲明了一個外賓接待委託。我們將以這個事例來說明委託的聲明、實例化以及綁定,详细代码请参考代码清单2-2

2.2.1 委託的申明

前面我們說過,委託是方法的一個包裝。它與方法有著相同的方法簽名和返回類型。

[訪問修飾符] delegate return_type handler_name(args_type args);

現在我們的廚師class Cook有一個烘焙蛋糕的方法void BakeCoke(string name);,我們的服裝師class Dress有一個穿正裝的方法void FormalWear(string name);其中name是外賓的名字。現在我們為這兩個方法創建一個委託,我們可以使用一下申明:

public delegate void ForeignHandler(string name);

方法簽名相同:方法的參數個數、順序、類型相同

2.2.2 委託的實例化

現在我們將ForeignHandler實例化,將需要委託的方法作為構造函數參數來實例化委託。

ForeignHandler fg_handler = new ForeignHandler(new Cook().BackCoke);

當然,靜態方法也可以進行委託,現在我們將烘焙蛋糕的方法設置為靜態方法static void BakeCoke(string name);

ForeignHandler fg_handler = Cook.BakeCoke;

2.2.3 方法绑定委托

绑定是委托的一个特性,它能让我们在一个委托事例中绑定多个方法,不需要去为每个方法去实例化一个委托。绑定符号为+=,当然我们可以使用-=来解除绑定。当将方法绑定到委托上后,调用委托时,绑定在这个委托上的所有方法都会被执行。

fg_handler += new Dress().FormalWear;

2.3 深入理解委托

现在我们知道了怎样去使用委托,但我们要怎样去理解委托?其实,大家应该有了一个认识,委托其实就是一个类,我们对委托的一些操作其实就是对类的操作,只是有一些特殊之处。

现在我们不使用委托来实现包装Cook.BakeCoke方法,我们需要怎样做?我们需要定义一个方法void function();,然后需要一个参数来接收BakeCoke方法,所以我们的方法变为了void function(*** MakeBakeCoke);,因为BakeCoke方法中有一个name参数,所以我们需要为function添加一个string类型的参数,最终,这个方法便如下所示:

public void function(string name, *** MakeBakeCoke)
{
MakeBakeCoke(name);
}

但是是,我们不知道MakeBakeCoke的类型,也就是说***具体应该怎样写?现在我们有了委托,一切问题都解决了,我们声明一个委托类型,然后将委托类型写在这就可以了。

2.4 为什么要使用委托

委托能够有效的为代码解耦,提高代码的可扩展性,具体如何尚在学习之中,在参考目录中有对于代码耦合度的相关链接,大家可以前往进行学习。

2.5 代码清单

namespace Event
{
public delegate void ForeignHandler(string name); public class Cook
{
public void BakeCoke(string name)
{
Console.WriteLine("Your Majesty,We'll baking coke for " + name);
}
} public class Dress
{
public void FormalWear(string name)
{
Console.WriteLine("Yor Majesty, You'll wear formal to meeting " + name);
}
} public class MeetingForeignManage
{
public void MeetingForeign(string name, ForeignHandler make_meeting)
{
make_meeting(name);
}
} class Program
{
static void Main(string[] args)
{
// 實例化委託
// ForeignHandler fg_handler;
// fg_handler = new CookServant().BakeCoke;
ForeignHandler fg_handler =
new ForeignHandler(new Cook().BakeCoke); // 綁定委託
fg_handler += new Dress().FormalWear;
// 調用委託
MeetingForeignManage mt_fg = new MeetingForeignManage();
mt_fg.MeetingForeign("Jorge King", fg_handler); Console.Read();
}
}
}

代码清单 2-2

3.事件

3.1 事件与委托的关系

在本小节中,我们将从事件的由来来慢慢理清事件与委托之间的关系,在理清这层关系之后我们将对委托与事件的理解更上一个层次,在日后代码量增多后,也将会对委托与事件的应用场景,委托与事件特性优势会有自己的理解。

3.3.1 事件的由来

通过代码清单2-2,我们大概知道了委托是如何使用的,但面向对象的程序是有封装性这一特性的,既然讲究封装性,我们何不把在Main函数中定义的委托对象封装到MeetingForeignManage类中呢?现在我们进行这一操作,于是,MeetingForeign类和Main函数代码变成了如代码清单3-1所示。

    public class MeetingForeignManage
{
public ForeignHandler fg_handler; // MeetingForeignManage类封装了委托 public void MeetingForeign(string name, ForeignHandler make_meeting)
{
make_meeting(name);
}
} class Program
{
static void Main(string[] args)
{
MeetingForeignManage mt_fg = new MeetingForeignManage();
mt_fg.fg_handler = new Cook().BakeCoke;
mt_fg.fg_handler += new Dress().FormalWear;
mt_fg.MeetingForeign("Jorge King", mt_fg.fg_handler); Console.Read();
}
}

代码清单 3-1

这样运行结果是没问题,但是有条语句却很奇怪mt_fg.MeetingForeign("Jorge King", mt_fg.fg_handler);我们在调用这个方法时,为什么还要把MeetingForeignManage类中的成员再传递到该类中?所以我们可以代码进行如代码清单 3-2所示的改进。

    public class MeetingForeignManage
{
public ForeignHandler fg_handler; public void MeetingForeign(string name)
{
if (fg_handler != null)
{
fg_handler(name);
}
}
} class Program
{
static void Main(string[] args)
{
MeetingForeignManage mt_fg = new MeetingForeignManage();
mt_fg.fg_handler = new Cook().BakeCoke;
mt_fg.fg_handler += new Dress().FormalWear;
mt_fg.MeetingForeign("Jorge King"); Console.Read();
}
}

代码清单 3-2

这样运行是可以得到我们想要的结果,但我们却要面对一个显而易见的问题,那边是fg_handler字段竟然是public公开的!这样导致了我们的任何MeetingForeignManage实例都可以对fg_handler进行操作,我们为了封装却严重了违反了封装的原则,这真是可笑的!但是我们能将fg_handler设置为private私有的吗?这显然也是不可行的,因为委托的目的就是把暴露在类的客户端进行的方法进行注册,设置为私有了,客户端无法调用它,那它还有什么用呢?所以C#为我们定义了事件这一特性。

好了,让我们把代码再改一下,改成和代码清单 3-3一样。

    public class MeetingForeignManage
{
public event ForeignHandler fg_event; public void MeetingForeign(string name)
{
fg_event(name);
}
} class Program
{
static void Main(string[] args)
{
MeetingForeignManage mt_fg = new MeetingForeignManage();
mt_fg.fg_event += new Cook().BakeCoke;
mt_fg.fg_event += new Dress().FormalWear;
mt_fg.MeetingForeign("Jorge King"); Console.Read();
}
}

代码清单 3-3

3.3.2 委托与事件的关系

通过对事件的由来的分析,我们可以这样理解委托与事件,委托可以类比为字段,事件可以理解为属性,也就是说事件对委托的封装可以类比为属性对字段的封装,这两种封装模式是完全一样的,不同的是事件对委托的封装实在编译阶段完成,是不可自定义的封装,而属性对字段的封装是在coding阶段进行的,是可自定义的封装。

3.2 订阅者与发布者(监视对象与监视者)

现在我们用Observer设计模式来完成我们的代码,我们有一位君主class Emperor,今天他有一件重要的事去做,那边是接见外宾,接见外宾时他要把自己打扮的美美的,毕竟面子事大,所以他需要服装师class Dress来帮他选衣服public void FormalWear(string name);,另外还要准备可口又不失优雅的食物来招待外宾,毕竟面子事大,所以他需要一个顶级的厨师class Cook来帮助他烘焙可口的蛋糕public void BakeCoke(string name);,现在爱美的君主要准备接见外宾了,他触发了接待外宾的事件public void MeetingWithForeignGuests(string fg_name);,我们一直关注着君主动态的厨师和服装师准备开始烘焙蛋糕和为君主穿上正装了(注册了事件的方法fg_coming.fg_event += new Cook().BakeCoke; fg_coming.fg_event += new Dress().FormalWear;),在这个代码中,君主是发布者(监视对象),厨师和服装师是订阅者(监视者),当事件一触发,他们便会作出反应。具体代码参见代码清单 3-4

namespace Event
{
public delegate void ForeignHandler(string name); // 订阅者(监视者)
public class Cook
{
public void BakeCoke(string name)
{
Console.WriteLine("Your Majesty,We'll baking coke for " + name);
}
} // 订阅者(监视者)
public class Dress
{
public void FormalWear(string name)
{
Console.WriteLine("Yor Majesty, You'll wear formal to meeting " + name);
}
} // 发布者(监视对象)
public class Emperor
{
public event ForeignHandler fg_event; public void MeetingWithForeignGuests(string fg_name)
{
Console.WriteLine("We need meeting with foreign guests."); if (fg_event != null)
{
fg_event(fg_name);
}
}
} class Program
{
static void Main(string[] args)
{
string fg_name = "Jorge King"; Emperor fg_coming = new Emperor(); // 使Cook和Dress订阅事件
fg_coming.fg_event += new Cook().BakeCoke;
fg_coming.fg_event += new Dress().FormalWear; fg_coming.MeetingWithForeignGuests(fg_name); Console.Read();
}
}
}

代码清单 3-4

4.參考目錄

對以上文章表示由衷感謝。

C#委托与事件总结的更多相关文章

  1. .NET面试题系列[7] - 委托与事件

    委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...

  2. .NET基础拾遗(4)委托、事件、反射与特性

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

  3. [转载]C#深入分析委托与事件

    原文出处: 作者:风尘浪子 原文链接:http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 同类链接:http://www.c ...

  4. [转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)

    原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委 ...

  5. C#委托与事件

    一.在控制台下使用委托和事件 我们都知道,C#中有"接口"这个概念,所谓的"接口"就是定义一套标准,然后由实现类来具体实现其中的方法,所以说"接口,是 ...

  6. C#委托与事件的简单使用

    前言:上一篇博文从原理和定义的角度介绍了C#的委托和事件.本文通过一个简单的小故事,来说明C#委托与事件的使用方法及其方便之处. 在阅读本文之前,需要你对委托和事件的基本概念有所了解.如果你是初次接触 ...

  7. C#之委托与事件

    委托与事件 废话一堆:网上关于委托.事件的文章有很多,一千个哈姆雷特就有一千个莎士比亚,以下内容均是本人个人见解. 1. 委托 1.1 委托的使用 这一小章来学习一下怎么简单的使用委托,了解一些基本的 ...

  8. [ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - 委托和事件

    在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但 ...

  9. .NET委托和事件

    .net学习之委托和事件   1.什么是委托 通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器 上传图片: 2.委托语法 准备一个方法:string Hello(string ...

  10. C#委托和事件

    委托和事件都可以用来调用跟自己方法签名一样的方法,两者在使用中主要有以下区别: 委托和事件没有可比性,因为委托是类型,事件是对象: 委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用: 委 ...

随机推荐

  1. django+javascrpt+python实现私有云盘

    代码稍后上,先整理下私有云盘的相关功能介绍. 1.登陆界面 2.首页展示,有个人目录.部门目录以及公司目录,针对不用的目录设置不同的权限控制. 3.个人信息展示 4.账号管理.账号信息展示 5.账号添 ...

  2. Edge-assisted Traffic Engineering and applications in the IoT

    物联网中边缘辅助的流量工程和应用 本文为SIGCOMM 2018 Workshop (Mobile Edge Communications, MECOMM)论文. 笔者翻译了该论文.由于时间仓促,且笔 ...

  3. 自学Python的经验之谈,学好Python的捷径

    其实python非常适合初学者入门.相比较其他不少主流编程语言,有更好的可读性,因此上手相对容易.自带的各种模块加上丰富的第三方模块,免去了很多“重复造轮子”的工作,可以更快地写出东西.配置开发环境也 ...

  4. 阿里云对象存储 OSS 应用服务器搭建代码

    背景说明 最近做一个APP客户端图片直传阿里云OSS的服务,需要在后台开一个阿里云的OSSToken获取的接口. 阿里云官方文档地址:快速搭建移动应用直传服务. 略过移动端说明,直接看服务端的. 不是 ...

  5. [Swift]LeetCode173. 二叉搜索树迭代器 | Binary Search Tree Iterator

    Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the ro ...

  6. [Swift]LeetCode306. 累加数 | Additive Number

    Additive number is a string whose digits can form additive sequence. A valid additive sequence shoul ...

  7. [Swift]LeetCode854. 相似度为 K 的字符串 | K-Similar Strings

    Strings A and B are K-similar (for some non-negative integer K) if we can swap the positions of two ...

  8. so库链接和运行时选择哪个路径下的库?

    总结今天遇到的一个so库链接.运行问题. 这几天修改了xapian的源码,重新编译so库,再重新编译之前的demo程序,跑起来后却发现执行的函数并非我修改过的,使用的还是老版本.折腾了一会儿,发现是因 ...

  9. Prometheus使用入门

    Monitoring with Prometheus读书笔记 原书见: https://www.safaribooksonline.com/library/view/monitoring-with-p ...

  10. 华为oj之字符串分割

    题目: 字符串分隔 热度指数:6139 时间限制:1秒 空间限制:32768K 本题知识点: 字符串 题目描述 •连续输入字符串,请按长度为8拆分每个字符串后输出到新的字符串数组: •连续输入字符串, ...