Where().Count()与Count()的区别

3
这是我的一个继承自 List(Of T) 的类的相关部分摘录。请查看 SelectedObjectsSelectedCount 属性。我添加了 SelectedCount 是为了提高性能,但现在我想知道它是否比 SelectedObjects.Count() 更好。
Public Class DrawingObjectsList
    Inherits List(Of DrawingObjectBase)

  Friend ReadOnly Property DOsOfCurrentFace As IEnumerable(Of DrawingObjectBase)
    Get
        Return Me.Where(Function(DObj) DObj.Face = mFace)
    End Get
  End Property

  Public ReadOnly Property SelectedObjects() As IEnumerable(Of DrawingObjectBase)
    Get
        Return DOsOfCurrentFace.Where(Function(DObj) DObj.IsSelected = True)
    End Get
  End Property

  Public ReadOnly Property SelectedCount() As Integer
    Get
        Return DOsOfCurrentFace.Count(Function(DObj) DObj.IsSelected = True)
    End Get
  End Property
End Class

我的理论是,Where(predicate).Count()Count(predicate)都需要遍历列表,因此它们之间不应该有任何显著的差异。另外,由于我没有在Where()上执行任何ToList()ToArray()操作,我不确定在其上调用Count()是否能够利用内置的Count属性。
那么,我应该保留还是删除SelectedCount属性呢?

1
如果你在考虑为了性能而做某些事情是否值得,有一个明显的方法可以找出答案:测量它。 - Jon Skeet
@JonSkeet:没错,但我也想让专业人士评估一下我的理论。 - dotNET
1
那么,为什么不现在进行测量,并将其添加到问题中...同时尝试确定“Count”所花费的时间在您的应用程序中是否真正重要。 - Jon Skeet
2个回答

0

我用一些(相当幼稚的)代码计时:

Dim r = New Random()
Dim sw As Stopwatch
Dim list = Enumerable.Range(0, 100000000).Select(Function(x) r.Next)

sw = Stopwatch.StartNew()
list.Count(Function(x) x Mod 2 = 0)
Console.WriteLine(sw.ElapsedMilliseconds)

sw = Stopwatch.StartNew()
Dim x = list.Where(Function(x) x Mod 2 = 0).Count()
Console.WriteLine(sw.ElapsedMilliseconds)

我得到的结果是

Count   Where/Count
-------------------
3494    3624
3484    3612
3523    3617
3522    3609
3500    3623
3493    3631
3536    3620
3541    3682
3621    3633
3515    3686   

平均而言,使用Where/Count大约需要多花费4%的时间(在这种情况下平均多110毫秒)。因此,仅使用Count可能会带来性能上的好处。但是,在特定的场景/环境中,您需要先确认这一点是否成立。可以参考Eric Lippert的C# Performance Benchmark Mistakes获取有关如何进行确认的好建议。


0

正如Jon Skeet在评论中所说,找出最佳方法是测量。 然而,如果您使用SelectedObjects,我建议删除CountObjects。原因如下:

如果您的IEnumerable是一个内存中的列表,那么就像您提到的一样,CountObjects将再次浏览原始列表。如果您已经调用了SelectedObjects并在变量中得到了结果,则调用Count只会在列表上调用属性,并立即给您对象的数量,而无需再次遍历它。如果您还没有调用SelectedObjects,我认为调用.Where(...).Count()与.Count(...)相比不会更慢。不过这是您应该进行测试的事情。
如果您的IEnumerable是IQueryable,那么要么它已经被实现,这种情况下无所谓,要么(如果它尚未实现)我预计调用.Where(...).Count()将转换为与.Count(...)相同的SELECT COUNT(*)查询。同样,这也是您应该进行测试的事情。

在处理 IQueryable 时,重要的是要意识到 .Where 方法和 .Count 方法之间的差异非常大。依赖继承/多态性将导致 .net 加载您的表,然后计算对象数。 - Aron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接