建议31:在LINQ查询中避免不必要的迭代
无论是SQL查询还是LINQ查询,搜索到结果立刻返回总比搜索完所有的结果再将结果返回的效率要高。
示例代码:
class MyList : IEnumerable<Person> { //为了演示需要,模拟了一个元素集合 List<Person> list = new List<Person>() { new Person(){ Name = "Mike", Age = 20 }, new Person(){ Name = "Mike", Age = 30 }, new Person(){ Name = "Rose", Age = 25 }, new Person(){ Name = "Steve", Age = 30 }, new Person(){ Name = "Jessica", Age = 20 } }; /// <summary> /// 迭代次数属性 /// </summary> public int IteratedNum { get; set; } public Person this[int i] { get { return list[i]; } set { this.list[i] = value; } } #region IEnumerable<Person> 成员 public IEnumerator<Person> GetEnumerator() { foreach (var item in list) { //每遍历一个元素就加1 IteratedNum++; yield return item; } } #endregion #region IEnumerable 成员 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } class Person { public string Name { get; set; } public int Age { get; set; } }
针对上述集合,返回年龄等于20的第一个元素。下面有两个查询模式,我们来考虑哪一个效率更高。
//第一种 var temp = (from c in list where c.Age == 20 select c).ToList(); //第二种 var temp2 = (from c in list where c.Age >= 20 select c).First();
通常我们会认为第一种的效率会更高一些,因为它似乎返回的就是等于20的那两个元素,而第二种模式则需要查询所有大于等于20的元素。实际上并不是这样的。看下面测试代码:
MyList list = new MyList(); var temp = (from c in list where c.Age == 20 select c).ToList(); Console.WriteLine(list.IteratedNum.ToString()); list.IteratedNum = 0; var temp2 = (from c in list where c.Age >= 20 select c).First(); Console.WriteLine(list.IteratedNum.ToString());
输出:
5
1
注意:第二次查询仅仅迭代1次,因为20正好在List的首位。First方法实际完成的工作是:搜索到满足条件的第一个元素,就从集合返回。如果一个集合包含了很多元素,那么这种查询会为我们带来可观的时间效率。
与First方法类似的还有Take方法,Take方法接收一个整型参数,然后我们返回该参数指定的个数。与First一样,它在满足条件后,会从当前的迭代过程中直接返回,而不是等待整个迭代过程完毕再返回。如果一个集合包含了很多的元素,那么这种查询会为我们带来可观的时间效率。
注意下面的例子:
MyList list = new MyList(); var temp = (from c in list select c).Take(2).ToList(); Console.WriteLine(list.IteratedNum.ToString()); list.IteratedNum = 0; var temp2 = (from c in list where c.Name == "Mike" select c).ToList(); Console.WriteLine(list.IteratedNum.ToString());
输出为:
2
5
虽然LINQ查询的最后的结果都是返回两个元素“Mike”对象,但是,实际上,使用Take方法仅仅为我们迭代了2次,而使用where查询方式带来的却是整个集合的迭代。
在实际编码过程中,要充分运用First和Take等方法,这样才能为我们的应用带来高效性,而不会让时间浪费在一些无效的迭代中。
转自:《编写高质量代码改善C#程序的157个建议》陆敏技