如何在VB.Net中创建一个可以从函数返回结果的GroupBy?

时间:2016-12-21 15:45:14

标签: vb.net linq

我必须编写一个方法,返回按这些对象的某些属性分组的对象列表。我可以成功地使用linq的GroupBy,但使用匿名类型,我无法发回给调用者。如果我尝试使用类来存储here中建议的IGrouping部分,则.GroupBy根本不会分组 - 就像Key关键字丢失一样,但我不能强烈使用它们打字课。

我搜索了很多 - 更多是因为我懒得写一个问题,而不是因为遵循stackoverflow问题指南 - 但我发现的大部分内容都是C#相关的,没有问题关键字关键字,或关于单列GroupBy,或者它使用匿名类型,因为它不关心从函数或子函数返回参数,所以我做了一个小程序来说明情况。

Imports System.Globalization

Module Module1

    Public Class TrackHist
        Public Property Data As DateTime
        Public Property Esdvn As String
        Public Property Cmnts As String
        Public Property IdCnt As String
    End Class

    Public Class GroupedTrack
        Public Property Data As DateTime
        Public Property Esdvn As String
    End Class

    Function Group(lista As List(Of TrackHist)) As List(Of IGrouping(Of GroupedTrack, List(Of TrackHist)))
        Return lista.GroupBy(Function(f) New GroupedTrack With {.Data = f.Data, .Esdvn = f.Esdvn}).ToList
    End Function

    Sub Main()
        Dim array As New List(Of TrackHist)
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("10/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "BIL", .IdCnt = "CN134234"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("10/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "BIL", .IdCnt = "CN284235"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("10/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "BIL", .IdCnt = "CN660003"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("12/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "LOA", .IdCnt = "CN134234"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("12/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "LOA", .IdCnt = "CN284235"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("13/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "LOA", .IdCnt = "CN660003"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("15/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "INSP", .IdCnt = "CN134234"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("16/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "DEP", .IdCnt = "CN284235"})
        array.Add(New TrackHist() With {.Data = DateTime.ParseExact("16/01/2017", "dd/MM/yyyy", CultureInfo.InvariantCulture), .Cmnts = "Nothing", .Esdvn = "DEP", .IdCnt = "CN660003"})

        Dim groupedAnonymous = array.GroupBy(Function(f) New With {Key f.Data, Key f.Esdvn}).ToList
        Dim groupedTyped = Group(array)

        Console.WriteLine("Grouped anonymous: {0} registers", groupedAnonymous.Count)
        Console.WriteLine("Grouped typed: {0} registers", groupedTyped.Count)
    End Sub

End Module

该程序产生以下输出:

分组匿名:5个寄存器 分组输入:9个寄存器

或者,换句话说,具有强类型对象的分组根本不是分组。那么,在VB.Net中创建GroupBy的正确方法是什么,其结果可以从函数返回?

2 个答案:

答案 0 :(得分:1)

将Slai和Mark的评论转换成答案:

您的问题是您尚未定义将GroupedTrack对象与另一个GroupedTrack对象进行比较的方法;因此,当创建这些对象时,它们将通过引用进行比较,并且因为它们实际上是不同的对象,所以您将获得9个不同的值。

您需要做的是覆盖Equals()GetHashCode()函数,以便编译器现在如何按值比较GroupedTrack个对象。

这样的事情:

Public Class GroupedTrack
    Public Property Data As DateTime
    Public Property Esdvn As String

    Public Overrides Function Equals(obj As Object) As Boolean
        If obj Is Nothing Then Return False
        If TypeOf obj IsNot GroupedTrack Then Return False
        Return Me.Data = CType(obj, GroupedTrack).Data And Me.Esdvn = CType(obj, GroupedTrack).Esdvn
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return (Me.Data.ToString() & Me.Esdvn).GetHashCode()
    End Function
End Class

(当你在那时,将你的功能定义更正为

Function Group(lista As List(Of TrackHist)) As List(Of IGrouping(Of GroupedTrack, TrackHist))

这样,两个分组都会产生一个包含5个元素的列表。

答案 1 :(得分:1)

如果“正确”的方式似乎太麻烦,懒惰的方式是使用类似的类型的键:

Function Group(items As IEnumerable(Of TrackHist)) As ILookup(Of String, TrackHist)
    Return items.ToLookup(Function(t) t.Esdvn & t.Data)
End Function

我甚至不打算使用该函数,但关键是String已经实现了EqualsGetHashCode方法,因此您不必这样做。 .GroupBy LINQ扩展程序使用Lookup来获取组,因此您可以直接使用.ToList来避免Lookup

如果您想分别比较关键项,可以将匿名类型转换为Object

 Dim lookup As ILookup(Of Object, TrackHist) = 
         array.ToLookup(Function(t) CObj(New With {Key t.Data, Key t.Esdvn}))