159

所以我有一个对象集合。确切的类型并不重要。从中我想提取一对特定属性的所有唯一对,因此:

myObjectCollection.Select(item=>new
                                {
                                     Alpha = item.propOne,
                                     Bravo = item.propTwo
                                }
                 ).Distinct();

所以我的问题是:在这种情况下 Distinct 会使用默认对象 equals (这对我来说没用,因为每个对象都是新的)还是可以告诉它做不同的 equals (在这种情况下,Alpha 和 Bravo 的值相等=> 相等的实例)?如果不这样做,有什么方法可以达到这个结果?

4

8 回答 8

202

阅读 K. Scott Allen 的精彩文章:

人人平等……匿名类型

简短的回答(我引用):

原来 C# 编译器覆盖了匿名类型的 Equals 和 GetHashCode。这两个重写方法的实现使用类型上的所有公共属性来计算对象的哈希码并测试是否相等。如果相同匿名类型的两个对象的属性值都相同——对象是相等的。

因此,在返回匿名类型的查询上使用 Distinct() 方法是完全安全的。

于 2009-02-12T21:59:11.240 回答
14
public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

很抱歉之前的格式混乱

于 2009-03-27T00:45:41.590 回答
5

有趣的是它在 C# 中有效,但在 VB 中无效

返回 26 个字母:

var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();

返回 52...

Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()
于 2009-10-26T20:58:11.043 回答
4

我跑了一个小测试,发现如果属性是值类型,它似乎工作正常。如果它们不是值类型,则该类型需要提供它自己的 Equals 和 GetHashCode 实现才能使其工作。我认为字符串会起作用。

于 2009-02-12T22:07:45.200 回答
2

您可以创建自己的采用 lambda 表达式的 Distinct Extension 方法。这是一个例子

创建一个派生自 IEqualityComparer 接口的类

public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

然后创建您的独特扩展方法

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

你可以使用这个方法找到不同的项目

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
于 2009-03-27T00:35:19.587 回答
0

如果AlphaBravo都继承自一个公共类,您将能够通过实现来指示父类中的相等性检查IEquatable<T>

例如:

public class CommonClass : IEquatable<CommonClass>
{
    // needed for Distinct()
    public override int GetHashCode() 
    {
        return base.GetHashCode();
    }

    public bool Equals(CommonClass other)
    {
        if (other == null) return false;
        return [equality test];
    }
}
于 2009-02-12T22:00:16.013 回答
0

嘿,我遇到了同样的问题,我找到了解决方案。您必须实现 IEquatable 接口或简单地覆盖 (Equals & GetHashCode) 方法。但这不是技巧,GetHashCode 方法中的技巧。你不应该返回你的类对象的哈希码,但你应该返回你想要比较的属性的哈希值。

public override bool Equals(object obj)
    {
        Person p = obj as Person;
        if ( obj == null )
            return false;
        if ( object.ReferenceEquals( p , this ) )
            return true;
        if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian )
            return true;
        return false;
        //return base.Equals( obj );
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }

如您所见,我有一个名为 person 的类,有 3 个属性(Name、Age、IsEgyptian“因为我是”) 在 GetHashCode 中,我返回了 Name 属性的哈希值,而不是 Person 对象。

试试看,它会在 ISA 上运行。谢谢你,莫达瑟·萨迪克

于 2011-11-16T14:51:48.703 回答
0

为了让它在 VB.NET 中工作,你需要Key在匿名类型的每个属性之前指定关键字,就像这样:

myObjectCollection.Select(Function(item) New With
{
    Key .Alpha = item.propOne,
    Key .Bravo = item.propTwo
}).Distinct()

我为此苦苦挣扎,我认为 VB.NET 不支持这种类型的功能,但实际上它确实支持。

于 2018-07-13T20:24:30.960 回答