对linq查询结果进行迭代时,linq才去执行查询,就是说{在迭代时,才真正执行}.这是延迟操作的一种性能提升的表现.
测试结果如下图:
但试想下,需要对结果进行第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();