# 什么是委托

1、从数据结构来讲,委托是和类一样是一种用户自定义类型。

2、委托是方法的抽象,它存储的就是一系列具有相同参数和返回类型的方法的地址。调用委托的时候,委托包含的所有方法将被执行。

# 委托声明、实例化和调用

1、声明

委托是一种特殊的类,因此委托的声明与类的声明方法类似,在任何可以声明类的地方都可以声明委托。委托声明用delegate关键字,同时委托要指明方法参数和返回值,写法与方法类似。综合类的声明和方法的声明,委托声明写成如下形式:

[访问修饰符] delegate 返回值类型 委托名 (形参列表);

public delegate void MyDel();//定义了一个委托MyDel,它可以注册返回void类型且没有参数的函数
public delegate void MyDel1(string str);//定义了一个委托MyDel1,它可以注册返回void类型且有一个string作为参数的函数
public delegate int MyDel2(int a,int b);//定义了一个委托MyDel2,它可以注册返回int类型且有两个int作为参数的函数

2、委托的实例化

与普通类的使用方法相同,声明了委托之后,我们必须给委托传递一个具体的方法,才能在运行时调用委托实例。委托实例包含了被传递给它的方法的信息,在运行时,调用委托实例就相当于执行它当中的方法。

委托实例化格式如下:

委托类名 委托实例名 = new 委托类名(Target) ;

其中,委托实例名是自定义的名称,Target是要传入的方法的名称。注意,Target是方法的引用,不能带()。带()的话是该方法的调用。区分引用和调用。

委托的实例化还有一种简单的方法:

委托类名 委托实例名 = Target;

在需要委托实例的地方直接传入Target引用即可,C#编译器会自动根据委托类型进行验证,这称为“委托推断”。

MyDel2 testDel=new MyDel2(Add);
MyDel2 testDel1 = Add;

3、委托实例的调用

委托实例等价于它当中实际方法,因此可以使用反射的Invoke()方法调用委托实例,也可以直接在委托实例后加上()进行调用。

int num = testDel(1,2);
int num1 = testDel.Invoke(1, 2);

4、委托完整的简单示例

namespace delegateTest
{
public delegate int MyCalculator(int num1, int num2);
class Program
{
static void Main(string[] args)
{
MyCalculator myCal=new MyCalculator(Add);
int addNum= myCal(1,2); MyCalculator myCal1 = Sub;
int subNum = myCal1.Invoke(1, 2); Console.WriteLine("addNum:{0},subNum:{1}", addNum, subNum); int calNum = Calculate(1, 2, Add);
Console.WriteLine("calNum:{0}", calNum);
} static int Add(int num1, int num2)
{
Console.WriteLine("num1 + num2={0}",num1 + num2);
return num1 + num2;
}
static int Sub(int num1, int num2)
{
Console.WriteLine("num1 - num2={0}", num1 - num2);
return num1 - num2;
}
static int Calculate(int num1,int num2,MyCalculator calDel)
{
return calDel(num1,num2);
}
}
}

控制台打印结果:

num1 + num2=3
num1 - num2=-1
addNum:3,subNum:-1
num1 + num2=3
calNum:3

#泛型委托

我们每次要使用一个委托时,都需要先声明这个委托类,规定参数和返回值类型,然后才能实例化、调用。为了简化这个过程, .NET 框架为我们封装了三个泛型委托类,因此大部分情况下我们不必再声明委托,可以拿来直接实例化使用,方便了我们的日常写代码。

这三种泛型委托包括:Func委托、Action委托和Predicate委托。

1、Func委托

Func委托代表着拥有返回值的泛型委托。Func有一系列的重载,形式如 Func<T1,T2, ... TResult>,其中TResult代表委托的返回值类型,其余均是参数类型。只有一个T时,即Func,代表该委托是无参数的。.NET封装了最多16个输入参数的Funct<>委托。

需要特别注意的是,若方法没有返回值,即返回 void ,由于 void 不是数据类型,因此不能定义Func委托。返回 void 的泛型委托见下文的Action。

Func的使用方法与一般的委托相同。例如上面的案例可改写如下:

namespace delegateTest
{
class Program
{
static void Main(string[] args)
{
int calNum = Calculate(1, 2, Sub);
Console.WriteLine("calNum:{0}", calNum);// -1
}
static int Calculate(int num1, int num2, Func<int, int, int> calDel)
{
return calDel(num1,num2);
}
static int Sub(int num1, int num2)
{
Console.WriteLine("num1 - num2={0}", num1 - num2);
return num1 - num2;
}
}
}

2、Action委托

Action委托代表返回值为空 void 的委托,它也有一些列重载,最多拥有16个输入参数。用法与Func相同。

namespace delegateTest
{
class Program
{
static void Main(string[] args)
{
DoSome("hello",Say);// hello
}
static void DoSome(string str,Action<string> doAction)
{
doAction(str);
}
static void Say(string str)
{
Console.WriteLine(str);
}
}
}

3、Predicate委托

这个一般用的较少,它封装返回值为bool类型的委托,可被Func代替。

#匿名委托

采用匿名方法实例化的委托称为匿名委托。

每次实例化一个委托时,都需要事先定义一个委托所要调用的方法。为了简化这个流程,C# 2.0开始提供匿名方法来实例化委托。这样,我们在实例化委托时就可以 “随用随写” 它的实例方法。

使用的格式是:

委托类名 委托实例名 = delegate (args) { 方法体代码 } ;

这样就可以直接把方法写在实例化代码中,不必在另一个地方定义方法。当然,匿名委托不适合需要采用多个方法的委托的定义。

使用匿名方法,以上代码可改写为:

MyCalculator myCal2 = delegate(int num1, int num2)
{
//打印匿名方法的名字
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); // <Main>b__0
return num1 + num2;
};
int num11= myCal2(1,2);//3

需要说明的是,匿名方法并不是真的“没有名字”的,而是编译器为我们自动取一个名字。

#Lambda表达式

纵然匿名方法使用很方便,可惜她很快就成了过气网红,没能领多长时间的风骚。如今已经很少见到了,因为delegate关键字限制了她用途的扩展。自从C# 3.0开始,她就被Lambda表达式取代,而且Lambda表达式用起来更简单。Lambda表达式本质上是改进的匿名方法。

1、表达式Lambda

当匿名函数只有一行代码时,可采用这种形式。例如:

MyCalculator myCal = (num1, num2) =>  num1 + num2;
int num = myCal(1, 2);// 3

其中=>符号代表Lambda表达式,它的左侧是参数,右侧是要返回或执行的语句。参数要放在圆括号中,若只有一个参数,为了方便起见可省略圆括号。有多个参数或者没有参数时,不可省略圆括号。

相比匿名函数,在表达式Lambda中,方法体的花括号{}和return关键字被省略掉了。

2、语句Lambda

当匿名函数有多行代码时,只能采用语句Lambda。

MyCalculator myCal = (int num1, int num2)=>
{
Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);
return num1 + num2;
};
int num = myCal(1, 2);// 3

语句Lambda不可以省略{}和return语句。

3、Lambda的主要用处

实际中用到Lambda表达式的地方大都是委托,例如linq的对集合类的扩展查询方法;

很多架构的搭建需要调用自定义方法,也离不开委托;

事件机制是基于委托的;

等等。

#多播委托

实例化委托时必须将一个匹配函数注册到委托上来实例化一个委托对象,但是一个实例化委托不仅可以注册一个函数还可以注册多个函数,注册多个函数后,在执行委托的时候会根据注册函数的注册先后顺序依次执行每一个注册函数。

函数注册委托的原型:

<委托类型> <实例化名> +=new <委托类型> (<注册函数>);

注意:委托必须先实例化以后,才能使用+=注册其他方法。如果对注册了函数的委托实例从新使用=号赋值,相当于是重新实例化了委托,之前在上面注册的函数和委托实例之间也不再产生任何关系。 有+=注册函数到委托,也有-=解除注册;

<委托类型> <实例化名> -=new <委托类型> (<注册函数>);

注意:如果在委托注册了多个函数后,如果委托有返回值,那么调用委托时,返回的将是最后一个注册函数的返回值。

MyCalculator multiCal=new MyCalculator(Add);
multiCal += Sub;
int num1 = multiCal(1, 2); // -1
multiCal -= Sub;
int num2 = multiCal(1, 2); // 3

#参考:

https://blog.csdn.net/wnvalentin/article/details/81840339


C# 委托(delegate)、泛型委托和Lambda表达式的更多相关文章

  1. 委托delegate 泛型委托action<> 返回值泛型委托Func<> 匿名方法 lambda表达式 的理解

    1.使用简单委托 namespace 简单委托 { class Program { //委托方法签名 delegate void MyBookDel(int a); //定义委托 static MyB ...

  2. .NET Framework System.Array.Sort 数组类,加深对 IComparer、IComparable 以及泛型委托、匿名方法、Lambda 表达式的理解

    本文内容 自定义类 Array.Sort 参考资料 System.Array.Sort 有很多对集合的操作,比如排序,查找,克隆等等,你可以利用这个类加深对 IComparer.IComparable ...

  3. C# 委托 (一)—— 委托、 泛型委托与Lambda表达式

    C# 委托 (一)—— 委托. 泛型委托与Lambda表达式 2018年08月19日 20:46:47 wnvalentin 阅读数 2992   版权声明:此文乃博主之原创.鄙人才疏,望大侠斧正.此 ...

  4. 3 委托、匿名函数、lambda表达式

    委托.匿名函数.lambda表达式 在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法.C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方 ...

  5. C#中分别对委托、匿名方法、Lambda表达式、Lambda表达式树以及反射执行同一方法的过程进行比较。

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. 委托、匿名方法、Lambda表达式的演进

    摘自:"http://www.cnblogs.com/eagle1986/archive/2012/01/19/2327358.html 假设给我们一个泛型对象List<T>,T ...

  7. 通过Func 委托理解委托和匿名方法及Lambda 表达式

    Func<T, TResult> 委托 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法. 命名空间: System 程序集: mscorlib(在 mscorlib.d ...

  8. 转 拉姆达表达式,委托、匿名方法、Lambda表达式的演进

    总结:Lambda表达式的语法:(参数列表=>执行语句) 无参数格式 :()=>{执行语句} 有参数格式:x=> x % 2 == 0 1.假设给我们一个泛型对象List<T& ...

  9. C#中委托、匿名函数、Lambda表达式的一些个人理解

    0x01定义一个委托,相当于定义一个可以存储方法的特殊变量类型 下面我们看具体的代码,通过代码更好理解 delegate void IntMethodInvoker(int x); 这行代码就是声明一 ...

  10. 委托,匿名函数和lambda表达式

    很早之前就接触到了委托,但是一直对他用的不是太多,主要是本人是菜鸟,能写的比较高级的代码确实不多,但是最近在看MSDN微软的类库的时候,发现了微软的类库好多都用到了委托,于是决定好好的研究研究,加深一 ...

随机推荐

  1. http-get调用接口简单代码

    一.简单便捷的httpget调用接口,并且返回接口数据1.导入相应的jar包: 2.代码如下: HttpGet get=null; try {HttpClient httpClient = new D ...

  2. 小白也能看懂的 Laravel 核心概念讲解

    自动依赖注入 什么是依赖注入,用大白话将通过类型提示的方式向函数传递参数. 实例 1 首先,定义一个类: /routes/web.php class Bar {} 假如我们在其他地方要使用到 Bar  ...

  3. selenium3+python3自动化测试学习之模拟事件处理

    自动化测试实战之ActionChains模拟用户行为 需要模拟鼠标操作才能进行的情况,比如单击.双击.点击鼠标右键.拖拽 解决:selenium提供了一个类来处理这类事件 selenium.webdr ...

  4. ES6_07_Symbol属性

    Symbol属性: 前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境 Symbol: 概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, ...

  5. JAVA UUID 生成唯一密钥(可随机选择长度)

    /**     * 获得指定数目的UUID      * @param number int 需要获得的UUID数量      * @return String[] UUID数组      */    ...

  6. hihoCoder 1308:搜索二·骑士问题(BFS预处理)

    题目链接 题意 中文题意. 思路 对于每一个骑士,可以先预处理出到达地图上某个点的需要走的步数,然后最后暴力枚举地图上每一个点,让三个骑士走过的距离之和最小即可. #include <bits/ ...

  7. SpringBoot 的过滤器

    在Springboot里面读封装的一些常用的API,当然对过滤器也不类外了. 首先讲下Spring中的AOP的理解: AOP不是一种具体的技术,而是一种编程思想.在面向对象编程的过程中,我们很容易通过 ...

  8. Shiro中@RequiresAuthentication等等注解介绍

    使用前请先开启Shiro的controller层注解,如果已经设置请下滑绕过 要在spring-mvc.xml中写. <!--下面的用于开启shiro的权限注解--> <bean c ...

  9. 如何取得Spring管理的bean

    本文主要讲3中实现方式,请用第3种方法(通用) 1.servlet方式加载时配置如下 <servlet> <servlet-name>springMVC</servlet ...

  10. Java学习笔记之---内部类

    Java学习笔记之---内部类 (一)成员内部类 内部类在外部使用时,无法直接实例化,需要借助外部类信息才能实例化 内部类的访问修饰符可以任意,但是访问范围会受到影响 内部类可以直接访问外部类的成员, ...