LINQ(Language Integrated Query)
Lambda表达式
格式
Lambda表达式比函数有着更为简洁的语法格式。在某些情况下使用Lamnbda就可以替代函数,让代码变得更简洁易读。而定义Lambda表达式与定义函数其实差别不大,而且还做到了简化。
定义函数你得声明函数的权限修饰符、形态修饰符和参数列表、返回类型等,而Lambda就这么一段代码就做完了函数的工作
e:参数
=>:操作符,可描述执行后面的逻辑……操作符后面的则是单个表达式,也可以是语句块。即函数的方法体。
示例
e => e.Name == "sam"; // 隐式声明了参数 操作符后面是求值表达式
e => { return e.Name == "sam"; } // 隐式声明了参数 操作符后面是求值语句块
(string e, string x)=>{ return e.Name == "sam" && e.Age = 32; } //显示声明了多参数 操作符后面是求值语句块
(e, x) => { return e.Name == "sam" && e.Age = 32; } //隐式声明了多参数 操作符后面是求值语句块
()=>Console.WriteLine(); //无参数 操作符后面是执行语句 返回void
Lambda与委托
Lambda就是一个匿名的函数,所以你可以将Lambda当成委托实例作为参数进行传递,如果要这样做,则你定义的Lambda表达式的参数和返回值类型必须符合委托的参数和返回类型。
{
static void ShowTime(Func<DateTime> d) //接收一个泛型委托
{
string time= d().ToString();
Console.WriteLine(time);
}
static void Main(string[] args)
{
//定义一个返回时间的Lambda表达式
//因为Lambda表达式就是函数的简洁版,所以它也可以是委托类型,以便将它当做参数传递
//使用内置Func<T>泛型委托是因为这种委托被设计成可以有返回值,这符合Lambda的需求,它返回一个时间
//使用Action委托就不可以了,因为Action委托被设计成不能有返回值,而Lambda又需要返回一个值
Func<DateTime> GetDateTime = () => DateTime.Now;
ShowTime(GetDateTime);
}
}
LINQ的扩展方法(LINQ操作符)
Enumerable类为IEnumrable<T>接口扩展出了一系列的方法,这些方法为实现了IEnumrable<T>接口的泛型集合提供了查询、排序、提取等操作,也将这些扩展方法称为LINQ操作符。
LINQ延迟查询机制
LINQ查询操作符并不会立即执行,它总是在查询结果集真正被使用的时候才会开启查询。 而且每次只将迭代的当前项存入内存中处理。这就降低了内存占用率。
{
Console.WriteLine("执行查询:" + n);
return n;
}
static void Main(string[] args)
{
double[] array = { 100, 5, 8,900 };
//延迟查询机制保证下面的查询并赋值的操作不会执行,直到records真正被使用的时候才会开启查询
var records = from n in array
select Square(n);
//想象一下假设有上千条数据需要被查询出来放进records,可是程序并不知道你在后来有没有使用过records
//如果你根本不使用records,那么查询就没有意义,如果你真的没有使用它,那么上千条记录读进内存就是在浪费资源
//所以查询并不会真的开启直到你确实使用了records时,查询才会开始运作
//records被迭代时才会自动打开查询
foreach (var r in records) { }
}
但警惕聚集计算操作符和某些需要一次性统计所有集合的操作符如OrderBy、Reverse、ToList、ToArray、ToDictionary,它们都会破坏延迟查询,通常情况下,使用Linq操作符返回的结果如果不是IEnumerable<T>类型则说明该查询会立即执行,这会造成一次性将所有集合项存入内存以便处理。这就是为什么很多人说LINQ查询的性能不高的根本原因。
LINQ查询表达式
LINQ查询表达式类似于SQL查询语句,但个人不喜欢它的书写格式。它是由from、join group by等句子组成的LINQ查询表达式,而LINQ查询表达式与直接使用LINQ扩展方法执行查询在语法结构上是不一样的,我认为后者更易于阅读,所以此处略过。
LINQ操作符
1.过滤
Where():对泛型集合项进行筛选。此方法要求一个Fun<T,Key>的泛型委托,它会自动迭代集合项,每迭代一次将调用泛型委托,并把集合中的一条记录作为参数传递给委托(Lambda),Where()方法期待该委托返回一个布尔值,为真时,将对象保留,否则排除。言简意赅地说,此方法是用于减少集合的行数。此方法所要求的委托还可以有第三个参数:Index,表示当前对象在集合中的索引号。
ElementAt():根据参数索引查找处于该索引处的项,类似的ElementAtOrDefault()保证在该索引处没有项时将安全返回一个default<T>
Single() :返回集合中的唯一的项或提供一个过滤的参数,类似的FirstOrDefault()保证在集合中如果没有项则安全返回一个default<T>,两个方法都将在此情况下引发异常:如果集合存在多个项
First():返回集合中的第一个项或提供一个过滤的参数,如果集合为空则引发异常,类似的FirstOrDefault()保证在集合中如果没有项则安全返回一个default<T>
Last():返回集合中的最后一个项或提供一个过滤的参数,如果集合为空则引发异常,类似的LastOrDefault()保证在集合中如果没有项则安全返回一个default<T>
2.投影
Select():指定需要查询的项的成员以便将它们组成新的结果集返回。言简意赅地说,此方法是用于减少集合的列数。
SelectMany():指定需要查询的项的集合成员以便将它们组成新的结果集返回。主要应用于一个对象的属性是一个子集合时,将取出子集合并组成新的结果集返回。此方法所要求的委托还可以有第三个参数:Index,表示当前对象在集合中的索引号。
var records = SampleData.books.Where(b => b.Title.Length > 10 ).SelectMany( b => b.Authors ); //Authors是book的属性,是一个包含n个属性的集合
3.去重
Distinct():去除集合中的重复项并返回去除后的结果集。此方法内部维护了一个测试相等性的比较器,如果项是结构或字符串类型,则比较所有项的值以确认项是否相等,如果项是引用类型,则比较所有项在堆上的引用地址以确认项是否相等。可是多半在执行查询时查询的是对象,对象多半都是不相等的堆引用,所以根本没法去重,别急!此方法还有重载,重载版可实现自定义的测试相等性的规则,重载版需要第二参数,参数是一个IEqualityComparer<T>泛型接口,也即你必须实现这个接口从而实现自定义的比较器。
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Web.UI;
using System.IO;
namespace Yin.General
{
/// <summary>
/// 常用的方法
/// </summary>
public static class LinqHelper<T>
{
#region 公共方法
public static IEqualityComparer<T> CreateComparer<TV>(Func<T, TV> keySelector)
{
return new CommonEqualityComparer<TV>(keySelector);
}
public static IEqualityComparer<T> CreateComparer<TV>(Func<T, TV> keySelector, IEqualityComparer<TV> comparer)
{
return new CommonEqualityComparer<TV>(keySelector, comparer);
}
#endregion
#region Nested type: CommonEqualityComparer
private class CommonEqualityComparer<TV> : IEqualityComparer<T>
{
private readonly IEqualityComparer<TV> _comparer;
private readonly Func<T, TV> _keySelector;
public CommonEqualityComparer(Func<T, TV> keySelector, IEqualityComparer<TV> comparer)
{
_keySelector = keySelector;
_comparer = comparer;
}
public CommonEqualityComparer(Func<T, TV> keySelector)
: this(keySelector, EqualityComparer<TV>.Default)
{ }
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return _comparer.Equals(_keySelector