但试想下,需要对结果进行第2次,第3次...,那么这种延迟执行,却是性能的一个瓶颈.
测试如下代码:
public IEnumerable<int> GetValue() { List<int> lst = new List<int> { 0, 1, 2 }; return lst.Where ( i => { Console.WriteLine(string.Format("where: {0}", i)); return i % 2 == 1; } ).Select( i => { Console.WriteLine(string.Format("return: {0}", i)); return i; } ); } var itor = this.GetValue(); Console.WriteLine("------第一次查询--------"); foreach (var i in itor) ; //第一次for Console.WriteLine(""); Console.WriteLine("-----第二次查询---------"); foreach (var i in itor) ; //第二次for Console.WriteLine(""); Console.WriteLine("------tolist,第三次查询--------"); var lst = itor.ToList(); //又执行了一次查询 Console.WriteLine(""); Console.WriteLine("------in list--------"); foreach (var i in lst) ; //第三次for,总算没有再进行查询
输出结果,如下图:
得出一个结论:
查询结果返回后,马上调用 ToList() 方法,那么以后再循环时就不会再去执行查询了.
可从上面的代码看, ToList() 执行一次查询,遍历了所有元素,第三次for时,后遍历了一次(虽然不是所有元素).
试想下,集合中如果有100,1000,1万个元素时...
解决思路:
在循环迭代查询结果时,便将结果保存到一个结果集合中,下次循环迭代时,从这个结果集合中进行循环迭代
class FixedEnumerable<T> : IEnumerable<T> { bool isFirst; T[] ary; public FixedEnumerable() { this.isFirst = true; } IEnumerable<T> Iterator; public FixedEnumerable(IEnumerable<T> iterator) : this() { this.Iterator = iterator; } #region IEnumerable<T> 成员 IEnumerator<T> IEnumerable<T>.GetEnumerator() { if (!this.isFirst) { foreach (var t in ary) yield return t; yield break; } if (this.isFirst) { if (this.Iterator == null) { this.isFirst = false; yield break; } List<T> lst = new List<T>(); foreach (var t in this.Iterator) { lst.Add(t); yield return t; } ary = lst.ToArray(); this.isFirst = false; this.Iterator = null; } } #endregion #region IEnumerable 成员 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return (this as IEnumerable<T>).GetEnumerator() as System.Collections.IEnumerator; } #endregion }
测试代码如下:
public IEnumerable<int> GetFixedValue() { return new FixedEnumerable<int>(this.GetValue()); } var itor = this.GetFixedValue(); Console.WriteLine("------第一次for,并查询--------"); foreach (var i in itor) { Console.WriteLine(i); } Console.WriteLine(""); Console.WriteLine("------第二次for,不再执行查询--------"); foreach (var i in itor) { Console.WriteLine(i); }
性能测试代码:
public class User { public string Name { get; set; } public bool Sex { get; set; } public DateTime Birthday { get; set; } }
public static IEnumerable<T> ToFixed<T>(this IEnumerable<T> iterator)
{
return new FixedEnumerable<T>(iterator);
}
List<User> lst = new List<.User>();
for (int i = 0; i < 1; i++)
{
lst.Add(new Classes.User { Birthday = DateTime.Now, Name = "a" + i, Sex = false });
}
var r1 = from u in lst where u.Sex == false && u.Name.StartsWith("A".ToLower()) && u.Birthday <= DateTime.Now select new { u.Sex, u.Name };
var r2 = r1.ToFixed();
int times = 3;
var sw = System.Diagnostics.Stopwatch.StartNew();
sw.Start();
for (int i = 0; i < times; i++)
{
//Console.WriteLine(i);
foreach (var u in r1)
{
var s = "a" + u.ToString();
s += "";
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < times; i++)
{
//Console.WriteLine(i);
foreach (var u in r2)
{
var s = "a" + u.ToString();
s += "";
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
sw.Reset();
以损失第一次迭代性能,提高再次迭代性能.
如果小数据量,对于LINQ来说,无所谓性能!
而对于另一种方式,可以采用如下方法
public static List<T> ToList<T>(this IEnumerable<T> iterator, Func<IEnumerable<T>, IList<object>> action) { var lst = action(iterator); return lst.Cast<T>().ToList(); }
var r3 = r1.ToList( ie => { var l = new List<object>(); foreach (var a in ie) { l.Add(a); } return l; } );
sw.Start(); for (int i = 0; i < times; i++) { //Console.WriteLine(i); foreach (var u in r3) { var s = "a" + u.ToString(); s += ""; } } sw.Stop(); Console.WriteLine(sw.Elapsed); sw.Reset();