C#中的委托、匿名方法、Lambda、Action和Func

委托

委托概述

委托是存有对某个方法的引用的一种引用类型变量。定义方法的类型,可以把一个方法当作另一方法的参数。所有的委托(Delegate)都派生自 System.Delegate 类。委托声明决定了可由该委托引用的方法。

# 声明委托类型

委托类型声明与方法类似,可以理解为方法的类型,它与方法声明不同的地方:

  • 以delegate关键字开头
  • 没有方法主体
csharp 复制代码
delegate void ShowInfo();//可带参数,可不带参数

它不需要在类内部声明,因为它是类型声明,委托可指向一个与其具有相同标签的方法。

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    delegate void ShowInfo();
    public class Program
    {
        
        static void Main(string[] args)
        {
            ShowInfo showinfo = ShowInfo;//把签名赋值给委托变量
            showinfo?.Invoke();//调用委托,可指向相同标签的方法
        }
        static void ShowInfo()
        {
            Console.WriteLine("打印ShowInfo方法");
        }
    }
}

运行结果:

创建委托实例

法一

第一种是使用带new关键字进行创建,操作组成如下:

  • 委托类型名称
  • 一组圆括号,其中参数列表为成员的方法的名称,该方法可以是实例方法 也可以是静态方法
csharp 复制代码
MyDel delVar;//声明委托类型的变量
MyDel delVar= new MyDel(实例方法);//创建委托并保存引用
MyDel delVar= new MyDel(静态方法);//创建委托并保存引用

法二

可以使用快捷语法,仅由方法说明符组成,因为在方法名称和其相对应的委托类型之间存在隐式转换,可以理解为方法赋值给委托变量

csharp 复制代码
delVar = 实例方法;//创建委托并保存引用
delVar = 静态方法;//创建委托并保存引用

委托的调用

可以通过两种方式调用委托。一种是像调用方法一样调用委托 ,另外一种是使用委托的Invoke方法。

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    delegate void Show();

    public class Test
    {
        //静态方法
        public static void Print1()
        {
            Console.WriteLine("打印静态方法");
        }
        //实例方法
        public void Print2()
        {
            Console.WriteLine("打印实例方法");
        }

    }
    public class Program
    {
        
        static void Main(string[] args)
        {
            Test test = new Test();
            //1、调用方法一样调用委托
            Show show = test.Print2;//实例方法赋值给委托变量
            show();//调用委托
            Show show2 = Test.Print1;//静态方法赋值给委托变量
            show2();//调用委托


            //2、使用委托的Invoke方法
            show?.Invoke();//使用Invoke和空条件运算符
            show2?.Invoke();
        }

    }
}

委托的多播

委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除委托。

匿名方法

匿名方法提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。在匿名方法中您不需要指定返回类型,它是从方法主体内的 return 语句推断的。

匿名方法表达式的语法包含如下组成部分:

  • delegate关键字
  • 参数列表,如果语句快没有使用任何参数则可以省略
  • 语句快,它包含了匿名方法的代码
csharp 复制代码
delegate ( 参数 ) {语句块};

案例

csharp 复制代码
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    delegate void Show();
    delegate int Add(int a, int b);
    public class Program
    {
        
        static void Main(string[] args)
        {
            //不带参数
            Show show = delegate
            {
                Console.WriteLine("打印匿名方法");
            };
            show();
            //带参数
            Add add = delegate(int a, int b)
            {
                int c = a + b;
                Console.WriteLine("和为多少:" + c);
                return c;
            };
            add(1,2);
            Console.ReadKey();

        }
    }
}

运行结果:

Lambda表达式

在匿名方法中,delegate关键字有点多余,因为编译器已经指定我们在将方法赋值给委托,因此我们可以利用Lambda表达式删除delegate关键字。在参数列表和匿名方法主体之间放置Lambda运算符=>。Lambda运算符读作"goes to"。如下代码演示这种转换。

csharp 复制代码
MyDel del = delegate(int x) {return x+1};//匿名方法
MyDel del = (int x) =>{return x+1};//Lambda表达式

上面看起来简洁,但是只省略6个字符,然后,编译器可以推断更多的信息,因此我们可以进一步简化。

  • 编译器可以从委托声明指定委托参数的类型,因此Lambda表达式可以省略参数的类型。如del1
    • 如果只有一个隐式类型参数还可以省略圆括号。如del2
  • Lambda表达式运行表达式的主体是语句块或表达式,如果语句块包含了返回语句,则可以省略return关键字。如del3
csharp 复制代码
MyDel del = delegate(int x) {return x+1};//匿名方法
MyDel del1 = (x) =>{return x+1};//Lambda表达式
MyDel del2 = x =>{return x+1};//Lambda表达式
MyDel del3 = x =>x+1;//Lambda表达式

Action 和Func

Action和Func都是.net内置的委托,可以使用他们以参数形式传递方法。他们都支持0-16个参数,然后Action没有返回值类型,Func有返回值类型,Func最后一个参数为返回值类型。

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{

    public class Program
    {
        
        static void Main(string[] args)
        {
            #region
            {
                //不带参数
                Action act1 = () => { Console.WriteLine("打印"); };
                act1();
                //带1个参数
                Action<int> act4 = a => { Console.WriteLine("a="+a); };//这里不能省略{}
                act4(2);


                //不带参数,带返回值
                Func<int> func1 = () => { return 1; };
                func1();

                //带1个参数,带带返回值
                Func<int, int> func2 = a => 
                {   
                    int b = a + 1;
                    Console.WriteLine("b=" + b);
                    return b;
                };
                func2(2);
            }
            #endregion
            Console.ReadKey();

        }

    }
}
相关推荐
尘浮生2 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
hopetomorrow16 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
小牛itbull26 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
广煜永不挂科34 分钟前
Devexpress.Dashboard的调用二义性
c#·express
请叫我欧皇i34 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落37 分钟前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
GIS瞧葩菜1 小时前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
chnming19871 小时前
STL关联式容器之set
开发语言·c++
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
GIS 数据栈1 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis