匿名方法和Lambda表达式

Posted xiaowie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了匿名方法和Lambda表达式相关的知识,希望对你有一定的参考价值。

来自MSDN上的说法是这样的

在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。 C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式。 有一种情况下,匿名方法提供了 Lambda 表达式中所没有的功能。 您可使用匿名方法来忽略参数列表。 这意味着匿名方法可转换为具有各种签名的委托。 这对于 Lambda 表达式来说是不可能的。 有关 lambda 表达式的更多特定信息,请参见 Lambda 表达式(C# 编程指南)。

根据MSDN上的说法,我们知道先有委托,再有匿名方法,最后到Lanbda表达式。因此,在讲解C#匿名方法之前,我们有必要说说C#委托这个东东。

 

委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。通俗的说,委托是一个可以引用方法的对象,当创建一个委托,也就创建一个引用方法的对象,进而就可以调用那个方法,即委托可以调用它所指的方法。

如何使用委托?
1、定义委托类型

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

2、声明委托对象

委托名 委托实例名;

3、创建委托对象(确定与哪些方法进行绑定)

委托实例名=new 委托名(某个类的方法)

4、使用委托调用方法

委托实例名(实参)

委托注意事项:
1、委托和方法必须具有相同的参数。

2、委托可以调用多个方法,即一个委托对象可以维护一个可调用方法的列表而不是单独的一个方法,称为多路广播(多播)。

3、使用+=和-=运算实现方法的增加和减少

下面我们通过一个小例子来讲解下委托,其代码如下(本事例通过计算器(+-*/)讲解简单的委托):

 

delegate int calculator(int x, int y); //委托类型
        static void Main(string[] args)
        {
            //创建委托对象(确定与哪些方法进行绑定),委托实例名=new 委托名(某个类的方法,本例与加法向绑定
            calculator MYAdd = new calculator(Adding);
            //创建委托对象(确定与哪些方法进行绑定),委托实例名=new 委托名(某个类的方法,本例与减法向绑定
            calculator MYMove = new calculator(Moveing);
            //创建委托对象(确定与哪些方法进行绑定),委托实例名=new 委托名(某个类的方法,本例与乘法向绑定
            calculator MYMultiply = new calculator(Multiply);
            //创建委托对象(确定与哪些方法进行绑定),委托实例名=new 委托名(某个类的方法,本例与除法向绑定
            calculator MYDivide = new calculator(Divide);
            //通过委托执行方法
            int A = MYAdd(4, 4);//8
            int B = MYMove(4, 4);//0
            int C = MYMultiply(4, 4);//16
            int D = MYDivide(4, 4);//1
        }

        /// <summary>
        /// 加法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static int Adding(int x,int y)
        {
            return x + y;
        }
        /// <summary>
        /// 减法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static int Moveing(int x, int y)
        {
            return x - y;
        }
        /// <summary>
        /// 乘法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static int Multiply(int x, int y)
        {
            return x * y;
        }
        /// <summary>
        /// 除法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static int Divide(int x, int y)
        {
            return x / y;
        }

根据上述的事例,大家应该了解简单委托的使用了。

那么,我们更近一步,来说说泛型委托。泛型应该是大家比较熟悉的一种数据类型,泛型委托又是如何和泛型向结合的呢?

再讲泛型委托之前,先说说微软引入泛型的目的及使用泛型的好处。

  •                  为什么要有泛型
  •       假设你是一个方法的设计者,
  •       这个方法有一个传入参数,有一个返回值。
  •       但你并不知道这个参数和返回值是什么类型的,
  •       如果没有泛型,你可能把参数和返回值的类型都设定为Object了
  •       那时,你心里肯定在想:反正一切都是对象,一切的基类都是Object
  •       没错!你是对的!
  •       这个方法的消费者,会把他的对象传进来(有可能会做一次装箱操作)
  •       并且得到一个Object的返回值,他再把这个返回值强制类型转化为他需要的类型
  •       那么这个过程涉及到的装箱拆箱会损耗系统的性能。
  •       那么我们如何把损耗性能避免掉呢?
  •       有泛型之后就可以了!

微软已经为我们事先定义好了三个泛型委托(Predicate,Action,Func),下面我们探讨下这三个委托的不同。

1、Predicate 泛型委托定义如下:

public delegate bool Predicate<in T>(T obj);
这个委托表示的方法需要传入一个T类型的参数,并且需要返回一个bool类型的返回值
代码实例如下:
static void Main(string[] args)
        {
            //Predicate 泛型委托代表一类具有 一个T类型(通用类型)作为参数并返回BOOL类型的方法
            var B = new Predicate<int>(isbol);
            bool BB=isbol(1);//true
        }

       /// <summary>
        /// Predicate 泛型委托
       /// </summary>
       /// <typeparam name="?"></typeparam>
       /// <param name="x"></param>
       /// <returns></returns>
        public static bool isbol(int x)
        {
            return x > 0;
        }

2、Action委托定义如下:

   public delegate void Action<T>(T obj,T obj2,...,obj16);      --最多16个参数

 他代表了一类方法,可以有0个到16个输入参数,输入参数的类型是不确定的。此类方法不能有返回值,也就是返回VOID类型的方法。

   代码实例如下:

static void Main(string[] args)
        {
            //Action 他代表了一类方法,可以有0个到16个输入参数,输入参数的类型是不确定的。此类方法不能有返回值,也就是返回VOID类型的方法。
            var B = new Action<int,string,string>(Action_d);
            B(-1,"true","flase");
            Console.ReadKey();
        }

       /// <summary>
        /// Action 泛型委托
       /// </summary>
       /// <typeparam name="?"></typeparam>
       /// <param name="x"></param>
       /// <returns></returns>
        public static void Action_d(int x,string y,string z)
        {
            if (x > 0)
            {
                Console.WriteLine(y);
            }
            else
            {
                Console.WriteLine(z);
            }
        }

3、Func泛型委托 定义如下:

    public delegate T Func<T>(T obj,T obj2,...,obj16);   --最多16个参数

   为了弥补Action泛型委托,不能返回值的不足,.net提供了Func泛型委托,相同的是它也是最多0到16个输入参数,参数类型由使用者确定,不同的是它规定要有一个返回值,返回值的类型也由使用者确定,说白了,就是ACtion委托没有返回值,而FUNC委托具有了返回值,他们都是最多16个参数。

    代码实例如下:

static void Main(string[] args)
        {
            //Func 他代表了一类方法,可以有0个到16个输入参数,输入参数的类型是不确定的。此类方法有返回值。
            var B = new Func<int, string,string,string>(Func_d);//注意:<>中最后一个string代表输出类型 
            Console.WriteLine(B(1,"True","False"));
            Console.ReadKey();
        }

       /// <summary>
        /// Predicate 泛型委托
       /// </summary>
       /// <typeparam name="?"></typeparam>
       /// <param name="x"></param>
       /// <returns></returns>
        public static string Func_d(int x,string y,string z)
        {
            if (x > 0)
            {
                return y;
            }
            else
            {
                return z;
            }
        }

学会了C#委托,那么C#匿名方法就显得很简单了!下面和小伙伴们探讨下C#匿名方法的使用!

还记得本篇上述的计算器吧!就是那个加减乘除委托。我们知道,我们在定义好委托后,还需要在定义加减乘数方法,C#引入匿名方法后,我们就不需要在单独写这些方法了,我们只需在匿名方法体内实现我们的业务逻辑即可!

delegate int calculator(int x, int y); //委托类型
        static void Main(string[] args)
        {
            //创建委托对象(确定与哪些方法进行绑定),委托实例名=new 委托名(某个类的方法,本例与加法向绑定
            calculator Adding =delegate( int x, int y)
            {
               return x+y;
            };

            calculator Moveing = delegate(int x, int y)
            {
                return x - y;
            };

            calculator Multiply = delegate(int x, int y)
            {
                return x * y;
            };

            calculator Divide = delegate(int x, int y)
            {
                return x / y;
            };
            Adding(4, 4);//8
            Moveing(4, 4);//0
            Multiply(4, 4);//16
            Divide(4, 4);//1
        }

讲解到现在,想必大家对匿名方法有一定的了解了!

下面是参考MSDN上的一些资料如下:

通过使用匿名方法,由于您不必创建单独的方法,因此减少了实例化委托所需的编码系统开销。

例如,如果创建方法所需的系统开销是不必要的,则指定代码块(而不是委托)可能非常有用。  启动新线程即是一个很好的示例。  无需为委托创建更多方法,线程类即可创建一个线程并且包含该线程执行的代码。  

 
void StartThread()
{
    System.Threading.Thread t1 = new System.Threading.Thread
      (delegate()
            {
                System.Console.Write("Hello, ");
                System.Console.WriteLine("World!");
            });
    t1.Start();
}
备注
 
 

匿名方法的参数的范围是“匿名方法块”。

如果目标在块外部,那么,在匿名方法块内使用跳转语句(如 gotobreak 或 continue)是错误的。  如果目标在块内部,在匿名方法块外部使用跳转语句(如 gotobreak 或 continue)也是错误的。  

如果局部变量和参数的范围包含匿名方法声明,则该局部变量和参数称为该匿名方法的“外部”变量。  例如,下面代码段中的 n 即是一个外部变量:  

 
int n = 0;
Del d = delegate() { System.Console.WriteLine("Copy #:{0}", ++n); };

外部变量的引用n被认为是捕获在创建委托时。  与本地变量不同,捕获的变量的生存期内扩展,直到引用该匿名方法委托被垃圾回收。  

匿名方法不能访问外部范围的 ref 或 out 参数。

在“匿名方法块”中不能访问任何不安全代码。

在 is 运算符的左侧不允许使用匿名方法。

 好了,截止到现在,匿名方法讲解完毕,如果需要更深入的了解匿名方法,可以找度娘哈!这里仅仅只是简单的介绍!

下一节,我们共同学习拉姆达表达式!lambda说: 一步一个脚印,学好C#基础很重要!



以上是关于匿名方法和Lambda表达式的主要内容,如果未能解决你的问题,请参考以下文章

编写高质量代码改善C#程序的157个建议——建议37:使用Lambda表达式代替方法和匿名方法

C#中的委托,匿名方法和Lambda表达式

18(番外)匿名方法+lambda表达式

Lambda表达式和匿名内部类

Lambda 表达式

linq和匿名方法委托匿名委托lambda