この種の問題は、C# と VB.NET で LINQ が導入された理由の好例です。.NET 3.5 以降では、LINQ は、この問題を解決するために探している "よりクリーンな方法" を提供します。
残念ながら、.NET 2.0 を使用しているため、多かれ少なかれ「手動」で問題を解決する必要があります。ただし、探している機能を適切に定義されたクラスとメソッドにカプセル化することで、クリーンなコードを書くことができます。これは、LINQ の利点の 1 つ (ただし、これだけではありません) です。つまり、必要な機能をクリーンで宣言的な方法でカプセル化します。
開始するためのサンプル コードを次に示します。
'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()
GetMax()
明らかに、コードをよりよく再利用するために、この抽象化の道を歩み続けることができますが、これは良いスタートになると思います。