C# 枚举性能
考虑以下代码示例:C# 枚举性能,c#,performance,enumeration,C#,Performance,Enumeration,考虑以下代码示例: IEnumerable<Y> myEnumeration = *some linq stuff into a datatable* If (myEnumeration.Count() > X) { foreach(Y bla in myEnumeration) { // do something } } IEnumerable myEnumeration=*将一些linq内容放入数据表中* If(myEnumera
IEnumerable<Y> myEnumeration = *some linq stuff into a datatable*
If (myEnumeration.Count() > X)
{
foreach(Y bla in myEnumeration)
{
// do something
}
}
IEnumerable myEnumeration=*将一些linq内容放入数据表中*
If(myEnumeration.Count()>X)
{
foreach(Y bla在我的计算中)
{
//做点什么
}
}
这会导致枚举2次吗?计数呼叫+foreach?如果是这样,有没有办法避免其中一种枚举
提前谢谢
编辑了myEnumeration.Count->myEnumeration.Count()(扩展方法)如果
Count
不是属性,而是方法可枚举的.Count
(正如我所期望的那样)。在这种情况下,我的建议是事先将其列入一份清单:
List<Y> myList = *some linq stuff into a datatable*.ToList();
if (myList.Count > X)
foreach (Y bla in myList)
//do something
List myList=*将一些linq内容放入数据表*.ToList();
如果(myList.Count>X)
foreach(myList中的Y bla)
//做点什么
它将为您生成两个查询:
SELECT COUNT(*) FROM sometable
及
这不是一次也不是两次。这将是两个独立的请求,但只有第二个请求将提取数据。第一个只会返回数字,速度会快得多。如果希望在一个请求中发生这种情况,请在查询结束时使用
.ToList()
。然后它将浏览列表。您也可以在SQL分析器中检查这一点。请记住,在某些情况下,双查询方法可能更安全,因此您不会突然开始提取数百万行。我将此代码放到LinqPad中,以显示生成的SQL:
IEnumerable<MyTable> myEnumeration = MyTable;
if (myEnumeration.Count() > 1)
{
foreach(MyTable bla in myEnumeration)
{
// do something
}
}
所以是的,数据将从数据库中检索两次。
考虑
List<Y> myEnumeration = *some linq stuff into a datatable* **.ToList();**
List myEnumeration=*将一些linq内容放入数据表***.ToList()**
是的,这将给您两个数据库调用Count()
将执行如下查询:
SELECT COUNT(1) FROM Table WHERE Blah
然后GetEnumerator()
将执行查询,该查询将获取所有必需字段:
SELECT Id, Foo, Bar FROM Table WHERE Blah
其实没有一个正确的答案。你应该考虑:
- 通常得到的结果数量(是数百万个实体,还是几十个实体)
- 所需实体的数量(是几个实体还是数百个实体)
- 更常见的情况是结果集中是否存在所需的实体数
- 是真正的性能问题,还是每周调用一次此方法
- 如果不是性能问题,那么只需进行两次数据库调用
- 若返回的项的数量不是很大,而且它们很可能包含所需数量的项,那个么只需将查询转储到列表中即可
- 若项目数量相当大,并且您不想将它们全部转储,那个么您可以使用下面的扩展方法,在不将所有序列保存到列表的情况下检查resultset中是否至少有N个项目。但是在这里,你应该考虑什么是更快的——倒N个需要的项目,或者做数据库调用来检查项目计数。
public static IEnumerable<T> TakeIfMoreThan<T>(
this IEnumerable<T> source, int count)
{
List<T> buffer = new List<T>(count);
using (var iterator = source.GetEnumerator())
{
while (buffer.Count < count && iterator.MoveNext())
buffer.Add(iterator.Current);
if (buffer.Count < count)
{
yield break;
}
else
{
foreach (var item in buffer)
yield return item;
buffer.Clear();
while (iterator.MoveNext())
yield return iterator.Current;
}
}
}
因此,您不需要将所有查询结果转储到内存列表中。您将使用单个数据库调用(但它将查询所有项目字段)。如果结果少于要求的数量,则不会枚举项目。1。
Count
不应该是Count()
?2.您可以随时查看sql profiler以查看数据是否在两次检索中,只需询问:为什么需要检查.Count()
?当我面临此问题时,我倾向于在将其拉入myEnumeration时调用ToArray()。从那一刻起,你就知道它在内存中,而且只需点击一次DB。是的,意思是count()…扩展方法…谢谢如果数据库中有一百万行呢Count()
单独(没有.ToList()
)无法检索到所有的数据,仅仅是它们的数量。@AndriusNaruševičius你有什么资源可以描述EF是如何做到这一点的吗?@AndriusNaruševičius如果他使用EF,他将使用IQueryable
,而不是inumerable
。不,只有调试时的体验。当然,我可能错了。耶,我想我会这样做的。我的意思是count()-当然是扩展方法。谢谢!如果我使用字典而不是数据表呢。和一个linq语句来访问字典中的一些数据。这是一样的,对吗?不,这不一样。我的答案不是关于内存操作。如果MyTable或Y是内存中的对象,我不建议调用ToList(),因为这样会进行不必要的强制转换。但是,通过网络从数据库中两次提取数据并在内存中工作是一个巨大的区别。好吧,但是如果我使用复杂的linq语句从字典中检索数据呢?然后,我将对复杂的linq语句进行两次枚举,可能还会出现性能问题。只是想知道。如果是字典,你还得记两次。我的回答只是说明了如何避免两次从数据库中提取数据。但当您每次强制转换或调用foreach时,数据将被枚举。但是,除非你有困难,否则不要把时间浪费在这些记忆中的事情上(但这只是我的观点)。
SELECT Id, Foo, Bar FROM Table WHERE Blah
public static IEnumerable<T> TakeIfMoreThan<T>(
this IEnumerable<T> source, int count)
{
List<T> buffer = new List<T>(count);
using (var iterator = source.GetEnumerator())
{
while (buffer.Count < count && iterator.MoveNext())
buffer.Add(iterator.Current);
if (buffer.Count < count)
{
yield break;
}
else
{
foreach (var item in buffer)
yield return item;
buffer.Clear();
while (iterator.MoveNext())
yield return iterator.Current;
}
}
}
foreach(Y bla in myEnumeration.TakeIfMoreThan(X))
{
// do something
}