现在的位置: 首页 > 综合 > 正文

C#中的委托和Lambda表达式

2018年04月08日 ⁄ 综合 ⁄ 共 5922字 ⁄ 字号 评论关闭

今天看Curator源码时发现其请求ZooKeeper集群时内部封装了重试机制,代码如下:

Stat resultStat = RetryLoop.callWithRetry
        (
            client.getZookeeperClient(),
            new Callable<Stat>()
            {
                @Override
                public Stat call() throws Exception
                {
                    return client.getZooKeeper().setData(path, data, version);
                }
            }
        );
public static<T> T callWithRetry(CuratorZookeeperClient client, Callable<T> proc) throws Exception
    {
        T result = null;
        RetryLoop retryLoop = client.newRetryLoop();
        while ( retryLoop.shouldContinue() )
        {
            try
            {
                client.internalBlockUntilConnectedOrTimedOut();
                
                //调用带返回值的Callable方法
                result = proc.call();
                retryLoop.markComplete();
            }
            catch ( Exception e )
            {
                retryLoop.takeException(e);
            }
        }
        return result;
    }

利用Java中的Callable<T>,即带返回值T的线程这个功能来将失败时要重试的代码段传递到重试函数中进行调用。


因为我要开发C#的对应的版本,所以也需要将一段代码(请求ZooKeeper的代码)传递到这样一个具备失败重试功能的函数中运行,由于我对C#不是很熟悉,所以能想到的只有通过声明一个代理,再定义一个操作ZooKeeper的函数,将这个函数赋值给代理,将代理传递到具备重试功能的函数中。


但是这种方式势必要定义一个代理,还要显式申明一个方法赋值给代理,代码量大且不是很优雅,所以我就开始深入了解C#这方面如何实现,然后发现了C#2.0引入的匿名方法,以及C#3.0引入的用于取代匿名方法的Lambda表达式,是编写内联代码的首选。下面先提供我写的一段测试代码,可以完成我的需求,之后再讲解何谓匿名方法和Lambda表达式。


CustomDelegate.cs:

using System;
using System.Collections.Generic;
using System.Linq;

namespace DelegateAndLamda
{
    delegate string CustomDelegate(string material);
}

PizzaMaker.cs:

namespace DelegateAndLamda
{
    class PizzaMaker
    {
        public static void makePizza(string material, CustomDelegate bake)
        {
            //前面有一堆固定流程,但是接下来烘烤方法不同
            string result = bake(material);
            Console.WriteLine(result);
        }
    }
}

Program.cs

namespace DelegateAndLamda
{
    class Program
    {
        public static void Main(string[] args)
        {
            //在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法
            CustomDelegate bake = new CustomDelegate(bakePizza);
            PizzaMaker.makePizza("张三的Pizza", bake);

            // C# 2.0 引入了匿名方法
            PizzaMaker.makePizza("李四的Pizza", delegate(string material)
            {
                return String.Format("将{0}烘烤成面包", material);
            } );

            //在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式
            PizzaMaker.makePizza("季义钦的Pizza", material => 
            { 
                return String.Format("将{0}烘烤成意大利pizza", material); 
            });
        }        

        public static string bakePizza(string material)
        {
            return String.Format("将{0}烘烤成西瓜", material);
        }
    }
}


Lambda表达式

Lambda表达式是一种可用于创建委托表达式目录树类型的匿名函数。 通过使用
lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数。 Lambda 表达式对于编写 LINQ 查询表达式特别有用。

若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。 例如,lambda表达式 x
=> x * x
 指定名为 x 的参数并返回 x 的平方值。

1 表达式 lambda

表达式位于 => 运算符右侧的 lambda 表达式称为“表达式 lambda”。 表达式 lambda 广泛用于表达式树(C#和 Visual Basic的构造。 表达式
lambda 会返回表达式的结果,并采用以下基本形式:

(input parameters) => expression

2语句lambda

语句 lambda 与表达式 lambda 表达式类似,只是语句括在大括号中:

(input parameters) => {statement;}

 

C#内置委托

其实细心的朋友会发现,即使是使用lambda表达式定义一段代码作为参数进行传递,虽然不需要显式定义一个函数了,但是仍然要自定义一个委托,那么C#中是否有一些内置的委托可以满足我们的需求呢?即我们不需要再去定义一些委托,尽量减少代码量,答案是肯定的。

内置的委托就像我们自己已定义好的一样,要实现某些功能,我们可以直接利用系统内置委托,实例化它们,而不必显式定义一个新委托并将命名方法分配给该委托。

一、Action类的委托

1.Action委托 封装一个方法,该方法不具有参数并且不返回值

2.Action<T>委托 封装一个方法,该方法只有一个参数并且不返回值

3.Action<T1,T2>委托 封装一个方法,该方法具有两个参数并且不返回值

…… ……

17.Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>委托 封装一个方法,该方法具有16个参数并且不返回值

下面以Action<T>委托为例,示范如何使用Action类的委托,该类委托仅仅是参数个数上有区别而已。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

static void Main(string[] args)

{

  #region Action<T>委托示例

  //需求:打印出整型集合list的元素

  List<int> list = new List<int>() { 1, 2, 3, 4, 5 };

  //将匿名方法分配给 Action<T> 委托实例

  Action<int> concat1 = delegate(int i) { Console.WriteLine(i); };

  list.ForEach(concat1);

  //将 lambda 表达式分配给 Action<T> 委托实例

  Action<int> concat2 = (i => Console.WriteLine(i));

  list.ForEach(concat2);

  Console.ReadKey();

  #endregion

}

总结:

Action类的委托最少可以传入0个参数,最多可以传入16个参数,参数类型皆为逆变,并且不返回值。


二、Func类的委托

1.Func(TResult)委托封装封装一个不具有参数但却返回 TResult 参数指定的类型值的方法

2.Func(T,TResult)委托 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法

3.Func(T1,T2,TResult)委托 封装一个具有两个参数并返回 TResult 参数指定的类型值的方法

…… ……

17.Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>委托 封装一个方法,该方法具有16个参数,并返回TResult参数所指定的类型的值

下面以Func<T,TResult>委托为例,示范如何使用Func类的委托,该类委托仅仅是参数个数上有区别而已。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

static void Main(string[] args)

{

  #region Func<T,TResult>委托示例

  //需求:查找整型集合list中大于3的所有元素组成的新集合,并打印出集合元素

  List<int> list = new List<int>() { 1, 2, 3, 4, 5 };

  //将匿名方法分配给 Func<T,TResult> 委托实例

  Func<int, bool> concat1 = delegate(int i) { return i > 3; };

  var newlist1 = list.Where(concat1).ToList();

  //将 Lambda 表达式分配给 Func<T,TResult> 委托实例

  Func<int, bool> concat2 = i => i > 3;

  var newlist2 = list.Where(concat2).ToList();

  newlist1.ForEach(i => Console.WriteLine(i.ToString()));

  newlist2.ForEach(i => Console.WriteLine(i.ToString()));

  Console.ReadKey();

  #endregion

}

总结:

Func类的委托最少可以传入输入泛型参数(in,逆变) 1个,最多可以传入输入泛型参数(in,逆变) 16个,传入的输出泛型参数(out,协变)有且只有一个,这个类型是此委托封装的方法的返回值类型。


三、Predicate<T>委托

表示定义一组条件并确定指定对象是否符合这些条件的方法

下面给出Predicate<T>委托的范例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

static void Main(string[] args)

{

  #region Predicate<T>委托示例

  //需求:查找整型集合list中大于3的所有元素组成的新集合,并打印出集合元素

  List<int> list = new List<int>() { 1, 2, 3, 4, 5 };

  //将匿名方法分配给 Predicate<T> 委托实例

  Predicate<int> concat1 = delegate(int i) { return i > 3; };

  var newlist1 = list.FindAll(concat1);

  //将 lambda 表达式分配给 Predicate<T> 委托实例

  Predicate<int> concat2 = (c => c > 3);

  var newlist2 = list.FindAll(concat2);

  newlist1.ForEach(i => Console.WriteLine(i));

  newlist2.ForEach(i => Console.WriteLine(i));

       Console.ReadKey();

  #endregion

}

总结:

Predicate<T>委托封装一个方法,该方法传入一个类型参数,这个参数是指要比较的对象的类型,此类型参数是逆变,同时接收一个参数(该参数就是要按照由此委托表示的方法中定义的条件进行比较的对象,参数的类型就是传入的类型参数的类型),该方法始终返回bool类型的值。如果该对象符合由此委托表示的方法中定义的条件,则为 true;否则为 false。


四、Comparison<T>委托

表示比较同一类型的两个对象的方法

下面给出Comparison<T>委托的范例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

static void Main(string[] args)

{

  #region Comparison<T>委托示例

  //需求:将整型集合list中的所有元素倒序排列打印出来

  List<int> list = new List<int>() { 1, 2, 3, 4, 5 };

  //将匿名方法分配给 Comparison<T> 委托实例

  Comparison<int> concat1 = delegate(int i, int j) { return j - i; };

  //将 lambda 表达式分配给 Comparison<T> 委托实例

  Comparison<int> concat2 = (i, j) => j - i;

  list.Sort(concat1);

  list.ForEach(c => Console.WriteLine(c.ToString()));

  list.Sort(concat2);

  list.ForEach(c => Console.WriteLine(c.ToString()));

       Console.ReadKey();

  #endregion

}

 总结:

Comparison<T>委托封装一个方法,该方法传入一个类型参数,这个参数是指要比较的对象的类型,此类型参数是逆变,同时接收两个同类型的参数(这两个参数就是要比较的两个对象,参数的类型就是传入的类型参数的类型),始终返回int类型的值,即一个有符号整数,指示 x 与 y 的相对值,如下表所示。

含义

小于0

x 小于y 

0

x 等于y 

大于0

x 大于y 

参考网址:

委托: http://msdn.microsoft.com/zh-cn/library/900fyy8e.aspx

匿名方法: http://msdn.microsoft.com/zh-cn/library/0yw3tz5k.aspx

Lambda表达式: http://msdn.microsoft.com/zh-cn/library/bb397687.aspx

表达式树: http://msdn.microsoft.com/zh-cn/library/bb397951.aspx

Linq查询: http://msdn.microsoft.com/zh-cn/library/bb397906.aspx

抱歉!评论已关闭.