Linq Distinct不是很明显

时间:2013-12-10 17:42:51

标签: c# wpf linq

这个linq语句对于从我的newSpecs集合中获取'Name'的唯一值是否正确?

distinctSpecialties显示重复。

IEnumerable<SpecialtyData> distinctSpecialties = newSpecs
    .Select(s => s).Distinct()
    .OrderBy(s => s.Name);

7 个答案:

答案 0 :(得分:4)

您需要定义“distinct” - 默认情况下,如果参考类型是单独的实例(ReferenceEquals),则它们是不同的。您可以通过以下任一方式覆盖默认值:

  • IEquatable<SpecialtyData>
  • 中实施SpecialtyData
  • Equals
  • 中覆盖GetHashCodeSpecialtyData
  • 定义IEqualityComparer<SpecialtyData>并将其传递给Distinct

按名称获取“第一个”不同项目的更简单方法是使用GroupBy

IEnumerable<SpecialtyData> distinctSpecialties = newSpecs.GroupBy(s => s.Name)
                                                         .Select(g => g.First());

答案 1 :(得分:4)

您要求从我的newSpecs集合中获取“名称”的唯一值,但您的代码会返回IEnumerable<SpecialtyData>。出了点问题:问题还是返回类型。

如果您只需要名称,则需要IEnumerable<string>代替:

IEnumerable<string> distinctNames = newSpecs
    .Select(s => s.Name)
    .Distinct().OrderBy(x => x);

如果您需要整个SpecialtyData个实例,可以使用GroupBy + First

IEnumerable<SpecialtyData> distinctSpecialties = newSpecs
    .GroupBy(s => s.Name)
    .Select(g=> g.First())

或覆盖Equals类中的GetHashCodeSpecialtyData方法,以使Distinct有效。

答案 2 :(得分:2)

那是因为默认比较是参考比较。它不是在查看数据本身,而是为您提供了不同的参考。您可以使用Select()获得值得比较的值,或者为您的类型实施IEqualityComparer<T>并将其传递给Distinct

以下是IEqualityComparer<T>;

的示例实现
class SpecialtyDataEqualityComparer : IEqualityComparer<SpecialtyData>
{
    public bool Equals(SpecialtyData lhs, SpecialtyData rhs)
    {
        return lhs.Name == rhs.Name;
    }

    public int GetHashCode(SpecialtyData p)
    {
        return p.Name.GetHashCode();
    }
}

如果您执行了.Distinct(new SpecialtyDataEqualitComparer()),那么您将获得具有不同名称的项目。如果你想要一些其他的相等定义,那么改变Equals中的逻辑来进行你需要的比较(对GetHashCode进行相关更改,使它们保持一致,如果你按名称做相等的话就不应该其他一些属性的哈希码。)

答案 3 :(得分:2)

而不是做自定义相等比较器和所有这些,而不是来自MoreLinq的一个非常好的扩展方法 - https://code.google.com/p/morelinq/(作者Jon Skeet)。

下载整个MoreLinq库,或者只需在下面添加以下代码:

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
                            this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
    var set = new HashSet<TKey>();
    return source.Where(element => set.Add(selector(element)));
}

并像这样使用它:

IEnumerable<SpecialtyData> distinctSpecialties = newSpecs
    .Select(s => s).DistinctBy(s => s.Name)
    .OrderBy(s => s.Name);

答案 4 :(得分:1)

您可以使用GroupBy,如下所示

IEnumerable<SpecialtyData> distinctSpecialties = 
               newSpecs.GroupBy(s => s.Name).Select(g=> g.First()).OrderBy(s => s.Name);

答案 5 :(得分:0)

为了在自定义对象(SpecialtyData)上使用Distinct(),您需要覆盖Distinct()用于确定相等性的两个方法。

    public override bool Equals(object other) {

    }

    public override int GetHashCode() {

    } 

这两个中的每一个都应返回将SpecialtyData对象定义为唯一的值。在您的情况下,它可能像评估“名称”属性一样简单。

答案 6 :(得分:0)

有一个非常好的post from James Michael Hare解释了为什么你得到这个结果,我会在这里引用它:

  

要使Distinct()(以及许多其他LINQ功能)起作用,这个类就是   比较(在您的示例中为SpecialtyData)必须实施Equals()GetHashCode(),或者提供单独的   IEqualityComparer<T>作为Distinct()的参数。

     

许多LINQ方法利用GetHashCode()来提高性能   因为在内部,他们会使用像Set<T>之类的东西来持有   唯一项,使用散列进行O(1)查找。另外,GetHashCode()   可以快速告诉您两个对象是否相同以及哪些对象   绝对不是 - 只要GetHashCode()得到正确实施   当然。

     

所以你应该在LINQ中创建你想要比较的所有类   实现Equals()GetHashCode()以获得完整性,或者创建一个   单独IEqualityComparer<T>实施。

原帖:This code returns distinct values. However, what I want is to return a strongly typed collection as opposed to an anonymous type