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();

        }

    }
}
相关推荐
嵌入式@秋刀鱼7 分钟前
《 第三章-招式初成》 C++修炼生涯笔记(基础篇)程序流程结构
linux·开发语言·数据结构·c++·笔记·visual studio code
shenyan~16 分钟前
关于 WASM: WASM + JS 混合逆向流程
开发语言·javascript·wasm
梦境虽美,却不长36 分钟前
C语言 学习 文件操作(开关,读写,定位,大小)操作 2025年6月8日12:19:24
c语言·开发语言·学习
Charlotte_jc44 分钟前
完美解决openpyxl保存Excel丢失图像/形状资源的技术方案
开发语言·python·excel·openpyxl
西北大程序猿1 小时前
服务器代码知识点补充
服务器·开发语言·网络·c++·网络协议
新知图书3 小时前
R语言ICU患者死亡率预测实战
开发语言·r语言
蒟蒻小袁3 小时前
力扣面试150题--实现Trie(前缀树)
leetcode·面试·c#
wennieFan3 小时前
python基础面试练习题
开发语言·python
阿福不是狗3 小时前
Python使用总结之Linux部署python3环境
linux·开发语言·python
枣伊吕波3 小时前
第十三节:第七部分:Stream流的中间方法、Stream流的终结方法
java·开发语言