委托与Lambda表达式
 
1、委托概述
2、匿名方法
3、语句Lambda
4、表达式Lambda
5、表达式树
 
一、委托概述
相当于C++当中的方法指针,在C#中使用delegate 委托来提供相同的功能,
它将方法作为对象封装起来,允许在"运行时"间接地绑定一个方法调用。
声明的委托相当于一种自定义的数据类型。
1、背景
冒泡排序

     static class SimpleSort1
{
public static void BubbleSort(int[] items)
{
int i = , j = , temp = ;
if (items == null)
{
return;
}
for (i = items.Length - ; i >= ; i--)
{
for (j = ; j <= i; j++)
{
if (items[j - ] > items[i])
{
temp = items[j - ];
items[j - ] = items[i];
items[i] = temp;
}
} }
}
}
如果需要提供升序和降序两个功能选择,可以加上参数SortType来区别

     class Program
{
static void Main(string[] args)
{ int[] arr = new int[] { , , , , , , , , , , , };
SimpleSort1.BubbleSort(arr, SortType.Ascending);
string str = "";
for (int i = ; i < arr.Length; i++)
{
str += arr[i] + ","; }
Console.WriteLine(str); str = "";
SimpleSort1.BubbleSort(arr, SortType.Descending);
for (int i = ; i < arr.Length; i++)
{
str += arr[i] + ","; }
Console.WriteLine(str);
Console.ReadLine(); }
}
enum SortType
{
Ascending,
Descending
}
static class SimpleSort1
{
public static void BubbleSort(int[] items, SortType sorttype)
{
int i = , j = , temp = ;
if (items == null)
{
return;
}
for (i = items.Length - ; i >= ; i--)
{
for (j = ; j <= i; j++)
{
switch (sorttype)
{
case SortType.Ascending:
if (items[j - ] > items[i])
{
temp = items[j - ];
items[j - ] = items[i];
items[i] = temp;
}
break;
case SortType.Descending:
if (items[j - ] < items[i])
{
temp = items[j - ];
items[j - ] = items[i];
items[i] = temp;
}
break; } } } }
}
如果想要按字母、随机或者按照其他方式来排序, 可以通过增加一个SortType参数
BubbleSort()方法及其对应的SortType值的数量很快会变量非常之多。
 
2、委托数据类型
为了减少重复代码的数量,可以将比较方法作一个参数
传给BubbleSort()方法,此外,为了能将方法作为参数传递,必须要有一个能够
表示方法的数据类型---也就是要有一个委托。
 
大致使用步骤:声明一个委托数据类型,声明的一个新的委托相当于一个数据类型。
可以用来作为形式参数的类型,在方法调用的时候,赋值一个与此委托相当签名的方法。
 
 
3、委托的内部机制
C#将所有委托定义成间接派生于System.Delegate
委托类型的对象模型:待查。
所有委托都是不可变的。更改“委托”要求实例化一个新委托,并在新委托中包含要修改
的地方。
 
4、委托类型的定义

   class DelegateSample
{
public delegate bool ComparisonHandler(int first, int second);
//相当于创建了一个数据类型:DelegateSample.ComparisonHandler
//因为它被定义成嵌套在DelegateSample中的一个类型。 }
虽然所有委托数据类型都间接从System.Delegate派生,但C#编译器并不允许定义一个直接或间接
从System.Delegate派生的类。 class Program
{
static void Main(string[] args)
{ int[] arr = new int[] { , , , , , , , , , , , };
string str = "";
//调用方法时,将指定的函数作为实际参数使用。使用方法来创建一个委托变量,委托是一个引用类型,但不必
//用new来实例化它。直接传递名称,而不是显式实例化,这是自C#2.0开始支持的一个新语法,称为委托推断 delegate interface
//采用这个语法,编译器将根据方法名来查找方法签名,并验证它同方法的参数类型匹配。
SimpleSort1.BubbleSort(arr, SimpleSort1.GreaterThan);
for (int i = ; i < arr.Length; i++)
{
str += arr[i] + ","; }
Console.WriteLine(str); str = "";
SimpleSort1.BubbleSort(arr, SimpleSort1.LonwerThan);
for (int i = ; i < arr.Length; i++)
{
str += arr[i] + ","; }
Console.WriteLine(str); str = "";
SimpleSort1.BubbleSort(arr, SimpleSort1.CharThan);
for (int i = ; i < arr.Length; i++)
{
str += arr[i] + ","; }
Console.WriteLine(str); Console.ReadLine(); }
} static class SimpleSort1
{
//使用委托数据类型 声明一个变量作为形式参数
public static void BubbleSort(int[] items, DelegateSample.ComparisonHandler compareMethod)
{
int i = , j = , temp = ;
if (items == null)
{
return;
}
for (i = items.Length - ; i >= ; i--)
{
for (j = ; j <= i; j++)
{
if (compareMethod(items[j - ], items[i]))
{
temp = items[j - ];
items[j - ] = items[i];
items[i] = temp;
}
}
}
} //以下四个函数都与数据类型DelegateSample.ComparisonHandler(委托) 具有同样的签名
public static bool GreaterThan(int first, int second)
{
return first > second;
}
public static bool LonwerThan(int first, int second)
{
return first < second;
}
public static bool CharThan(int first, int second)
{
int flag = (first.ToString()).CompareTo(second.ToString());
return (flag > ) ? true : false;
}
}
class DelegateSample
{
public delegate bool ComparisonHandler(int first, int second);
//相当于创建了一个数据类型:DelegateSample.ComparisonHandler }
 
二、匿名方法
C#2.0 和更高的版本还支持不念旧恶名为匿名方法的特性。
所谓匿名方法,就是没有实际方法声明的委托实例。

             DelegateSample.ComparisonHandler compareMethod;
compareMethod =
delegate(int first, int second)
{
return first > second;
};
SimpleSort1.BubbleSort(arr, compareMethod);
 
在这个上下文中,delegate关键字指定了一个“委托字面值“类型,这类似用双引号来指定一个
字符串字面值。
同理可以在不使用compareMethod 变量的前提下直接使用匿名方法当作实际参数来使用。
             SimpleSort1.BubbleSort(arr,
delegate(int first, int second)
{
return first > second;
}
);

在任何情况下,匿名方法的参数和返回值类型都必须兼容于相对应的委托类型。

自C#2.0开始,可以利用匿名方法这一新特性来声明一个没有名字的方法,该方法将
自动被转换成一个委托。
无参数的匿名方法,可以省略() ,但返回类型仍然需要与委托的返回类型兼容。
 
三、系统定义的委托:Func<> Action<>
在.NET3.5(C#3.0)中,存在一系列名为Action和Func的泛型委托。(委托模板)
System.Func代表有返回类型的委托,而System.Action代表无返回类型的委托。
这些委托全是泛型的,因此可以直接使用它们而不用自定义一个委托。
如:
系统自带的
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);这只是其中的一种
更多可以参考源程序集。
可以如此使用
             System.Func<int, int, bool> compareMethodFun;
compareMethodFun =
delegate(int first, int second)
{
return first > second;
};
 
代码可以不必声明一个ComparisonHandler委托类型,而是直接使用Func<int, int, bool> 这个泛型类型委托
来声明一个委托类型的实例。
注:Func的最后一个类型参数总是委托的返回类型,在它之前的类型参数
则依次对应于委托参数的类型。
对于那些没有返回类型而且不接受参数的委托,应该使用System.Action或者某个泛型类型。
 
 
比如自定义的委托类型,可以让我们明确知道该委托的用途,相反
Func<int, int, bool> 除了让我们明白方法的签名之外,就不能再提供其他有意义的信息了。
 
注:这些只有安装.NET3.5的客户端才能使用此功能。
 
注意,即使可以且一个Func泛型委托来代替一个显式定义的委托,两者的类型也不兼容。
不能将一个委托类型赋给另一个委托类型的变量,即使类型参数匹配。
 
通过C#4.0 添加的对协变性和逆变性的支持,确实可以在它们之间进行一定程序的转型。
 
但是,假如代码中使用某个委托类型能使编码变得更简单,那么仍然应该声明委托类型。
 
void Action<in T>(T arg) 有一个in类型的参数修饰符,所以支持逆变性
所以可以将Action<object>类型的一个对象赋给Action<string>类型的一个变量。
同理out

             Action<Object> broadAction = delegate(Object o)
{
Console.WriteLine(o);
};
Action<String> narrowAction = broadAction; Func<string> narrowFunction = delegate()
{
return Console.ReadLine();
}; Func<Object> broadFunction = narrowFunction;
同理,可以in out同时发生。
               Func<Object, String> narrowFunction = delegate(Object obj)
{ return obj.ToString();
}; Func<String, Object> broadFunction = narrowFunction;
在这些泛型委托中对协变性和逆变性 的需求是C#现在添加这个功能的一个关键原因。
 
四、Lambda表达式
C#3.0中引入的Lambda表达式是比匿名方法更加简洁的一种匿名函数语法。
这里的匿名函数泛指Lambda表达式和匿名方法。
Lambda表达式本身划分为两种类型:语句Lambda和表达式Lambda。
与匿名函数有关的术语,待查。
注:所有匿名函数都是不可变的。
 
1、语句Lambda
语句Lambda是C#3.0为匿名方法提供的一种简化语法。
这种语法不包含delegate关键字,但添加了Lambda运算符  =>  。

             SimpleSort1.BubbleSort(arr,
(int first, int second) =>
{
//可以有多个语句
return first > second;
}
);
查看含有Lambda运算符的代码时,可以将这个运算符理解成"用于"两字。
如以上则理解为first second用于返回 first>second。
语句Lambda还允许通过"类型参数推断"来进一步简化语法。可以不显式声明参数的
数据类型,只要编译器能推断出类型,语句Lambda就允许省略参数类型。
             SimpleSort1.BubbleSort(arr,
(first, second) =>
{
return first > second;
}
);
通常,只要编译器能推断出参数类型,或者能将参数类型隐式转换成期望的类型,语句
Lambda就不需要参数类型。
只要语句Lambda包含了一个类型,所有类型都要加上。
即使是无参数的语句Lambda,也需要输入一对空白的圆括号。
 
圆括号规则的一个例外是,如果编译器能推断出数据类型,而且只有一个输入参数的时候,语句
Lambda可以不带圆括号。
如:
             IEnumerable<Process> processes = Process.GetProcesses().Where(
process => { return process.WorkingSet64 > ( ^ ); });
 
以上代码返回的是对物理内存占用超过1GB的进程的一个查询。
虽然一个语句Lambda允许包含任意数量的语句,但在它的语句块中,一般只使用两三条语句。
 
2、表达式Lambda
语句Lambda含有一个语句块,所以可以包含多个语句或者零个语句。
表达式Lambda只有一个表达式,没有语句块。其它一致。
如:
             SimpleSort1.BubbleSort(arr, (first, second) => first > second);
SimpleSort1.BubbleSort(arr, (int first, int second) => first > second);
语句Lambda和表达式Lambda的区别在于,语句Lambda在Lambda运算符的右侧有一个语句块,用{}包含。
而表达式Lambda只有一个表达式(没有return语句及大括号)。
 
注:括号及参数类型的省略同语句Lambda
 
3、Lambda表达式和匿名方法的内部机制
 
Lambda表达式(和匿名方法)并非CLR内部的固有构造,它们的实现是C#编译器在编译时生成的。
 
Lambda表达式为“以内嵌方式声明的委托”模式提供了一个对应的C#语言构造。
这样一来,C#编译就会为这个模式生成实现代码。
在CIL中,一个匿名方法被转换成一个单独、由编译器
内部声明的静态方法,这个静态方法再实例化成一个委托,并作为参数传递。
 
4、外部变量
在Lambda表达式(包括参数)的外部声明,但在Lambda表达式内部访问的局部变量称为该
Lambda表达式的外部变量。
this也是一个外部变量,外部变量被匿名函数捕捉(访问)后,在匿名函数的委托被销毁之前,该
外部变量将一直存在。
被捕捉的变量的生存期至少和捕捉它的最长命的委托对象一样长。
类似闭包。
 
外部变量的CIL实现 。
被捕捉的局部变量被当作一个实例字段来实现,从而延长了其生命周期。
对局部变量的所有引用都被重新编写成对那个字段的引用。
变量允许在不改变表达式签名的前提下,将数据从表达戒指一次调用传递到下一次调用。
 
5、表达式树
 
如果一种Lambda表达式代表的是与表达式有关的数据,而不是编译好的代码,这种Lambda表达式就是
"表达式树"。
1、Lambda表达式作为数据使用
由于表达式树代表的是数据而非编译好的代码,所以可以把数据转换成一种替代格式。
例如,可以把它从表达式数据转换成数据库中执行的SQL代码。
但,表达式树并非只能转换成SQL语句。
 
2、表达式树作为对象图使用
表达式树转换成的数据是一个对象图,它由System.Linq.Expressions.Expression表示。
 
 
3、Lambda表达式和表达式树的比较
 
在编译时,Lambda表达式在CIL中被编译成一个委托,而表达式树被编译成System.Linq.Expressions.Expression
类型的一个数据结构。
 
可通过System.Linq.Enumerable 与 System.Linq.Queryable这两个类的进行比较.
它们分别实现了IEnumerable和IQueryable集合接口。例如 Where()。
 
4、解析表达式树
由于Lambda表达式没有与生俱来的类型,所以将Lambda表达式赋给System.Linq.Expressions.Expression<TDelegate>
会创建表达式树,而不是委托。
  System.Linq.Expressions.Expression<Func<int, int, bool>> expression;
expression = (x, y) => x > y;
 
表达式树是一个数据集合,而通过遍历数据,就可以将数据转换成另一种格式。
有多个表达式类型,
如:
System.Linq.Expressions.BinaryExpression
System.Linq.Expressions.ConditionalExpression
System.Linq.Expressions.LambdaExpression
System.Linq.Expressions.MethodCallExpression
System.Linq.Expressions.ParameterExpression
System.Linq.Expressions.ConstantExpression
每个类都派生自System.Linq.Expressions.Expression
 
通常,语句Lambda和表达式Lambda可以互换使用。然则,不能将语句Lambda转换成"表达式树"。
只能使用表达式Lambda语法来表示"表达式树"。
 
小结:
利用委托,可以传递一组能在不同位置调用的指令,这些指令不是立即调用的,而是在完成编码之后,再在其他位置调用。
C#2.0 中引入了"匿名方法"
C#3.0 中引入了Lambda表达式的概念。
Lambda表达式语法取代(但并非消除)了C#2.0 的匿名语法。
不管哪种语法,程序员都可以利用它们直接将一组指令赋一个变量,而不必显式地定义
一个包含这些指令的方法。
这使得程序员可以在方法内部动态地编写指令----这是一个很强大的概念,
它通过一个称为LINQ(语言集成查询)的API大幅简化了集合编程。
表达式树的概念,描述了它们如何编译成数据来表示一个Lambda表达式,而不是表示委托的具体实现。
利用这一特性,LINQ to SQL 和 LINQ to XML等库能解释表达式树,
并在CIL之外的上下文中使用它。
 

十二、C# 委托与Lambda表达式(匿名方法的另一种写法)的更多相关文章

  1. Lambda表达式&匿名方法

    “Lambda表达式“(lambda Expression)就是一个匿名函数(匿名方法),lambda表达式基于数学中的入演算得名. lambda运算符:所有的lambda表达式都是用新的lambda ...

  2. 委托,lambda,匿名方法

    lambda表达式其实就是匿名方法的变体或者说简写. 原来我们用 delegate void Del(int x); Del d = delegate(int x) { return x + 1; } ...

  3. 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

  4. 委托学习过程及委托、Lambda表达式和匿名方法的关系总结及事件总结

    第一章,当开始学习委托的时候,我们会问什么是委托?为什么要学习委托? 一,什么是委托? 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法, ...

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

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

  6. .net学习之新语法学习(匿名类和匿名方法,扩展方法,系统内置委托,Lambda表达式和linq等)

    1.自动属性 Auto-Implemented Properties 2.隐式类型 var  var变量不能作为全局变量使用,因为不能在编译时确定类型 3.参数默认值 和 命名参数 4.对象初始化器 ...

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

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

  8. 匿名函数、委托和Lambda表达式

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

  9. [深入学习C#] 匿名函数、委托和Lambda表达式

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

随机推荐

  1. 51Testing招聘软件测试课程研发人员

    最近有些两三年测试工作经验的小伙伴对自己的下一个工作有些迷茫,感觉很难有技术的突破,毕竟公司不是学校,不会允许员工海阔天空的去尝试各种新的技术.现在我就送给这些好学上进的小伙伴一个礼物,51Testi ...

  2. Windows作业

    1.什么是Windows作业 Windows作业实际上一个进程组,可以给作业设置权限,一旦进程加入到作业内,进程的权限将会被作业限制. 2.创建一个作业 HANDLE CreateJobObject( ...

  3. linux 内核驱动加载过程中 向文件系统中的文件进行读写操作

    utils.h 文件: #ifndef __UTILS_H__ #define __UTILS_H__ void a2f(const char *s, ...); #endif utils.c 文件: ...

  4. 12个强大的Web服务测试工具

    在过去的几年中,web服务或API的普及和使用有所增加. web服务或API是程序或软件组件的集合,可以帮助应用程序进行交互或通过形成其他应用程序或服务器之间的连接执行一些进程/事务处理.基本上有两种 ...

  5. hdoj 2899 Strange fuction【二分求解方程】

    Strange fuction Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  6. 银联手机支付(.Net Csharp),3DES加密解密,RSA加密解密,RSA私钥加密公钥解密,.Net RSA 3DES C#

    前段时间做的银联支付,折腾了好久,拼凑的一些代码,有需要的朋友可以参考,本人.Net新手,不保证准确性! 这个银联手机支付没有SDK提供,技术支持也没有.Net的,真心不好搞! RSA加解密,这里有个 ...

  7. myeclipse svn

    打开myeclipse的help---install from site 点击add弹出对话框 在输入框中输入对应内容 http://subclipse.tigris.org/update_1.10. ...

  8. tableview: 实现tableview 的 section header 跟随tableview滑动

    方法一:(只有一个headerView)一段 如果你的tableview恰好只有一个headerView,实现这种效果就好办了.把要设置的headerView设置成tableView的header而不 ...

  9. UltraISO对光盘镜像的常用操作

    UltraISO,它能直接编辑光盘映像或者从光盘映像文件里面提取文件:可以从CD-ROM里面制作光盘映像:也可以把硬盘上的文件制作成ISO文件:可以把ISO中启动信息保存下来,也可以为ISO添加启动功 ...

  10. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(30)-本地化(多语言)

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(30)-本地化(多语言) 我们的系统有时要扩展到其他国家,或者地区,需要更多的语言环境,微软提供了一些解决 ...