.NET面试题系列(十)委托与事件
序言
委托
有了委托的存在,使得方法可以作为参数传递给另一个方法。
int Max(int x,int y)
{
return x>y?x:y;
}
int Min(int x,int y)
{
return x<y?x:y;
}
上面两个函数的共同特点:具有相同的返回值和参数列表。在C++里,我们使用函数指针来指代(被授权,代表)这两个函数。
实际上,我们可以用函数指针指向任意一个具有相同返回值和参数列表的函数(静态方法或实例的方法成员)。
在C#里没有提供函数指针,取而代之的是委托(delegate);利用委托,我们可以像使用函数指针一样在程序运行时动态指向具备相同签名(具有相同参数类型、参数个数以及相同类型返回值)的方法。
委托的本质:函数指针。说的通俗一些,委托就是能够让方法作为变量来传递。
委托是一种类型安全的函数回调机制, 它不仅能够调用实例方法,也能调用静态方法,并且具备按顺序执行多个方法的能力。
委托的声明
Demo1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace DelegateSamples
{
//声明一个委托,参数为string,无返回值
delegate void DelSamples(string msg);
class Program
{
static void Main(string[] args)
{
//使用new关键字
DelSamples delSample = new DelSamples(new Program().SpeakChinese);
delSample("Koala工作室"); //不使用new,自动推断委托类型
DelSamples delSample2 = SpeakEnglish;
delSample2("KoalaStudio"); //利用Lambda表达式
DelSamples delSample3 = (string msg) => SpeakEnglish(msg);
delSample3("KoalaStudio"); Console.ReadKey();
} private void SpeakChinese(string msg)
{
Console.WriteLine("你好,我是{0}",msg);
} private static void SpeakEnglish(string msg)
{
Console.WriteLine("Hello,I'm {0}",msg);
}
}
}
Demo2
private static void EnglishGreeting(string name)
{
Console.WriteLine("Good Morning, " + name);
} private static void ChineseGreeting(string name)
{
Console.WriteLine("早上好, " + name);
}
public delegate void GreetingDelegate(string name);//委托
private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name);
}
private void button45_Click(object sender, EventArgs e)
{
GreetPeople("Liker", EnglishGreeting);
GreetPeople("沐风", ChineseGreeting);
Console.ReadLine();
}
我们现在对委托做一个总结:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性。
匿名委托
在上一分钟已经知道了,完成一个委托应用分三步走,缺一步都不行,如果要跨大步,当心步子大了扯着蛋。但是微软不怕扯着蛋,非要把三步做成两步来走啊!所以微软就用匿名方法来简化上边的三个步骤。匿名方法这个玩意儿怎么说呢,在C#中完全是可有可无的东西,只是为C#锦上添花,有人别出心裁给它取个名字叫语法糖。
public partial class WebForm3 : System.Web.UI.Page
{
//step01:首先用delegate定义一个委托
public delegate int CalculatorAdd(int x, int y); protected void Page_Load(object sender, EventArgs e)
{
//step02:用这样的写法 delegate(int x, int y) { return x + y; },把一个方法赋值给委托
CalculatorAdd cAdd = delegate(int x, int y) { return x + y; };
int result = cAdd.Invoke(, );
}
}
step01:首先用delegate定义一个委托 。
step02:用这样的写法 delegate(int x, int y) { return x + y; },把一个方法赋值给委托,其实这种写法就是匿名方法。
这时会惊奇的发现,这不是三步当着两步走了哇?
匿名委托的写法更加优雅,但是需要注意两点:
1、在函数内部不能使用跳转语句跳出函数外部;
2、不能使用ref和out等关键字
Lambda表达式实现匿名委托
原本很简单的程序,加上几个delegate关键字,这代码一下就变得深奥了,深奥的东西懂的人就变少了,所以这个还可以作为加薪的筹码。但是微软对C#的设计理念是简单易用。微软就想方设法的来简化delegate(int x, int y) { return x + y; }这个匿名方法,Lambda就出现了。下边我来看几种lambda表达式的写法:
public partial class WebForm3 : System.Web.UI.Page
{
public delegate int CalculatorAdd(int x, int y); protected void Page_Load(object sender, EventArgs e)
{
//方法一:
CalculatorAdd cAdd1 = (int x, int y) => { return x + y; };
int result1 = cAdd1(, ); //方法二:
CalculatorAdd cAdd2 = (x, y) => { return x + y; };
int result2 = cAdd2(, ); //方法三:
CalculatorAdd cAdd3 = (x, y) => x + y;
int result3 = cAdd2(, );
}
}
方法一:简单的把delegate去掉,在()与{}之间加上 "=>"。
方法二:在方法一的基础上把参数类型都干掉了。
方法三:要干就干彻底些,把{},以及return关键字都去掉了。
这几种方法随便怎么写都行,不过就是害苦了初学者,一会儿看到这种写法,一会儿看到那种写法,把人搞的神魂颠倒人,如果没人指点,确实会迷糊,难就难在这儿。
多播委托
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace DelegateSamples
{
//声明一个委托,参数为string,无返回值
delegate void DelSamples(string msg);
class Program
{
static void Main(string[] args)
{
//多播委托可以带返回值,但是只有最后一个方法的返回值会被返回。
DelSamples delSample6 = new Program().SpeakChinese;
delSample6 += SpeakEnglish;
delSample6("KoalaStudio"); Console.ReadKey();
} private void SpeakChinese(string msg)
{
Console.WriteLine("你好,我是{0}",msg);
} private static void SpeakEnglish(string msg)
{
Console.WriteLine("Hello,I'm {0}",msg);
}
}
}
多播委托可以连续执行函数,但是如果函数有返回值,那只有最后一个函数的返回值会被正确返回.
泛型委托
泛型委托包括Action、Func和Predicate三种委托。
1.Action-无返回值
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace DelegateSamples
{
class Program
{
static void Main(string[] args)
{
/* Action<T>:封装只有一个参数(类型为T),不包括返回值的签名函数,它包括以下几种情况:
* Action<T>、Action<T1,T2>、Action<T1,T2,T3>、Action<T1,T2,T3,T4>
* 声明:
* delegate void Action();
* delegate void Action<T1>(T1 arg1);
* delegate void Action<T1,T2>(T1 arg1,T2 arg2);
* delegate void Action<T1,T2,T3>(T1 arg1,T2 arg2,T3 arg3);
* delegate void Action<T1,T2,T3,T4>(T1 arg1,T2 arg2,T3 arg3,T4 arg4);
*/
Action<string> action = SpeakEnglish;
action("KoalaStudio"); Action<string, string> action2 = SpeakTwoLanguage;
action2("KoalaStudio","Koala工作室");
Console.ReadKey();
} private void SpeakChinese(string msg)
{
Console.WriteLine("你好,我是{0}",msg);
} private static void SpeakEnglish(string msg)
{
Console.WriteLine("Hello,I'm {0}",msg);
} private static void SpeakTwoLanguage(string msg1, string msg2)
{
Console.WriteLine("你好,我是{0}",msg1);
Console.WriteLine("Hello,I'm {0}",msg2);
}
}
}
2.Func-有返回值
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace DelegateSamples
{
class Program
{
static void Main(string[] args)
{
/* Func<T,TResult>:封装一个具有参数(类型为T),返回TResult类型值的签名函数,它包括以下几种情况:
* Func<T,TResult>、Func<T1,T2,TResult>、Func<T1,T2,T3,TResult>、Func<T1,T2,T3,T4,TResult>
* 声明:
* ……略去
*/
Func<string,string/*这是返回值类型*/> func = SpeakEnglish;
func("KoalaStudio"); Func<string, string, string/*这是返回值类型*/> func2 = SpeakTwoLanguage;
func2("KoalaStudio","Koala工作室");
Console.ReadKey();
} private static string SpeakEnglish(string msg)
{
return string.Format("Hello,I'm {0}", msg);
} private static string SpeakTwoLanguage(string msg1, string msg2)
{
Console.WriteLine("你好,我是{0}",msg1);
Console.WriteLine("Hello,I'm {0}",msg2);
return string.Format("你好,我是{0};Hello,I'm {1}", msg1,msg2);
}
}
}
3.Predicate
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace DelegateSamples
{
class Program
{
static void Main(string[] args)
{
/* bool Predicate<T>:表示定义一组条件并确定指定对象是否符合这些条件的方法。
* 通常,此类委托由Array和List类的几种方法使用,用于在集合中搜索元素。
* delegate bool Predicate<T>(T obj),如果obj符合此委托表示的方法中定义的条件,则返回true,否则返回false
*/ List<string> listString = new List<string>()
{
"a","abc","koala","xyz","take"
}; //List对象的FindAll定义:public List<T> FindAll(Predicate<T> match);
//match 类型:System.Predicate<T> 委托,用于定义要搜索的元素应满足的条件。
//返回值
//类型:System.Collections.Generic.List<T>
//如果找到,则为一个 List<T>,其中包含与指定谓词所定义的条件相匹配的所有元素;否则为一个空 List<T>。
Predicate<String> match = delegate(string word)
{
if (word.Length > )
{
return true;
}
return false;
}; List<string> result = listString.FindAll(match);
}
}
}
协变与逆变
http://www.cnblogs.com/laoyu/archive/2013/01/13/2859000.html
异步委托
表达式树
事件
1.什么是事件
谈到委托,必提事件,事件本质是对委托的封装,对外提供add_EventName(对应+=)和remove_EventName(对应-=)访问,从而实现类的封装性。
2.事件能解决什么问题
将公有的委托变量定义为私有变量,从而满足类的封装性原则;
具有委托具有的作用;
3.怎么使用事件
声明委托
声明事件
事件注册方法
4.事件机制
事件的本质就是委托,向外提供两个访问方法add_EventName(对应+=)和remove-EventName(对应-=),我们通过.NET Reflector反汇编工具来查看,到底是不是这样的。
委托、事件与Observer设计模式
声明委托事件
public delegate void delegatename(int param); //声明委托
public event delegatename eventname; //声明事件
using System;
using System.Collections.Generic;
using System.Text; namespace Delegate {
// 热水器
public class Heater {
private int temperature;
public delegate void BoilHandler(int param); //声明委托
public event BoilHandler BoilEvent; //声明事件 // 烧水
public void BoilWater() {
for (int i = ; i <= ; i++) {
temperature = i; if (temperature > ) {
if (BoilEvent != null) { //如果有对象注册
BoilEvent(temperature); //调用所有注册对象的方法
}
}
}
}
} // 警报器
public class Alarm {
public void MakeAlert(int param) {
Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);
}
} // 显示器
public class Display {
public static void ShowMsg(int param) { //静态方法
Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);
}
} class Program {
static void Main() {
Heater heater = new Heater();
Alarm alarm = new Alarm(); heater.BoilEvent += alarm.MakeAlert; //注册方法
heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法
heater.BoilEvent += Display.ShowMsg; //注册静态方法 heater.BoilWater(); //烧水,会自动调用注册过对象的方法
}
}
}
输出结果
资料
http://www.tracefact.net/tech/009.html
https://www.cnblogs.com/wangjiming/p/8300103.html
.NET面试题系列(十)委托与事件的更多相关文章
- .NET面试题系列[7] - 委托与事件
委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...
- .Net之美读书系列(一):委托与事件
开启新的读书之旅,这次读的书为<.Net之美:.Net关键技术深入解析>. 我是选择性阅读的,把一些自己觉得容易忘记的,或者比较重要的知识点记录下来,以便以后能方便呢查阅. 尊重书本原作者 ...
- C# 基础知识系列- 11 委托和事件
0. 前言 事件和委托是C#中的高级特性,也是C#中很有意思的一部分.出现事件的地方,必然有委托出现:而委托则不一定会有事件出现.那为什么会出现这样的关系呢?这就需要从事件和委托的定义出发,了解其中的 ...
- .NET面试题系列[8] - 泛型
“可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用.“ - Jon Skeet .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] ...
- .NET面试题系列[0] - 写在前面
.NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] - .NET框架基础知识(2) .NET面试题系列[3] - C# 基础知识(1) .NET ...
- jQuery-1.9.1源码分析系列(十) 事件系统——事件委托
jQuery的事件绑定有几个比较优秀的特点: 1. 可以绑定不限数量的处理函数 2. 事件可以委托到祖先节点,不必一定要绑到对应的节点,这样后添加的节点也照样能被处理. 3. 链式操作 下面主要分析事 ...
- [转]大白话系列之C#委托与事件讲解(三)
本文转自:http://www.cnblogs.com/wudiwushen/archive/2010/04/21/1717378.html [我希望大家在看完文章的时候,多做做练习,自己也可以想个场 ...
- 大白话系列之C#委托与事件讲解(一)
从序言中,大家应该对委托和事件的重要性有点了解了吧,虽然说我们现在还是能模糊,但是从我的大白话系列中,我会把这些概念说的通俗易懂的.首先,我们还是先说说委托吧,从字面上理解,只要是中国人应该都知道这个 ...
- 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...
随机推荐
- ESXi虚拟机出现关机时卡住的问题处理
1. ESXi在日常使用时经常会遇到机器卡住的情况 这种情况下GUI的方式无从下手, 需要从cli的方式处理 我记得之前写过一个 但是不知道放哪里去了. 再重新写一下. 直接按照图处理 2. 然后xs ...
- pandas取值
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/24 15:03 # @Author : zhang chao # @Fi ...
- loadrunner报错
Action.c(1516): Error -27727: Step download timeout (120 seconds) has expired when downloading resou ...
- Hibernate事务以及一级缓存02
一. Hibernate中的事务 1. 事务的回顾 1.1 什么是事务(Transaction)(面试重点) 是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的 ...
- Rsync 服务器端配置
Centos 6.3 已经自带Rsync服务 安装xinetd # yum -y install xinetd 编辑/etc/xinetd.d/rsync文件,把disable = yes修改为dis ...
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
- 实现 60fps 的网易云音乐首页
网易云音乐是一款很优秀的音乐软件,我也是它的忠实用户.最近在研究如何更好的开发TableView,接着我写了一个Model驱动的小框架 - MDTable.为了去验证框架的可用性,我选择了网易云音乐的 ...
- nginx 深入篇
nginx 防盗链 上文介绍了如何以最最简单的方式配置静态资源,还存在一定的隐患,一般的盗链如何预防, 设置验证referer server { listen 8000; server_name 12 ...
- 使用debootstrap制作debian-jessie系统docker镜像
先看一下Docker官网提示:In general, you'll want to start with a working machine that is running the distribut ...
- A1052. Linked List Sorting
A linked list consists of a series of structures, which are not necessarily adjacent in memory. We a ...