1

我在 Framework 2.0 上使用 VB.NET。我希望通过两个属性快速分组一个通用列表<>。为了这个例子,假设我有一个带有 CustomerId、ProductId 和 ProductCount 属性的 Order 类型的列表。如何在 VB.NET 中获得按 CustomerId 和 ProductId 分组的 ProductCounts 的平均值?

不幸的是,我不能在数据库级别做到这一点(这很容易)。我也不能在 Framewwork 2.0 上使用 LINQ 或 Lambada 作为 Im。所以我需要在 VB.net 的应用程序级别执行此操作。该列表按 CustomerId 和 ProductId 排序返回给我,所以我想用几个循环我应该能够创建一个平均值,但必须有一个更清洁的方法来做到这一点?

4

1 回答 1

1

这类问题是 C# 和 VB.NET 中引入 LINQ 的一个很好的例子。在 .NET 3.5 及更高版本中,LINQ 提供了您正在寻找的“更清洁的方式”来解决此问题。

不幸的是,因为您使用的是 .NET 2.0,所以您将以或多或少的“手动”方式解决问题。但是,您仍然可以通过将您正在寻找的功能封装到定义良好的类和方法中来编写干净的代码。这是 LINQ 的好处之一(但不是唯一的),即它以一种干净的、声明性的方式封装了您期望的功能。

这里有一些示例代码可以帮助您入门:

'The AggregateItem and AggregateItems classes will help encapsulate the '
'the functionality you are looking for.'

Public Class AggregateItem
    Public Property GroupByProperty1 As Integer ' Get/Set code...'
    Public Property GroupByProperty2 As Integer ' Get/Set code...'
    Public Property Values As List(Of Double) = New List(Of Double()() _
        ' Get/Set code...'

    Public Function GetAverage() As Double
        'Code to calculate and return Average...'
    End Function     
End Class

Public Class AggregateItems
    Public Property AggregateItemList As List(Of AggregateItem) = _
        New List(Of AggregateItem)() ' Get/Set code...'

    Public Sub InsertAggregateItem(groupByProperty1 As Integer, _
                                   groupByProperty2 As Integer, _
                                   value As Double)
        Dim aiExisting As AggregateItem
        aiExisting = GetMatchingAggregateItem(groupByProperty1, _
                                              groupByProperty2)
        If Not aiExisting Is Nothing Then
            aiExisting.Values.Add(value)
        Else
            aiExisting = New AggregateItem
            aiExisting.GroupByProperty1 = groupByProperty1
            aiExisting.GroupByProperty2 = groupByProperty2
            aiExisting.Values.Add(value)
            AggregateItemList.Add(aiExisting)
    End Sub

    Private Function GetMatchingAggregateItem(groupByProperty1 As Integer, _
                                              groupByProperty2 As Integer) _
            As AggregateItem
         Dim aiMatch As AggregateItem = Nothing
         For Each ag As AggregateItem in AggregateItemList
             If ag.GroupByProperty1 = groupByProperty1 AndAlso _
                ag.GroupByProperty2 = groupByProperty2 Then
                 aiMatch = ag
                 Exit For
             End If
         Next

         Return aiMatch
    End Function
Enc Class

'Then, to consume these classes....'

Public Module MyProgram
    Public Sub Main()
         Dim aItems As New AggregateItems()
         'Say you have List(Of Order) named listOfOrders'
         'We will loop through that list, insert the grouping IDs and values'
         'into our AggregateItems object'
         For Each o As Order In listOfOrders
            aItems.InsertAggregateItem(o.OrderId, o.ProductId, o.ProductCount)
         Next

        'Now we can loop through aItems to cleanly get the average: '
        For Each ai As AggregateItem in aItems.AggregateItemsList
            Console.WriteLine("Order: {0} Product: {1} Average: {2}", _
                ai.GroupByProperty1, ai.GroupByProperty2, _
                ai.GetAverage())
        Next
    End Sub

End Module

将数据插入封装良好的类的好处是使用代码非常简洁且易于理解。此外,由于您已经将数据聚合到一个AggregateItem类中,因此您可以使用更多方法轻松扩展该类,例如GetSum()or GetMax()

显然,您可以继续走这条抽象之路,以更好地重用您的代码,但我认为这为您提供了一个良好的开端。

于 2010-08-18T13:32:01.640 回答