12.1.委托概述

12.1.2 委托的数据类型

为了减少重复代码数量,可以将比较方法作为参数传递给 BubbleSort()方法。此外,为了将方法作为参数传递,必须有一个能够标识方法的数据类型——也就是委托。这里的委托类型是 ComparisonHandler 。
 c# 2.0之前的写法
  1. class DelegateSample

  2. {

  3. static void Main(string[] args)

  4. {

  5. //int[] arr = { 10, 20, 30, 40, 50 };

  6. int[] arr = { 50, 40, 30, 20, 10 };

  7. ConsoleArr(arr);

  8. ComparisonHandler wx = new ComparisonHandler(DelegateSample.IsTrue);

  9. BubbleSort(arr, wx);

  10.       //C#2.0之前是这么写的
  11.       //BubbleSort(arr, new ComparisonHandler(IsTrue));  
  12. ConsoleArr(arr);

  13. Console.Read();

  14. }

  15. public delegate bool ComparisonHandler(int a, int b);

  16. public static bool IsTrue(int a, int b)

  17. {

  18. return a > b;

  19. }

  20. public static void BubbleSort(int[] items, ComparisonHandler comparisonMethod)

  21. {

  22. int i;

  23. int j;

  24. int temp;

  25. if (items == null)

  26. {

  27. return;

  28. }

  29. if (comparisonMethod == null)

  30. {

  31. throw new ArgumentNullException("comparisonMethod");

  32. }

  33. for (i = items.Length - 1; i >= 0; i--)

  34. {

  35. for (j = 1; j <= i; j++)

  36. {

  37. if (comparisonMethod(items[j - 1], items[j]))

  38. {

  39. temp = items[j - 1];

  40. items[j - 1] = items[j];

  41. items[j] = temp;

  42. }

  43. }

  44. }

  45. }

  46. public static void ConsoleArr(int[] arr)

  47. {

  48. foreach (var item in arr)

  49. {

  50. Console.Write(item+",");

  51. }

  52. Console.WriteLine();

  53. }

  54. }



C#2.0以后可以直接调用方法
 
  1. public static bool AlphabeticalIsTrue(int a,int b)

  2. {

  3. int comparison;

  4. comparison = (a.ToString().CompareTo(b.ToString()));

  5. return comparison > 0;

  6. }

  7. //C# 2.0以后直接传递方法

  8. BubbleSort(arr, AlphabeticalIsTrue);


12.1.3 委托内部机制

第一个属性属于 System.Reflection.MethodiInfo 类型,MethodInfo 定义一个特定方法的签名,其中包括方法的名称、参数和返回类型。除了 MethodInfo,委托还需要一个对象实例,其中包含了要调用的方法。这正式第二个属性 Target 的用途。在静态方法的情况下,Target 对应于类型自身。

  1. // 摘要:

  2. //     初始化一个委托,该委托对指定的类实例调用指定的实例方法。

  3. //

  4. // 参数:

  5. //   target:

  6. //     类实例,委托对其调用 method。

  7. //

  8. //   method:

  9. //     委托表示的实例方法的名称。

  10. //

  11. // 异常:

  12. //   System.ArgumentNullException:

  13. //     target 为 null。 - 或 - method 为 null。

  14. //

  15. //   System.ArgumentException:

  16. //     绑定到目标方法时出错。

  17. [SecuritySafeCritical]

  18. protected Delegate(object target, string method);

  19. //

  20. // 摘要:

  21. //     初始化一个委托,该委托从指定的类调用指定的静态方法。

  22. //

  23. // 参数:

  24. //   target:

  25. //     System.Type,它表示定义 method 的类。

  26. //

  27. //   method:

  28. //     委托表示的静态方法的名称。

  29. //

  30. // 异常:

  31. //   System.ArgumentNullException:

  32. //     target 为 null。 - 或 - method 为 null。

  33. //

  34. //   System.ArgumentException:

  35. //     target 不是 RuntimeType。 请参见 反射中的运行时类型。 - 或 - target 表示开放式泛型类型。

  36. [SecuritySafeCritical]

  37. protected Delegate(Type target, string method);



12.2.匿名方法


传递一个匿名方法

  1. class Program

  2. {

  3. public delegate bool ComparisonHandler(int a, int b);

  4. static void Main(string[] args)

  5. {

  6. int i;

  7. int[] items = new int[5];

  8. ComparisonHandler comparionMethod;

  9. for (i = 0; i < items.Length; i++)

  10. {

  11. Console.WriteLine("Enter an integer:");

  12. items[i] = int.Parse(Console.ReadLine());

  13. }

  14. comparionMethod = delegate(int first, int second)

  15. {

  16. return first < second;

  17. };

  18. BubbleSort(items, comparionMethod);

  19. for ( i = 0; i < items.Length; i++)

  20. {

  21. Console.WriteLine(items[i]);

  22. }

  23. Console.Read();

  24. }

  25. public static void BubbleSort(int[] items, ComparisonHandler comparisonMethod)

  26. {

  27. int i;

  28. int j;

  29. int temp;

  30. if (items == null)

  31. {

  32. return;

  33. }

  34. if (comparisonMethod == null)

  35. {

  36. throw new ArgumentNullException("comparisonMethod");

  37. }

  38. for (i = items.Length - 1; i >= 0; i--)

  39. {

  40. for (j = 1; j <= i; j++)

  41. {

  42. if (comparisonMethod(items[j - 1], items[j]))

  43. {

  44. temp = items[j - 1];

  45. items[j - 1] = items[j];

  46. items[j] = temp;

  47. }

  48. }

  49. }

  50. }

  51. }



12.3.系统定义的委托:Func 和 Action 声明

在.NET3.5(C# 3.0)中,存在一系列名为“Action”和“Func”的泛型委托。
System.Func 代表有返回类型的委托,而 System.Action 代表无返回类型的委托。
 
.NET 委托类型不具备结构的相等性(structural equality)。不能将某个委托类型对象引用转换为不相关的委托类型,即使这两个委托类型的形参和返回类型完全一致。例如,这里就不能将 ComparisonHandler 引用直接赋给一个Func<int,int,bool>变量。
遗憾的是,需要结构一致但不相关的委托类型的情况下,为了使用给定的委托,唯一的办法是创建一个新委托。让它引用旧委托的 Invoke 方法。假定有一个 ComparisonHandler 变量 c,需要把 c 赋值给 Func<int,int,bool>类型的变量 f ,那么可以写成 f = c.Invoke;
Invoke说明

公共语言运行时提供 Invoke 每种委托类型,具有相同的签名与委托的方法。 您不需要显式调用此方法,从 C#、 Visual Basic 或 Visual c + +,因为编译器会自动调用。 Invoke 方法就很有用 反射 如果想要查找的委托类型签名。

https://msdn.microsoft.com/zh-cn/library/system.delegate.aspx



12.4.语句Lambda

1.无参数的语句

即使无参数的语句lambda(代表无输入参数的委托),也要输入一对空白的圆括号
  1. static void Main(string[] args)

  2. {

  3. //...

  4. Func<string> getUserInput =

  5. () =>

  6. {

  7. string input;

  8. do

  9. {

  10. input = Console.ReadLine();

  11. }

  12. while (input.Trim().Length == 0);

  13. return input;

  14. };

  15. //...

  16. }

圆括号规则的一个例外是,当编译器能推断出数据类型,而且只有一个参数的时候。语句Lambda可以不带圆括号。

2.只有一个参数的语句

  1. IEnumerable<Process> processes = Process.GetProcesses()

  2. .Where(process => { return process.WorkingSet64 > 100000000; });

Where() 返回的是对物理内存占用超过 1GB 的进程的一个查询

12.5.表达式Lambda

语句Lambda含有一个语句块,所以可以包含零个或者更多的语句,
表达式Lambda比语句Lambda更进一步。语句Lambda的代码块都只由一个return语句构成。其实在这种lambda块中,唯一需要就是准备返回的表达式。其他可以省略。
使用一个表达式Lambda来传递委托
  1. BubbleSort(items, (first, second) => first > second);

语句lambda的比较
  1. BubbleSort(items, (first, second) =>

  2. {

  3. return first > second;

  4. }

  5. );



12.6.表达式树


1.Lambda表达式作为数据使用

来看看下面的表达式
    class Program
    {
        static void Main(string[] args)
        {
            Person[] persons = new Person[3];
            IEnumerable<Person> obj = persons.Where(person => person.Name.ToUpper() == "INIGO MONTOYA");
        }
    }

    public class Person
    {
        public string Name { get; set; }
    } 
Lambda对应 Where 的是一具有委托类型的 Func<Person,bool>.
现在假定persons不是 Person[ ] 类型,而是代表远程数据库的对象,表中含有数百万人的数据。表中每一行信息都可以从服务器传输到客户端,客户端可以创建一个Person对象代表那一行。在客户端调用Where执行查询,如何判断结果?
  1. 一个技术是将几百万数据传输到客户端,为每一行创建一个Person对象,根据Lambda创建一个委托,再针对每个Person执行这个委托。概念上和数组的情况一致,但代价过于昂贵。

  2. .第二个技术要好很多,将Lambda的含义(过滤掉姓名不是INIGO MONTOYA的每一行)发给服务器。服务器将符合条件的少数几行传输到客户端;而不是先创建几百万个Person对象,再丢弃。但是没在那将Lambda的含义发送给服务器?

这正式语言中添加表达式树这一概念的动机。

 传给Where()的表达式树指出Lambda实参由以下几个几部分组成:
  1. 对Person的Name属性的调用;

  2. 对string的ToUpper()方法调用;

  3. 一个常量值“INIGO MONTOYA”;

  4. 一个相等性操作符==。

Where()方法获取这些数据并转换成一个 SQL where 子句。

2.表达式树作为对象图使用

表达式树转换成的数据是一个对象图,由 System.Linq.Expressions.Expression 表示。图中的“根”本身代表Lambda对象本身,这个对象引用代表参数、一个返回类型和一个主体表达式的对象。
 可以为一元表达式或二元表达式创建一个对象图
 UnaryExpression代表一个形如count++的表达式。它具有Expression类型单个子操作数(Operand)。BinaryExpression有两个子表达式,Left和Right。两个类型都通过NodeType属性标识具体的运算符。两者都是从基类Expression派生。其他还有约30中表达式类型,如NewExpression、ParameterExrepssion等。

3.委托和表达式树的比较

像Where() 这样的用于构建LINQ查询方法是扩展方法。
扩展了 IEnumerable<T> 接口方法获取委托参数,扩展 IQueryable<T> 接口的方法获取表达式树参数。

假定一个集合支持IEnumerable,可以像下面这样调用 Where():
IEnumerable<Person> obj = persons.Where(person => person.Name.ToUpper() == "INIGO MONTOYA"); 

在 System.Linq.Enumerable 类中声明的扩展方法签名:
  1. public static IEnumerable<TSource> Where<TSource>(

  2. this IEnumerable<TSource> source,

  3. Func<TSource, bool> predicate);


在 System.Linq.Queryable 类声明的扩展方法签名:
  1. public static IQueryable<TSource> Where<TSource>(

  2. this IQueryable<TSource> source,

  3. Expression<Func<TSource, bool>> predicate)


编译器根据persons在编译时的类型决定使用哪个扩展方法;如果是一个能转换成 IQueryable<Person>的类型,就选择来自  System.Linq.Queryable 的方法。它将Lambda转换成一个表达式树。
执行时,persons接收表达式树结构,构造一个SQL,在请求查询结果时传给数据库并生成结果。调用Where的结果是一个对象。
如果persons不能隐式转换成 IQueryable<Person>,但能隐式转换成 IEnumerable<Person>,那就选择来自System.Linq.Enumerable的方法,lambda被转换成一个委托,当请求查询时,将委托作为断言应用于集合的每个成员,并生成与断言匹配的结果,调用Where 的结果是一个对象。

4.解析表达式

将Lambda表达式转换成 System.Linq.Expressions.Expression<TDelegate> 将创建一个表达式树,而不是委托。前面说将
  1. (x,y)=>x>y

这样的Lambda转换成
  1. Func<int,int,bool>

这样的委托类型。如下面的代码,可以检查生成树对象,显示它结构相关的信息,还可以显示更复杂的表达式树信息。
要注意,将表达式树实例传给 Console.WriteLine(expression) 方法,会自动将表达式树转换成一个描述性字符串形式。为表达式树重写了 ToString() ,以便调试时看出表达式内容。

名称 说明
Body 获取 lambda 表达式的主体。 (继承自 LambdaExpression。)
CanReduce 指示可将节点简化为更简单的节点。 如果返回 true,则可以调用 Reduce() 以生成简化形式。 (继承自Expression。)
Name 获取 lambda 表达式的名称。 (继承自 LambdaExpression。)
NodeType Expression." xml:space="preserve">返回此 Expression 的节点类型。 (继承自 LambdaExpression。)

在 Silverlight for Windows Phone Windows Phone OS 7.1 中,此成员是从 Expression.NodeType 中继承的。

在 XNA Framework Windows Phone OS 7.0 中,此成员是从 Expression.NodeType 中继承的。
Parameters 获取 lambda 表达式的参数。 (继承自 LambdaExpression。)
ReturnType 获取 lambda 表达式的返回类型。 (继承自 LambdaExpression。)
TailCall 获取一个值,该值指示是否将通过尾调用优化来编译 lambda 表达式。 (继承自 LambdaExpression。)
Type Expression represents." xml:space="preserve">获取此 Expression 所表示的表达式的静态类型。 (继承自 LambdaExpression。)

在 Silverlight for Windows Phone Windows Phone OS 7.1 中,此成员是从 Expression.Type 中继承的。

在 XNA Framework Windows Phone OS 7.0 中,此成员是从 Expression.Type 中继承的。
  1. Expression<Func<int, int, bool>> expression;

  2. expression = (x, y) => x > y;

  3. Console.WriteLine("------{0}------",expression);

  4. PrintNode(expression.Body, 0);//expression.Body: (x > y)  [lambda 表达式的主体]

  5. Console.WriteLine();

  6. Console.WriteLine();

  7. expression = (x, y) => x * y > x + y;

  8. Console.WriteLine("------{0}------",expression);

  9. PrintNode(expression.Body, 0);                  //expression.Body: (x * y) > (x + y)

  10. /*

  11. * expression.Body: (x * y) > (x + y)  [lambda 表达式的主体]

  12. * BinaryExpression是否具有二进制运算符:true

  13. * expression.Left:(x * y)

  14. * BinaryExpression是否具有二进制运算符:true

  15. * expression.Left:x   expression.NodeType:Parameter

  16. * BinaryExpression是否具有二进制运算符:false

  17. * expression.Left:*   expression.NodeType:Multiply

  18. * .....

  19. */

  20. Console.WriteLine();

  21. Console.WriteLine();

  22. Console.Read();

  23. }

  24. public static void PrintNode(Expression expression, int indent)

  25. {

  26. if (expression is BinaryExpression)   //具有二进制运算符的表达式

  27. PrintNode(expression as BinaryExpression, indent);

  28. else

  29. PrintSingle(expression, indent);

  30. }

  31. private static void PrintNode(BinaryExpression expression, int indent)

  32. {

  33. PrintNode(expression.Left, indent + 1);

  34. PrintSingle(expression, indent);

  35. PrintNode(expression.Right, indent + 1);

  36. }

  37. private static void PrintSingle(Expression expression, int indent)

  38. {

  39. Console.WriteLine("{0," + indent * 5 + "}{1}", "", NodeToSting(expression));

  40. }

  41. private static string NodeToSting(Expression expression)

  42. {

  43. switch (expression.NodeType)

  44. {

  45. case ExpressionType.Multiply:

  46. return "*";

  47. case ExpressionType.Add:

  48. return "+";

  49. case ExpressionType.Divide:

  50. return "/";

  51. case ExpressionType.Subtract:

  52. return "-";

  53. case ExpressionType.GreaterThan:

  54. return ">";

  55. case ExpressionType.LessThan:

  56. return "<";

  57. default:

  58. return expression.ToString() + "(" + expression.NodeType.ToString() + ")";

  59. }

  60. }

证明表达式树是由零个或多个其他表达式树构成,代表Lambda的“根”通过Body属性引用Lambda的主体。每个表达式树节点都包含枚举类型ExpressionType的一个NodeType属性,描述它是哪一种表达式。

注意:Lambda语句不能转换成表达式树,只有表达式Lambda才能转为表达式树。





《C#本质论》读书笔记(12)委托和Lambda表达式的更多相关文章

  1. 《疯狂Kotlin讲义》读书笔记6——函数和Lambda表达式

    函数和Lambda表达式 Kotlin融合了面向过程语言和面向对象语言的特征,相比于Java,它增加了对函数式编程的支持,支持定义函数.调用函数.相比于C语言,Kotlin支持局部函数(Lambda表 ...

  2. 强化学习读书笔记 - 12 - 资格痕迹(Eligibility Traces)

    强化学习读书笔记 - 12 - 资格痕迹(Eligibility Traces) 学习笔记: Reinforcement Learning: An Introduction, Richard S. S ...

  3. 机器学习实战 - 读书笔记(12) - 使用FP-growth算法来高效发现频繁项集

    前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第12章 - 使用FP-growth算法来高效发现频繁项集. 基本概念 FP-growt ...

  4. C#高级编程(第9版) 第08章 委托、lambda表达式和事件 笔记

          本章代码分为以下几个主要的示例文件: 1. 简单委托 2. 冒泡排序 3. lambda表达式 4. 事件示例 5. 弱事件     引用方法 委托是寻址方法的.NET版本.在C++中函数 ...

  5. 委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底

    本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景.各位看官,这里就不啰嗦了,直接上代码. 首先定义一个泛型委托类型,如下: public delegate T Function&l ...

  6. 转载 C#匿名函数 委托和Lambda表达式

    转载原出处: http://blog.csdn.net/honantic/article/details/46331875 匿名函数 匿名函数(Anonymous Function)是表示“内联”方法 ...

  7. 十二、C# 委托与Lambda表达式(匿名方法的另一种写法)

    委托与Lambda表达式   1.委托概述 2.匿名方法 3.语句Lambda 4.表达式Lambda 5.表达式树   一.委托概述 相当于C++当中的方法指针,在C#中使用delegate 委托来 ...

  8. 委托与Lambda表达式

    ~,先不急说委托和Lambda表达式,先看两个例子再说: 1. 通过委托,为一个数字加10,如下代码: class Program { private delegate int JiSuan(int ...

  9. 系统预定义委托与Lambda表达式

    NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式   开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Fun ...

  10. C#函数式程序设计之函数、委托和Lambda表达式

    C#函数式程序设计之函数.委托和Lambda表达式 C#函数式程序设计之函数.委托和Lambda表达式   相信很多人都听说过函数式编程,提到函数式程序设计,脑海里涌现出来更多的是Lisp.Haske ...

随机推荐

  1. BZOJ1588——[HNOI2002]营业额统计

    1.题目大意:一个简单的treap模板题(我才不告诉你题目少一句话呢,看discuss去 2.分析:treap模板题 #include <cstdio> #include <cstd ...

  2. [BZOJ3572][Hnoi2014]世界树

    [BZOJ3572][Hnoi2014]世界树 试题描述 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条 ...

  3. [KOJ95603]全球奥运

    [COJ95603]全球奥运 试题描述 一个环形的图中有N个城市,奥运会重要项目就是传递圣火,每个城市有A[i]个圣火,每个城市可以向它相邻的城市传递圣火(其中1号城市可以传递圣火到N号城市或2号城市 ...

  4. 如何优雅地使用 Sublime Text

    Sublime Text:一款具有代码高亮.语法提示.自动完成且反应快速的编辑器软件,不仅具有华丽的界面,还支持插件扩展机制,用她来写代码,绝对是一种享受.相比 于难于上手的Vim,浮肿沉重的Ecli ...

  5. 我要阻止做java开发的男朋友去创业型公司工作吗?

    命这样的标题,我没有瞧不起创业型公司,我只是有点急了,因为男朋友今天晚上就要回复招聘公司了.我先来说说来由吧. 前段时间男朋友离职了,从毕业到现在在公司呆了2年多,因为资金不足导致他做的项目被迫停止了 ...

  6. mysql 查询某个日期时间段,每天同一时间段的数据

    mysql 查询某个日期时间段,每天同一时间段的数据: SELECT * FROM t_a01_eltable WHERE DATE_FORMAT(acqtime,'%Y-%m-%d')>='2 ...

  7. ZUI前段框架简介

    一.说明 基于Bootstrap定制 ZUI继承了Bootstrap 3中的大部分基础内容,但出于与Bootstrap不同的目的,一些组件都进行了定制和修改.这些变化包括: 移除了部分插件的限制,增加 ...

  8. qmake的使用

    [TOC] 本文由乌合之众 lym瞎编,欢迎转载 blog.cnblogs.net/oloroso 本文由乌合之众 lym瞎编,欢迎转载 my.oschina.net/oloroso *** 还是先说 ...

  9. linux 终端报错 Out of memory: Kill process[PID] [process name] score问题分析

    从Out of memory来看是内存超出了,后面的 Kill process[PID] [process name] score好像和进程有关了,下面我们就一起来看看linux 终端报错 Out o ...

  10. js中val()和value的区别

    val()是在有jQuery插件的时候才能用,value是在没有jQuery插件的情况下也能用.val()是jQuery根据原生JS里面的value写出来的函数 $(this).val(); 有四个重 ...