.net LINQ中的DISTINCE是什么意思?

.net LINQ中的DISTINCE是什么意思?,.net,linq,.net,Linq,我有以下几点 Dim query = From city In myCountry.Cities From street In city.Streets Select New StreetInfo With {.Name = street.Name, .Index = street.Index} Distinct 现在。我再次请求,如果我有多条相同的街道(具有相同的名称和索引),StreetInfo列表将包含所有重复的街道 如何为生成

我有以下几点

Dim query = From city In myCountry.Cities
          From street In city.Streets
          Select New StreetInfo With {.Name = street.Name, .Index = street.Index}
          Distinct
现在。我再次请求,如果我有多条相同的街道(具有相同的名称和索引),StreetInfo列表将包含所有重复的街道

如何为生成的StreetInfo值集合指定真正不同的值

比如说,
StreetInfo
类的定义如下:

Public Class StreetInfo
  Public Property Name As String
  Public Property Index As Integer
End Class

Distinct
使用默认的相等比较器,这意味着您必须在
StreetInfo
上覆盖
Equals
GetHashCode
,才能让它工作。

您可以在
StreetInfo
中覆盖
GetHashCode
Equals
,但如果它是可变的,那么它的现有形式就不太好了。(您可以创建两个实例,一分钟相等,下一分钟不相等。这通常会导致调试体验混乱。)或者,您可以使用匿名类型,确保使用只读“键”属性:

Dim query = From city In myCountry.Cities
            From street In city.Streets
            Select New With { Key .Name = street.Name, Key .Index = street.Index}
            Distinct
在这里,编译器将自动提供相等和哈希代码。这相当于:

Dim query = From city In myCountry.Cities
            From street In city.Streets
            Select street.Name, street.Index
            Distinct
有关更多信息,请参阅

如果以后确实需要
StreetInfo
值,并且不想使其不可变,则可以在以后添加另一个投影(选择)。我不知道是否可以在单个查询中的Distinct子句之后追加另一个Select子句,但可以使用第二个表达式:

Dim query = From city In myCountry.Cities
            From street In city.Streets
            Select street.Name, street.Index
            Distinct

Dim query2 = From street in query
             Select New StreetInfo With {.Name = street.Name, _
                                         .Index = street.Index}

正如其他人提到的,使用匿名类型是一个很好的解决方案,因为编译器会为您创建
Equals
GetHashCode
方法。但是,问题是您丢失了对原始对象的引用(
StreetInfo
),而是得到了一个不同匿名类型的列表

相反,您可以使用自定义的
IEqualityComparer
根据所需的任何自定义字段来比较原始对象。下面是一个可重用的
CustomComparer
,它根据任何自定义条件比较任何对象

Public Class CustomComparer(Of TSource As Class, TCompareType)
    Implements IEqualityComparer(Of TSource)

    Private getComparisonObject As Func(Of TSource, TCompareType)

    Public Sub New(ByVal getComparisonObject As Func(Of TSource, TCompareType))
        If (getComparisonObject Is Nothing) Then
            Throw New ArgumentNullException("getComparisonObject")
        End If
        Me.getComparisonObject = getComparisonObject
    End Sub

    Public Function Equals(ByVal x As TSource, ByVal y As TSource) As Boolean
        If (x Is Nothing) Then
            Return (y Is Nothing)
        ElseIf (y Is Nothing) Then
            Return false
        End If
        Return EqualityComparer.Default.Equals(getComparisonObject(x), getComparisonObject(y))
    End Function

    Public Function GetHashCode(ByVal obj As TSource) As Integer
        Return EqualityComparer.Default.GetHashCode(getComparisonObject(obj))
    End Function
End Class
这允许您编写以下代码:

Dim query = From city In myCountry.Cities
      From street In city.Streets
      Select New StreetInfo With {.Name = street.Name, .Index = street.Index}
query = query.Distinct(new CustomComparer(function (street) New With {Key.Name = street.Name, Key.Index = street.Index}))
这将返回所有不同的
StreetInfo
,而不是返回不同的匿名类型


(注意:请原谅任何VB.NET语法问题…我切换到C#在lambdas和anon类型之前)

或为
Distinct
重载提供您自己的相等比较器,或使用匿名类型,编译器将自动为其生成
Equals
GetHashCode
方法。@JimMischel:是,这正是我个人会采取的方法。唯一的缺点是无法传递这样的集合或以良好的方式返回它@斯科特·里皮:我应该在哪里指定比较器?不幸的是,我不能使用匿名类型,因为我需要序列化结果。。。但是这些类型是不可序列化的。。。谢谢你的密钥,我没有注意到区别…@serhio:你仍然可以使用匿名类型来实现平等,然后投影到
StreetInfo
。但是使
StreetInfo
不可变并重写Equals/GetHashCode是一个很好的选择。(如果你能在问题中陈述你的要求,这会很有用,以避免回答实际上没有帮助的问题。)我真的不明白你说的使用一个动画类型然后投影到真实对象是什么意思。。。保持相同的查询,这是怎么可能的?@serhio:您应该能够先进行选择,然后进行区分,然后再进行另一次选择。我不清楚您是否可以在单个查询表达式中实现这一点,但您肯定可以使用多个查询表达式。将进行编辑。您是否也可以将
StreetInfo
实例选择为匿名类型?然后执行第二个查询,仅从第一个查询的结果中选择
StreetInfo
引用?这样可以避免创建一堆新的
StreetInfo
对象。谢谢。虽然你的代码根本不可编译),但我发现了你的意思,MSDN上有我的例子:是的,我从C#转换了代码,我不确定是否需要修改才能编译。这是一个维基,所以请随意修复它。