LINQ to Objects
通过LINQ to Objects 可以使用 LINQ 直接查询
任何 IEnumerable<T> 或 IQueryable<T> 集合(例如string[]、 int[] 和 List<T>),
而不需要使用直接的 LINQ 提供程序或 API。
一、LINQ to Objects
假设有一个类型为 string 的数组,该数组包含姓名列表。
下面的程序输出该字符串数组中以字符G开头的所有姓名:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Demo_LINQ { class Program { static void Main(string[] args) { string[] allNames = new string[]{ "Jeffrey", "Kirby", "Gabriel", "Philip", "Ross", "Adam", "Alston", "Warren", "Garfield"}; foreach (string str in allNames) { if (str.StartsWith("G")) Console.WriteLine(str); } Console.ReadKey(); } } }
使用 LINQ to Objects,可以按照如下代码所示重写该程序:
namespace Demo_LINQ { class Program { static void Main(string[] args) { string[] allNames = new string[]{ "Jeffrey", "Kirby", "Gabriel", "Philip", "Ross", "Adam", "Alston", "Warren", "Garfield"}; IEnumerable<string> foundNames = from name in allNames where name.StartsWith("G") select name; foreach (string str in foundNames) Console.WriteLine(str); Console.ReadKey(); } } }
IEnumerable<string> foundNames = from name in allNames where name.StartsWith("G") select name;
该示例中查询结果是:IEnumerable<string>,当然也可一使用隐式类型化功能让编译器自动推断类型,如:
var foundNames = from name in allNames where name.StartsWith("G") select name;
在使用更为复杂的筛选时,LINQ 的使用性就更加明显,如:
var foundNames = from name in allNames where name.StartsWith("G") && name.EndsWith("d") select name;
下面是以整数数组,用 LINQ 检索出奇数,并按降序排列:
int[] nums = { 12, 34, 10, 3, 45, 6, 87, 49, 23, 32 }; var oddNums = from n in nums where (n % 2 == 1) // 检索奇数 orderby n descending // 降序排列 select n; foreach (int n in oddNums) Console.WriteLine(n);
然后可以使用 以下方法检索出奇数的个数:
int oddCount = oddNums.Count();
也可以将结果转换为 int 数组:
int[] oddNumsArray = oddNums.ToArray();
二、查询语法与查询方法的对比以及拉姆达表达式
上面使用的 LINQ 查询 都使用了查询语法,如下:
var oddNums = from n in nums here (n % 2 == 1) // 检索奇数 orderby n descending // 降序排列 select n;
除了使用查询语法之外,也可以使用方法语法:
var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);
(n => n % 2 == 1)
称为拉姆达表达式,=> 是拉姆达运算符,读作“ goes to(转到)”,
可以将这个拉姆达表达式认为是如下函数:
static bool function(int n) { return ( n %2 == 1); }
可以使用方法语法重写前面的字符串数组示例,如下:
var foundNames = allNames.Where( name => name.StartWith("G") && name.EndWith("l"));
在这两种语法中,应该如何选择使用何种语法?
<1> 方法语法和查询语法之间没有性能上的区别。
<2> 查询语法更容易阅读,因此尽可能使用这种语法。
<3> 在没有相等的查询语法的情况下使用方法语法。
例如, Count 和 Max 方法就没有相等的查询语法。
三、延迟查询执行 与 强制立即执行查询
1、延迟查询执行
var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);
该语句中 oddNum s变量并没有存储满足条件的查询结果,该变量只存储了查询(可以理解为有了方法,到位执行,当然没有结果了);
只有在遍历查询变量时,才会执行该查询。如执行以下语句时,才执行查询,返回结果:
foreach (int n in oddNums) Console.WriteLine(n);
这种概念成为延迟查询。意义在于:仅建立一个查询,每次执行该查询时都会获得最新的结果!
为了证明延迟查询,下面程序首先定义一个查询,然后用 foreach 遍历输出结果,
将数组中的每个元素都加20,然后再次致谢 foreach 循环。
namespace Demo_LINQ { class Program { static void Main(string[] args) { int[] nums = { 12, 34, 10, 3, 45, 6, 87, 49, 23, 32 }; var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n); Console.WriteLine("First execution"); foreach (int n in oddNums) Console.WriteLine(n); for (int i = 0; i < 10; i++) nums[i] += 20; Console.WriteLine("\nSecond execution"); foreach (int n in oddNums) Console.WriteLine(n); Console.ReadKey(); } } }
返回的结果为:
First execution
87
49
45
23
3
Second execution
107
69
65
43
23
两次结果不同,证明了查询直到被访问时才被执行!
2、强制立即执行查询
强制立即执行查询的一种方法是将查询结果显示的转换为 List 对象即可!
var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n);
把该语句修改为:
var oddNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n).ToList();
你会发现两次 foreach 循环的结果是相同的!
四、LINQ 和匿名类型
先看下面的代码:
namespace Demo_LINQ { // 定义 Contact 类 public class Contact { public int id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } class Program { static void Main(string[] args) { // 假设有包含 Contact 对象的列表,如下所示: List<Contact> Contacts = new List<Contact>() { new Contact() {id=1, FirstName = "Jhon", LastName = "Chen"}, new Contact() {id=2, FirstName = "Maryann", LastName = "Chen"}, new Contact() {id=3, FirstName = "Richard", LastName = "Wells"} }; // 可以用 LINQ 查询姓为 Chen 的所有联系人: IEnumerable<Contact> foundContacts = from c in Contacts where c.LastName == "Chen" select c; // foundContacts 对象的类型是 IEnumerable<Contact> // 可以用 foreach 循环输出所有姓 “Chen”的联系人 foreach (var c in foundContacts) Console.WriteLine("{0} - {1} {2}", c.id, c.FirstName, c.LastName); Console.ReadKey(); } } }
然而,可以修改查询以使结果形成自定义类而非 Contac 类型。如下:
var foundContacts = from c in Contacts where c.LastName == "Chen" select new { id = c.id, Name = c.FirstName + " " + c.LastName }; foreach (var c in foundContacts) Console.WriteLine("{0} - {1}", c.id, c.Name);
值得注意的是:必须使用关键字 var 让编译器自动推断 foundContacts 的类型;
因为结果是当前定义的匿名类型。