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. 真有用?Snap和Flatpak 通吃所有发行版的打包方式。

    导读 最近我们听到越来越多的有关于Ubuntu的Snap包和由Red Hat员工Alexander Larsson创造的 Flatpak (曾经叫做 xdg-app)的消息.这两种下一代打包方法在本质 ...

  2. linux中comm命令用法

    linux系统中comm命令用法详解 linux系统下的comm命令是一个非常实用的文件对比命令. comm命令功能:   选择或拒绝两个已排序的文件的公共的行. comm命令语法:comm [-12 ...

  3. huffman编码压缩算法(转)

    参考:http://blog.csdn.net/sunmenggmail/article/details/7598012 笔试时遇到的一道题.

  4. Linux下ffmpeg的各种编解码器的安装

    首先要安装各种解码器 1.lame  tar -zxvf lame- cd lame- ./configure --enable-shared make make install 2.libogg  ...

  5. BZOJ 1068: [SCOI2007]压缩

    Sol 区间DP.这个区间DP需要三维, \(f[i][j][k]\) 表示\([i,j]\) 这个区间中是否存在 \(M\) . 转移有两种,一种是这个区间存在 \(M\) ,那么直接枚举 \(M\ ...

  6. windows下的getopt/getoptlong函数

    windows下的getopt/getoptlong函数 getopt/getopt_long函数是GNU C中的函数,在linux编程中很常用到.这里就不介绍了. windows下没有找到类似的函数 ...

  7. 代码高亮插件SyntaxHighlighter

    http://alexgorbatchev.com/SyntaxHighlighter/download/

  8. 10个php笔试题

    Q1 第一个问题关于弱类型 $str1 = 'yabadabadoo'; $str2 = 'yaba'; if (strpos($str1,$str2)) { echo "/"&q ...

  9. 不懂点CAP理论,你好意思说你是做分布式的吗?

  10. 基础知识《八》---Java反射机制

    1.反射可以做什么 2.反射相关的API 3.获取Class运行时类的实例的三种方法: 1)通过运行时类本身的.class属性***** Class clazz= Person.class; 2)通过 ...