C#学习笔记-LINQ
罗朝辉(http://www.cnblogs.com/kesalin/)
《C#与.NET高级程序设计》读书笔记
1,LINQ(语言级集成查询)的意图是提供一种统一且对称的方式,让程序员在广义的数据上得到和操作数据。通过使用LINQ,我们能够在C#编程语言内直接创建被称为查询表达式的实体。这些查询表达式是基于许多查询运算符的,而且有意设计成类似SQL表达式的,但它可以被用来与多种数据交互,而不局限于关系数据库。具体来说,LINQ允许查询表达式以统一的方式来操作任何通过扩展方法直接或间接实现了 IEnumerable<T>接口的对象,关系数据库,Dataset或XML文档。
static void QueryOverString()
{
string[] Games = {"Morrowind", "BioShock", "Half Life 2", "The Darkness"};
// 建造一个查询表达式,来代表数组中多于6个字母的项。
IEnumerable<string> subSet = form g in Games where g.Length > 6 orderby g select g;
// 输出结果
foreach (string s in subset)
Console.WriteLine("Item: {0}", s);
}
2,LINQ表达式是强类型和可扩展的。在上面的例子中 subSet 的类型是确定的,即便我们是使用 var 来修饰它。LINQ表达式在我们迭代内容之前,不会真正进行运算,这个特性被叫做延迟执行,该特性的好处在于可以为相同的容器多次应用相同的LINQ查询,而始终可以获得最新的最好的结果。如果要立即执行的话,可以调用由Enumerable类型定义的许多扩展方法来完成,如:ToArray,ToDictionary,TKey以及ToList等扩展方法,它们允许我们以强类型容器来捕获LINQ查询结果。
3,LINQ的查询运算符是设计用于任何实现了IEnumerable<T>接口的类型的,无论是直接地还是通过扩展方法间接实现的。但System.Collections中传统的非泛型容器类却没有这些结构,我们可以用泛型Enumerable.OfType<T>方法来对包含在这些非泛型集合里的数据进行迭代操作。因为非泛型类型可以包含任何类型的项,所以我们还可以通过OfType<T>来过滤调与需要迭代操作所指定类型不同的元素。
ArrayList strs = new ArrayList() {"one", "two", "three"};
// 把ArrayList转换成一个兼容于IEnumerable<T>的类型
IEnumerable<string> strEnum = strs.OfType<string>();
// 建立查询表达式
var longStr = from str in strs where str.Length > 2 select str;
4,上面的例子中查询运算符(如:from,in,where,orderby和select),C#编译器实际上是把这些标记翻译成了对System.Linq.Enumerable类型的各种方法的调用(也可能是其他类型,这取决于LINQ查询)。实际上,Enumerable的许多方法的原型都是把委托昨晚参数,特别是许多方法都要求一个定义在System.Core.dll的System命名空间中类型为Func<>的泛型委托作为参数,该委托的代理最多可以接受4个输入参数。我们可以手工创建一个新的委托类型,编写所需要的目标方法,并使用C#的匿名方法,或者也可以定义一个合适的Lambda表达式来替代查询表达式。如上例可改写为:
ArrayList strs = new ArrayList() {"one", "two", "three"};
// 把ArrayList转换成一个兼容于IEnumerable<T>的类型
IEnumerable<string> strEnum = strs.OfType<string>();
// 建立查询表达式
var longStr = strs.Where(str => str.Length > 2).Select(str => str);
或者
ArrayList strs = new ArrayList() {"one", "two", "three"};
// 把ArrayList转换成一个兼容于IEnumerable<T>的类型
IEnumerable<string> strEnum = strs.OfType<string>();
// 使用 Enumerable 类型和匿名方法来建立查询表达式
// 使用匿名方法建立所需的Func<>委托
Func<string, bool> filter = delegate(string str) { return str.Length > 2;};
Func<string, string> itemToProcess = delegate(string s) { return s;};
// 把委托传递给Enumerable的方法
var subset = strs.Where(filter).Select(itemToProcess);
5,LINQ查询运算符有:from,in,where,select,join,on,equals,into,orderby,ascending,descending,group,by等。此外,Enumerable类型还提供了一套没有直接的查询运算符简化符号,而且是以扩展方法呈现的方法,可以调用这些泛型方法以各种方式来对一个结果集进行转换(Reverse<>(), ToArray<>(), ToList<>()等);或对结果集进行操作(Distinct<>(), Union<>(), Intersect<>()等);或对结果集进行聚合操作(Count<>(), Sum<>(), Min<>(), Max<>()等)。比如:
// 获取从查询获得的总数
int numb = (from str in strs where str.Length > 2).Count<string>();
6,我们也可以从现有的数据源投影出新的数据形式,具体做法是定义一个select语句,通过匿名类型动态地形成新的类型。
class Car
{
public string PetName = string.Empty;
public string Color = string.Empty;
}
Car[] myCars = new [] {
new Car { PetName = "Henry", Color = "White"},
new Car { PetName = "Daisy", Color = "Tan"},
new Car { PetName = "Mary", Color = "Black"},
};
// 查询子集
var onlyBlack = from c in myCars where c.Color = "Black" select c;
// 投影新数据类型
var names = from c in myCars select new {c.PetName};
foreach (var o in names)
Console.WriteLine(o.PetName);
使用投影技术有一个问题,我们不能把投影出的新数据当作返回值,因为它是隐式类型的(var names),因为我们必须把它转换为强类型数据(如Array)再返回,如果我们要返回上例中的names,我们可使用 return names.ToArray()。