当前位置: 首页 > 图文教程 > 网络编程 > ASP.NET > LINQ学习笔记:Lambda表达式

ASP.NET
C# Quoted-Printable编码、解码
asp.net Google的translate工具翻译 API
.NET读取所有目录下文件正则匹配文本电子邮件
asp.net 半角全角转化工具
AjaxControlToolKit 显示浏览者本地语言的方法
asp.net HTML文件上传标签
ASP.NET 绑定DataSet中的多个表
ASP.NET 重定向的几种方法小结
Asp.Net 重定向必须要知道的一些资料
ASP.NET 导出到Excel时保留换行的代码
Asp.Net Cache缓存使用代码
document.getElementsByName和document.getElementById 在IE与FF中不同实现
GridView单元格合并
asp.net 大文件上传控件
asp.net 日期函数 某月的第一天和最后一天的日期
asp.net 时间类 一周的周一和周末的日期
C# javaScript函数的相互调用
asp.net membership 密码重设
"虚拟路径"..."映射到另一个应用程序,这是不允许的!
如何传值在2个页面之间 要求不刷新父页面,并且不能用Querystring传值

ASP.NET 中的 LINQ学习笔记:Lambda表达式


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-28   浏览: 227 ::
收藏到网摘: n/a

1. 查询操作链

为了构造更加复杂的查询, 我们可以增加而外的查询操作方法, 形成一个查询链. 例如下面的例子:

 1: string[] names = { "James","Jack","Harris"};
 2:  
 3: IEnumerable<string> query = names.Where(n => n.Contains ("a")).OrderBy (n => n.Length).Select (n => n.ToUpper( ));
 4:  
 5: foreach (string name in query)
 6: Console.Write (name + ","); //JACK,JAMES,HARRIS,

Where, OrderBy和Select都是标准的查询操作并且对应于Enumerable类中的扩展方法. Where会生成一个输入序列的过滤版本, OrderBy则生成一个输入序列的排序版本, Select方法生成一个新的序列, 此序列中的每个元素都是根据给定的lambda表达式进行了转换或者返回. 整个数据流程是从左到右, 因此数据将先被过滤, 然后排序, 最后才被返回.

一个查询操作不会去改变输入序列, 相反的, 它总是返回一个新的序列. 这点上LINQ受到了函数式编程的影响, 并且保持一致的风格.

当查询操作被串联在一起的时候, 每一个操作生成的输出序列将会被作为下一个操作的输入序列. 因此我们也可以使用另外一种渐进式的查询写法:

 1: var filtered = names.Where(n => n.Contains ("a"));
 2: var sorted = filtered.OrderBy(n => n.Length);
 3: var finalQuery = sorted.Select(n => n.ToUpper()); 

最终我们便利finalQuery得到的结果将会跟上面的例子是一样的:

 1: foreach (string name in finalQuery)
 2: Console.Write (name + ","); // JACK,JAMES,HARRIS,

2. 编写Lambda表达式

让我们看一下一个最简单的Lambda表达式,n => n.Contains(“a” ) , 在这个表达式中,输入类型是string, 输出类型是bool. 如果一个Lambda表达式返回一个bool值的话我们将它称为一个判定(predicate). Lambda表达式的目的是取决于特定的查询操作符. 例如如果是Where操作符的话, 它指示一个元素是否将会被包含在输出序列中. 而如果是OrderBy操作符的话, Lambda表达式将会把输入序列中的每一个元素映射到它的排序键上. 如果是Select操作符, Lambda表达式将决定输入序列中的每个元素在被放入输出序列之前应该如何转换. 在一个操作中包含的Lambda表达式总是应用于输入序列中的每一个单一元素, 而不是整个序列.

由于Lambda表达式是一个callback, 我们可以在其中放入我们自己的逻辑, 这令查询操作非常的强大, 并且简单. 我们可以看一下Enumerable.Where的实现, 忽略异常处理:

 1: public static IEnumerable<TSource> Where<TSource> (
 2: this IEnumerable<TSource> source,
 3: Func<TSource,bool> predicate)
 4: {
 5: foreach (TSource element in source)
 6: if (predicate (element))
 7: yield return element;
 8: }

2.1 Lambda表达式和Func标记

在标准的查询操作符中利用了范型Func代理, 它们存在于System.Linq中, 并且定义如下

Func中出现的参数顺序类型跟Lambda表达式中出现的一致. 因此, Func<TSource,bool>匹配一个TSource => bool的Lambda表达式, 它接受一个TSource类型的参数并且返回一个布尔值. 类似的, Func<TSource, TResult>匹配一个TSource => TResult的Lambda表达式. 以下是所有的Func代理定义(注意返回值的类型总是最后一个参数的类型):

delegate TResult Func <T> ( );

delegate TResult Func <T, TResult> (T arg1);

delegate TResult Func <T1, T2, TResult> (T1 arg1, T2 arg2);

delegate TResult Func <T1, T2, T3, TResult> (T1 arg1, T2 arg2, T3 arg3);

delegate TResult Func <T1, T2, T3, T4, TResult> (T1 arg1, T2 arg2, T3 arg3, T4 arg4);

2.2 Lambda表达式和元素命名

标准的查询操作使用以下的类型命名:

TSource: 输入序列中的元素类型

TResult: 输出序列中的元素类型 – 如果它是不同于TSource的话

TKey: 用于sorting, grouping, join中的key类型

TSource是由输入序列决定的, 而TResult和Tkey则由Lambda表示式来推断. 例如, 考虑以下的Select操作:

 1: static IEnumerable<TResult> Select<TSource,TResult> (
 2: this IEnumerable<TSource> source,
 3: Func<TSource,TResult> selector)

Func<TSource,TResult>匹配一个TSource=>TResult的Lambda表达式, 一个对应于输入元素, 另一个则对应于输出元素. TSource和TResult是不同的类型, 因此Lambda表达式可以更改每一个元素的类型. 更进一步, Lambda表达式决定了输出序列的类型. 例如:

 1: string[] names = { "James","Jack","Harris"};
 2: IEnumerable<int> query = names.Select (n => n.Length);
 3: foreach (int length in query)
 4: Console.Write (length); // 546

3. 自然排序

针对一个输入序列的排序功能在LINQ中是非常重要的. 一些操作符, 例如Take, Skip和Reverse都依赖于这个行为. Take操作符输出头x个元素, 而排除剩余的, Skip操作符则忽略了头x个元素, 输出所有余下的. Reverse操作符将一个序列中的元素进行反转.

4. 其他操作符

并非所有的操作都回返回一个序列, 针对于元素的操作符都只返回一个元素, 例如First, Last, Single, ElementAt等等:

 1: int[] myNumbers = { 7,6,5,4,3};
 2: int fn = myNumbers.First(); // 7
 3: Console.WriteLine(fn);
 4: int ln = myNumbers.Last(); // 3
 5: Console.WriteLine(ln);
 6: int sn = myNumbers.ElementAt(1); // 6
 7: Console.WriteLine(sn);

另外聚合操作符则返回一个单一值:

 1: int count = myNumbers.Count(); // 5;
 2: Console.WriteLine(count);
 3: int min = myNumbers.Min(); // 3;
 4: Console.WriteLine(min);

另外Contains,Any则返回bool值. 这些操作符并不返回一个集合, 因此他们不能在他们的结果集中再次调用其它的操作, 换句话说他们必须出现在整个查询链中的最后一个(或者是子查询.) 另外有些操作符接受两个输入序列, 例如Contact, 它将会将一个序列添加到另外一个当中去, 而Union类似, 区别是它会自动删除其中重复的元素. 待续!