委托

如果我们要把方法当做参数来传递的话,就要用到委托。简单来说委托是一个类型,这个类型可以赋值一个方法的引用。

声明委托

在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方法组成的,然后使用这个类实例化对象。在我们使用委托的时候,也需要经过这两个阶段,首先定义委托,告诉编译器我们这个委托可以指向哪些类型的方法,然后,创建该委托的实例。

定义委托的语法如下:

delegate void IntMethodInvoker(int x); 

定义了一个委托叫做IntMethodInvoker,  这个方法要带有一个int类型的参数,并且方法的返回值是void的。 定义一个委托要定义方法的参数和返回值,使用关键字delegate定义。

使用委托

private delegate string GetAString();

static void Main(){
int x = 40;
GetAString firstStringMethod = new GetAString(x.ToString);
Console.WriteLine(firstStringMethod());
}

在这里我们首先使用GetAString委托声明了一个类型叫做fristStringMethod,接下来使用new 对它进行初始化,使它引用到x中的ToString方法上,这样firstStringMethod就相当于x.ToString,我们通过firstStringMethod()执行方法就相当于x.ToString()

通过委托示例调用方法有两种方式

fristStringMethod();
firstStringMethod.Invoke();

委托的赋值

只需要把方法名给一个委托的构造方法就可以了

GetAString firstStringMethod = new GetAString(x.ToString);

也可以把方法名直接给委托的实例

GetAString firstStringMethod = x.ToString;

Action委托和Func委托

除了我们自己定义的委托之外,系统还给我们提供过来一个内置的委托类型,Action和Func

Action委托引用了一个void返回类型的方法,T表示方法参数

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _005_Action委托 {
class Program {
static void PrintString()
{
Console.WriteLine("hello world.");
} static void PrintInt(int i)
{
Console.WriteLine(i);
} static void PrintString(string str)
{
Console.WriteLine(str);
} static void PrintDoubleInt(int i1, int i2)
{
Console.WriteLine(i1+i2);
}
static void Main(string[] args)
{
//Action a = PrintString;//action是系统内置(预定义)的一个委托类型,它可以指向一个没有返回值,没有参数的方法
//Action<int> a=PrintInt;//定义了一个委托类型,这个类型可以指向一个没有返回值,有一个int参数的方法
//Action<string> a = PrintString;//定义了一个委托类型,这个类型可以指向一个没有返回值,有一个string参数的方法 在这里系统会自动寻找匹配的方法
Action<int, int> a = PrintDoubleInt;
a(34, 23);
Console.ReadKey();
//action可以后面通过泛型去指定action指向的方法的多个参数的类型 ,参数的类型跟action后面声明的委托类型是对应着的 }
}
}

Func引用了一个带有一个返回值的方法,它可以传递0或者多到16个参数类型,和一个返回类型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _006_Func委托 {
class Program {
static int Test1()
{
return 1;
} static int Test2(string str)
{
Console.WriteLine(str);
return 100;
} static int Test3(int i, int j)
{
return i + j;
}
static void Main(string[] args)
{
//Func<int> a = Test1;//func中的泛型类型制定的是 方法的返回值类型
//Console.WriteLine(a());
//Func<string, int> a = Test2;//func后面可以跟很多类型,最后一个类型是返回值类型,前面的类型是参数类型,参数类型必须跟指向的方法的参数类型按照顺序对应
Func<int, int, int> a = Test3;//func后面必须指定一个返回值类型,参数类型可以有0-16个,先写参数类型,最后一个是返回值类型
int res = a(1, 5);
Console.WriteLine(res);
Console.ReadKey();
}
}
}

多播委托

前面使用的委托都只包含一个方法的调用,但是委托也可以包含多个方法,这种委托叫做多播委托。使用多播委托就可以按照顺序调用多个方法,多播委托只能得到调用的最后一个方法的结果,一般我们把多播委托的返回类型声明为void。

Action action1 = Test1;     

action2+=Test2;    

action2-=Test1; 

多播委托包含一个逐个调用的委托集合,如果通过委托调用的其中一个方法抛出异常,整个迭代就会停止。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _008_多播委托 {
class Program {
static void Test1()
{
Console.WriteLine("test1");
//throw new Exception();
} static void Test2()
{
Console.WriteLine("test2");
}
static void Main(string[] args) {
//多播委托
Action a = Test1;
a += Test2;//表示添加一个委托的引用
//a -= Test1;
//a -= Test2;
//if(a!=null)
// a();//当一个委托没有指向任何方法的时候,调用的话会出现异常null // 取得多播委托中所有方法的委托
Delegate[] delegates = a.GetInvocationList();
foreach (Delegate de in delegates)
{
de.DynamicInvoke();
}
Console.ReadKey();
}
}
}

匿名方法

到目前为止,使用委托,都是先定义一个方法,然后把方法给委托的实例。但还有另外一种使用委托的方式,不用去定义一个方法,应该说是使用匿名方法(方法没有名字)。

Func<int,int,int> plus = delegate (int a,int b){     
int temp = a+b;     
return temp;
};
int res = plus(34,34);
Console.WriteLine(res);

在这里相当于直接把要引用的方法直接写在了后面,优点是减少了要编写的代码,减少代码的复杂性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _009_匿名方法 {
class Program {
static int Test1(int arg1, int arg2)
{
return arg1 + arg2;
}
static void Main(string[] args)
{
//Func<int, int, int> plus = Test1;
//修改成匿名方法的形式
Func<int, int, int> plus = delegate(int arg1, int arg2)
{
return arg1 + arg2;
};
//匿名方法 本质上是一个方法,只是没有名字,任何使用委托变量的地方都可以使用匿名方法赋值 Console.ReadKey();
}
}
}

Lambda表达式

从C#3.0开始,可以使用Lambda表达式代替匿名方法。只要有委托参数类型的地方就可以使用Lambda表达式。刚刚的例子可以修改为

Func<int,int,int> plus = (a,b)=>{ int temp= a+b;return temp; };
int res = plus(34,34);
Console.WriteLine(res);

Lambda运算符“=>”的左边列出了需要的参数,如果是一个参数可以直接写  a=>(参数名自己定义),如果多个参数就使用括号括起来,参数之间以,间隔。

如果Lambda表达式只有一条语句,在方法快内就不需要花括号和return语句,编译器会自动添加return语句。如果Lambda表达式的实现代码中需要多条语句,就必须添加花括号和return语句。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _010_Lambda表达式 {
class Program {
static void Main(string[] args) {
//lambda表达式用来代替匿名方法,所以一个lambda表达式也是定义了一个方法
//Func<int, int, int> plus = delegate(int arg1, int arg2) {
// return arg1 + arg2;
//};
//Func<int, int, int> plus = (arg1, arg2) =>// lambda表达式的参数是不需要声明类型的
//{
// return arg1 + arg2;
//};
//Console.WriteLine(plus(90,60)); Func<int, int> test2 = a => a+1;//lambda表示的参数只有一个的时候,可以不加上括号 当函数体的语句只有一句的时候,我们可以不加上大括号 也可以不加上return语句
Func<int, int> test3 = (a) =>
{
return a + 1;
};
Console.WriteLine(test2(34));
Console.WriteLine(test3(34));
Console.ReadKey();
}
}
}

通过Lambda表达式可以访问Lambda表达式块外部的变量。这是一个非常好的功能,但如果不能正确使用,也会非常危险。示例:

int somVal = 5;
Func<int,int> f = x=>x+somVal;
Console.WriteLine(f(3));//8
somVal = 7;
Console.WriteLine(f(3));//10

这个方法的结果,不但受到参数的控制,还受到somVal变量的控制,结果不可控,容易出现编程问题,用的时候要谨慎。


事件

事件(event)基于委托,为委托提供了一个发布/订阅机制,我们可以说事件是一种具有特殊签名的委托。

比如:我们按键点击,这就是一个事件,在其它地方接收到这个事件,触发相应。

下面使用猫和老鼠的实例来演示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _012_观察者设计模式_猫捉老鼠 {
/// <summary>
/// 猫类
/// </summary>
class Cat
{
private string name;
private string color; public Cat(string name, string color)
{
this.name = name;
this.color = color;
} /// <summary>
/// 猫进屋(猫的状态发生改变)(被观察者的状态发生改变)
/// </summary>
public void CatComing()
{
Console.WriteLine(color+"的猫"+name+"过来了,喵喵喵 ..."); if(catCome!=null)
catCome();
} public event Action catCome;//声明一个事件 发布了一个消息
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _012_观察者设计模式_猫捉老鼠 {
/// <summary>
/// 观察者类:老鼠
/// </summary>
class Mouse
{
private string name;
private string color; public Mouse(string name, string color,Cat cat)
{
this.name = name;
this.color = color;
cat.catCome += this.RunAway;//把自身的逃跑方法 注册进 猫里面 订阅消息
}
/// <summary>
/// 逃跑功能
/// </summary>
public void RunAway()
{
Console.WriteLine(color+"的老鼠"+name+"说: 老猫来, 赶紧跑, 我跑, 我使劲跑,我加速使劲跑 ...");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace _012_观察者设计模式_猫捉老鼠 {
class Program {
static void Main(string[] args) {
Cat cat = new Cat("加菲猫","黄色");
Mouse mouse1 = new Mouse("米奇","黑色",cat);
Mouse mouse2 = new Mouse("水", "红色",cat);
cat.CatComing();
//cat.catCome();//事件不能再类的外部触发,只能在类的内部触发
Console.ReadKey();
}
}
}

C#编程 委托 Lambda表达式和事件的更多相关文章

  1. C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)

    委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...

  2. C#高级编程(第9版) 第08章 委托、lambda表达式和事件 笔记

          本章代码分为以下几个主要的示例文件: 1. 简单委托 2. 冒泡排序 3. lambda表达式 4. 事件示例 5. 弱事件     引用方法 委托是寻址方法的.NET版本.在C++中函数 ...

  3. 委托、Lambda表达式、事件系列07,使用EventHandler委托

    谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...

  4. 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

  5. 委托、Lambda表达式、事件系列05,Action委托与闭包

    来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); C ...

  6. 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理

    委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链.本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理. □ 调用返回类型为 ...

  7. 委托、Lambda表达式、事件系列03,从委托到Lamda表达式

    在"委托.Lambda表达式.事件系列02,什么时候该用委托"一文中,使用委托让代码简洁了不少. namespace ConsoleApplication2 { internal ...

  8. 委托、Lambda表达式、事件系列02,什么时候该用委托

    假设要找出整型集合中小于5的数. static void Main(string[] args) { IEnumerable<int> source = new List<int&g ...

  9. 委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性

    委托是一个类. namespace ConsoleApplication1 { internal delegate void MyDelegate(int val); class Program { ...

随机推荐

  1. OkHttp3-基本用法(转)

    OkHttp 一个支持Http和Http/2,可适用于Android以及Java应用的网络请求客户端. 概述 Http是现代网络应用的所常用的协议,它是一种数据传输的媒介.执行高效的Http代码可以让 ...

  2. .net大文件上传断点续传解决方案

    HTML部分 <%@PageLanguage="C#"AutoEventWireup="true"CodeBehind="index.aspx. ...

  3. javascript类型判断最佳实践

    javascript有8种数据类型 值类型 Number Null Undefined String Symbol Boolean BigInt 引用类型 Object Array Function ...

  4. ThinkPHP系统常量

    _ROOT__ : 网站根目录地址 __APP__ : 当前项目(入口文件)地址 __URL__ : 当前模块地址 __ACTION__ : 当前操作地址 __SELF__ : 当前 URL 地址 _ ...

  5. React 的 DOM 添加多个点击事件

    第一直觉代码如下:后果是写在后面的事件函数覆盖前面的事件函数,只执行第二条(弹出 222). import React, { Component, Fragment } from 'react' ex ...

  6. JavaWeb-RESTful(三)_使用SpringMVC开发RESTful_下

    JavaWeb-RESTful(一)_RESTful初认识 传送门 JavaWeb-RESTful(二)_使用SpringMVC开发RESTful_上 传送门 JavaWeb-RESTful(三)_使 ...

  7. python中的fstring的 !r,!a,!s

    首先是fstring的结构 f ' <text> { <expression> <optional !s, !r, or !a> <optional : fo ...

  8. Backen-Development record 1

    单例模式 在应用这个模式时,单例对象的类必须保证只有一个实例存在. 服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简化了在复杂环境下的配置管理. __new__实现 用装饰器实现单例 ...

  9. Nginx事件管理之ngx_event_core_module模块

    1. 概述 ngx_event_core_module 模块是一个事件类型的模块,它在所有事件模块中的顺序是第一位.它主要完成以下两点任务: 创建连接池(包括读/写事件): 决定究竟使用哪些事件驱动机 ...

  10. Mac Vmware NAT模式

    1.NAT模式原理 2.MAC上关于Vmware的配置 1)/Library/Preferences/VMware Fusion/networking MacBookPro:~ zhangxm$ vi ...