首页 > 编程语言 >C# 委托(delegate)、泛型委托和Lambda表达式

C# 委托(delegate)、泛型委托和Lambda表达式

时间:2023-04-05 18:58:52浏览次数:45  
标签:委托 num2 C# int 实例 delegate 方法 num1

# 什么是委托

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

 

标签:委托,num2,C#,int,实例,delegate,方法,num1
From: https://www.cnblogs.com/dhcpclass/p/17290375.html

相关文章

  • CentOS更换YUM源
    前言查看当前YUM(YellowdogUpdater,Modified)配置,如果像下图一样使用国外地址,由于网络限制下载会比较慢,更换国内yum源来解决。cat/etc/yum.repos.d/CentOS-Base.repo更换为阿里云YUM源echo"备份原来的源">/dev/nullmv/etc/yum.repos.d/CentOS-Base.repo/etc/yum.rep......
  • c++ std::variant
    std::variant是c++17引入的一个类型,其作用类似于C语言中的Union,但是比Union的功能强大的多。C语言中一个联合体Union可以储存多种类型数据,但缺点有很多。比如:1没有可用的方法来判断Union中真实储存的类型,获取值时也是内存拷贝的结果,可能会存在问题。这就只能靠程序员人脑保证......
  • Docker制作一个镜像完整过程
    前言以制作CentOS镜像为例,讲述对镜像自定义,打包以及推送的远程仓库的过程,步骤都比较简单可以快速上手。创建步骤创建CentOS基础镜像创建构建目录和Dockerfile,在Dockerfile中编辑镜像相关设置,参考菜鸟教程-Dockerfile。echo"在当前用户目录下创建创建目录docker/build/cent......
  • git bash报错fatal: detected dubious ownership in repository at的解决方法
    情况在gitbash中输入"gitadd."命令时报错"fatal:detecteddubiousownershipinrepositoryat" 原因文件夹的所有者和现在的用户不一致例如:文件夹的所有者是Administrator,而当前用户是myAccount 方法1右键文件夹->属性->所有者->更改->勾选应用到所有子目......
  • 前端开发-CSS
    三种CSS写法1.在标签内书写2.在head中书写3.在外部文件书写 各种选择器常用:类选择器,标签选择器,后代选择器少用:ID选择器,属性选择器    多个样式覆盖问题:1.样式不同时一起作用2.样式相同时,取style中排序后面的3.若要强制使用,则添加important,如color:red!imp......
  • 架构浅谈之 MVC
    阅读本文大概需要6.66分钟。很多人表示对架构没有任何概念,想了解下架构,但是看了网上的一些文章又觉得云里雾里,其实架构远没有那么难,今天从这篇文章开始我来给大家谈谈架构,争取让大家都看得懂。1什么是架构?对于架构,业界从来没有一个统一的定义,架构一词最初来自建筑业,假如我们要盖......
  • CSE 3430 问题求解
    CSE3430SP23LAB1ABDESCRIPTIONThislabhastwoparts,Part1andPart2.ForPart1,wewillwritecodetodocalculationsondatainasinglestaticallyallocatedarray(asdescribedinslidesetB-5),andwewillreadinputfortheprogramfromaf......
  • c#之委托、泛型
    委托:是一种类型,可“持有”多个方法,可以看作一个方法串(eg糖葫芦)委托相当于c++中的函数指针在c#中,在类外,想要调用一个方法有两种方式第一种:通过方法名。1、实例名.方法名(实例方法)2、类名.方法名(静态方法)第二种:委托方式。委托有两种类型1、自定义委托eg:delegate返回值......
  • openGauss备份恢复之gs_probackup
    一、概述1、基础信息gs_probackup是一个用于管理openGauss数据库备份和恢复的工具。它对openGauss实例进行定期备份,以便在数据库出现故障时能够恢复服务器。可用于备份单机数据库,也可对主机或者主节点数据库备机进行备份,为物理备份。可备份外部目录的内容,如脚本文件、配置文件、日......
  • 1130 -Host 'ip' is not allowed to connect to this MySQL server
      由于mysql默认不允许其他IP地址(非虚拟机)访问可以将访问的用户(如root)的host由localhost(本机)改成%(任意,也可指定ip)最后flushprivileges刷新权限 [root@hadoop4~]#mysql-uroot-pmysql>usemysql;mysql>selecthost,userfromuser;+-----------+------+|host......