当前位置: 首页 > article >正文

C# 匿名函数

目录

    • 1、什么是匿名函数?
      • 1.1 一般函数的定义
      • 1.2 匿名函数的定义
    • 2、理解匿名函数的例子
      • 2.1 带输入参数的匿名函数
      • 2.2 带返回值的匿名函数
      • 2.3 匿名函数可以使用函数外部定义的变量
      • 2.4 把匿名函数作为其他函数的输入参数
      • 2.5 匿名函数作事件处理器
    • 3、为什么要使用匿名函数?
      • 3.1 不使用匿名函数
      • 3.2 使用匿名函数
    • 4、匿名函数的局限性
      • 4.1 匿名函数中不能包含跳转语句
      • 4.2 匿名函数不能使用外部函数的ref或out参数

1、什么是匿名函数?

匿名函数(anonymous method),顾名思义,就是没有名字的函数。或者没有名字的代码块。在C#中,我们使用delegate关键字来定义匿名函数,并且匿名函数可以赋值给委托类型的变量。委托参见C# 委托。

匿名函数的形式。

delegate(输入参数){函数体};

1.1 一般函数的定义

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Action action = WriteBlog;
            action.Invoke();
            Console.ReadKey();
        }

        static void WriteBlog()
        {
            Console.WriteLine("I'm writing a blog.");
        }
    }
}

一般函数有函数名,在这个例子中函数名为WriteBlog。我们将有名字的函数绑定到委托类型的变量上。

1.2 匿名函数的定义

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Action action = delegate() { Console.WriteLine("I'm writing a blog."); };
            action.Invoke();
            Console.ReadKey();
        }
    }
}

匿名函数没有函数名。我们将没有名字的函数(匿名函数)绑定到委托类型的变量上。

通过这个例子的对比,我们可以感性地看出,匿名函数使代码量减少了。

2、理解匿名函数的例子

2.1 带输入参数的匿名函数

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Action<string> action = delegate(string name) { Console.WriteLine("{0} is writing a blog.", name); };
            action.Invoke("Mike");
            Console.ReadKey();
        }
    }
}

2.2 带返回值的匿名函数

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<double,double,double> func1 = delegate(double a,double b) { return a + b; };
            double result = func1.Invoke(100,200);
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
}

2.3 匿名函数可以使用函数外部定义的变量

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string name = "Mike";
            Action action = delegate () { Console.WriteLine("{0} is writing a bolg.",name); };
            action.Invoke();
            Console.ReadKey();
        }
    }
}

当然,匿名函数也可以调用外部函数。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Action action = delegate ()
            {
                bool b = Test();
                Console.WriteLine(b);
            };
            action.Invoke();
            Console.ReadKey();
        }

        static bool Test()
        {
            return true;
        }
    }
}

2.4 把匿名函数作为其他函数的输入参数

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Predicate<int> isEven = delegate (int num) { return num % 2 == 0; };
            int[] nums = { 1, 2, 3, 4, 5 };
            ProcessNums(isEven, nums);
            Console.ReadKey();
        }

        static void ProcessNums(Predicate<int> predicate,int[] nums)
        {
            Console.WriteLine("Processing nums...");
            foreach (int num in nums)
            {
                bool b = predicate.Invoke(num);
                if(b)
                {
                    Console.WriteLine("{0} is even number.",num);
                }
                else
                {
                    Console.WriteLine("{0} is not even number.", num);
                }
            }
        }
    }
}

/*
Outputs:
Processing nums...
1 is not even number.
2 is even number.
3 is not even number.
4 is even number.
5 is not even number.
*/

值得一提的是,Predicate类型的委托用来封装有一个输入参数并且返回值为bool类型的方法。在这个例子中,Predicate委托封装的方法输入一个int类型的数字,方法判断数字是否为偶数,然后返回判断的结果。
我们将匿名函数作为ProcessNums的输入参数,在ProcessNums函数体中对匿名函数进行调用。

2.5 匿名函数作事件处理器

事件参见C# 事件。简单回顾一下,事件模型由5部分组成:事件的拥有者、事件、事件的响应者、事件处理器、事件订阅。下面分别使用代码演示不适用匿名函数定义事件,以及使用匿名函数定义事件。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Chicken chicken = new Chicken();
            People zuDi = new People();
            chicken.Crow += zuDi.Action;
            chicken.StartCrow();
            Console.ReadLine();
        }
    }

    class Chicken
    {
        public event EventHandler Crow;

        public void StartCrow()
        {
            Console.WriteLine("koke-kokko");
            OnCrow(EventArgs.Empty);
        }

        protected virtual void OnCrow(EventArgs e)
        {
            Crow?.Invoke(this, e);
        }
    }

    class People
    {
        public void Action(object sender, EventArgs e)
        {
            Console.WriteLine("I get up.");
            Console.WriteLine("I started practicing my sword.");
        }
    }
}

/*
Outputs:
koke-kokko
I get up.
I started practicing my sword.
*/

这是不使用匿名函数定义的事件,事件处理器作为People的成员方法Action。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Chicken chicken = new Chicken();
            chicken.Crow += delegate (object sender, EventArgs e)
            {
                Console.WriteLine("I get up.");
                Console.WriteLine("I started practicing my sword.");
            };
            chicken.StartCrow();
            Console.ReadLine();
        }
    }

    class Chicken
    {
        public event EventHandler Crow;

        public void StartCrow()
        {
            Console.WriteLine("koke-kokko");
            OnCrow(EventArgs.Empty);
        }

        protected virtual void OnCrow(EventArgs e)
        {
            Crow?.Invoke(this, e);
        }
    }
}

/*
Outputs:
koke-kokko
I get up.
I started practicing my sword.
*/

这是使用匿名函数定义的事件,我们不需要定义一个People类,再在类里面定义Action方法,也不需要在Main函数中创建一个People类的实例,然后将实例的Action方法绑定到委托。直接一个匿名函数就将上面说的一大串操作完成了。

3、为什么要使用匿名函数?

使用匿名函数可以减少代码量,一般地,对于那种我们只使用一次的函数,且函数体的代码量比较少的情况,推荐使用匿名函数。
下面再举一个稍微复杂一点的例子,来看一看匿名函数的便利性。
例子来源。

3.1 不使用匿名函数

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Predicate<Employee> employeePredicate = new Predicate<Employee>(IsEmployeeExist);
            List<Employee> listEmployees = new List<Employee>()
            {
                new Employee{ ID = 101, Name = "Pranaya", Gender = "Male", Salary = 100000},
                new Employee{ ID = 102, Name = "Priyanka", Gender = "Female", Salary = 200000},
                new Employee{ ID = 103, Name = "Anurag", Gender = "Male", Salary = 300000},
                new Employee{ ID = 104, Name = "Preety", Gender = "Female", Salary = 400000},
                new Employee{ ID = 104, Name = "Sambit", Gender = "Male", Salary = 500000},
            };
            Employee employee = listEmployees.Find(x => employeePredicate(x));
            Console.WriteLine(@"ID : {0}, Name : {1}, Gender : {2}, Salary : {3}",
                employee.ID, employee.Name, employee.Gender, employee.Salary);
            Console.ReadKey();
        }

        public static bool IsEmployeeExist(Employee emp)
        {
            return emp.ID == 103;
        }
    }

    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Gender { get; set; }
        public double Salary { get; set; }
    }
}

/*
Outputs:
ID : 103, Name : Anurag, Gender : Male, Salary : 300000
*/

上面的代码可以分为以下几步:

  • 1.首先创建一个Employee类,类中有4个成员属性,用来表示雇员的状态。
  • 2.创建一个函数IsEmployeeExist,函数的输入参数为Employee类型的参数,函数的返回值为bool。函数的功能是,根据Employee的ID值判断Employee是否存在。
  • 3.Predicate<Employee> employeePredicate = new Predicate<Employee>(IsEmployeeExist);创建一个Predicate委托类型的实例,Predicate委托相匹配的函数签名为:函数的输入参数类型为Employee,函数的返回值类型为bool。因此IsEmployeeExist函数签名和委托签名相匹配。将IsEmployeeExist函数名作为参数传递给Predicate的构造函数。这样我们就将函数绑定到委托了,可以通过调用委托实例来间接调用函数。
  • 4.创建一个列表,列表中存储的数据类型为Employee,列表中一共存储了5个Employee类型的实例,也就是说,列表中存储了5个雇员的信息。因此我们可以将这个列表看成是雇员信息系统。
  • 5.Employee employee = listEmployees.Find(x => employeePredicate(x));调用listEmployees的Find方法,该方法接收一个Predicate类型的实例。这行代码实现的功能是,我们将listEmployees中的每一个雇员实例取出来,然后将每一个雇员作为参数传递给employeePredicate委托,也就是对于每一个雇员,委托类型的实例employeePredicate都会被调用,在内部,将调用IsEmployeeExist函数,函数接收listEmployees中的每一个雇员实例,判断雇员的ID是否等于103,如果等于,就将其存储到employee变量中,如果不等于,不存储。

3.2 使用匿名函数

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Employee> listEmployees = new List<Employee>()
            {
                new Employee{ ID = 101, Name = "Pranaya", Gender = "Male", Salary = 100000},
                new Employee{ ID = 102, Name = "Priyanka", Gender = "Female", Salary = 200000},
                new Employee{ ID = 103, Name = "Anurag", Gender = "Male", Salary = 300000},
                new Employee{ ID = 104, Name = "Preety", Gender = "Female", Salary = 400000},
                new Employee{ ID = 105, Name = "Sambit", Gender = "Male", Salary = 500000},
            };
            Employee employee = listEmployees.Find(delegate (Employee x) { return x.ID == 103; });
            Console.WriteLine(@"ID : {0}, Name : {1}, Gender : {2}, Salary : {3}",
                employee.ID, employee.Name, employee.Gender, employee.Salary);
            Console.ReadKey();
        }
    }

    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Gender { get; set; }
        public double Salary { get; set; }
    }
}

/*
Outputs:
ID : 103, Name : Anurag, Gender : Male, Salary : 300000
*/

通过使用匿名函数,我们可以省略掉2、3步。

这样,我们就可以很明显地看出来匿名函数的优势了。

4、匿名函数的局限性

4.1 匿名函数中不能包含跳转语句

匿名函数中不能包含跳转语句,如goto、break、continue。

在这里插入图片描述

4.2 匿名函数不能使用外部函数的ref或out参数

在这里插入图片描述

我们可以使用局部变量来解决这种问题。

在这里插入图片描述


http://www.kler.cn/a/515355.html

相关文章:

  • 从对等通信到万维网:通信模型变迁与拥塞求解
  • Spring MVC和Spring WebFlux的区别
  • 等变即插即用图像重建
  • 【FISCO BCOS】二十四、通过Java SDK对FISCO BCOS进行压力测试
  • Spring WebFlux 和 Spring MVC 的主要区别是什么?
  • 【云网】云网络基础概念(华为云)
  • 计算机网络 (56)交互式音频/视频
  • C语言初阶牛客网刷题——HJ73 计算日期到天数转换【难度:简单】
  • 文献精汇|121 模型:用于高收益交易的 LSTM 驱动的协整策略
  • 读写和解析简单的 nc 文件
  • flutter入门系列教程<2>:Http请求库-dio的使用
  • 二叉树的递归遍历力扣--145,144,94
  • 【深度学习】嘿马深度学习笔记第11篇:卷积神经网络,学习目标【附代码文档】
  • WPF自定义布局--瀑布布局
  • Kafka后台启动命令
  • 详细介绍:Kubernetes(K8s)的技术架构(核心概念、调度和资源管理、安全性、持续集成与持续部署、网络和服务发现)
  • wx036基于springboot+vue+uniapp的校园快递平台小程序
  • django admin list_display显示外键字段处理办法
  • 频繁刷新网页会对服务器造成哪些影响?
  • 如何轻松实现域名指向服务器
  • 代码统计工具cloc
  • 第五篇 vue3 ref 与 reactive 对比
  • 如何在 Flask 中实现用户认证?
  • 如何使用 Flask-Caching 提高性能?
  • 标签编码和独热编码对线性模型和树模型的影响
  • Android系统开发(十九):无缝拉伸的艺术——9-Patch 可绘制对象详解