C# LINQ Select Distinct with Anonymous Types
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/543482/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
LINQ Select Distinct with Anonymous Types
提问by GWLlosa
So I have a collection of objects. The exact type isn't important. From it I want to extract all the unique pairs of a pair of particular properties, thusly:
所以我有一个对象的集合。确切的类型并不重要。我想从中提取一对特定属性的所有唯一对,因此:
myObjectCollection.Select(item=>new
{
Alpha = item.propOne,
Bravo = item.propTwo
}
).Distinct();
So my question is: Will Distinct in this case use the default object equals (which will be useless to me, since each object is new) or can it be told to do a different equals (in this case, equal values of Alpha and Bravo => equal instances)? Is there any way to achieve that result, if this doesn't do it?
所以我的问题是:在这种情况下,Distinct 会使用默认对象 equals(这对我来说没用,因为每个对象都是新的)还是可以告诉它执行不同的 equals(在这种情况下,Alpha 和 Bravo 的相等值=> 相等的实例)?如果这样做不行,有什么办法可以达到这个结果吗?
采纳答案by Matt Hamilton
Have a read through K. Scott Allen's excellent post here:
在这里阅读 K. Scott Allen 的精彩帖子:
And Equality for All ... Anonymous Types
The short answer (and I quote):
简短的回答(我引用):
Turns out the C# compiler overrides Equals and GetHashCode for anonymous types. The implementation of the two overridden methods uses all the public properties on the type to compute an object's hash code and test for equality. If two objects of the same anonymous type have all the same values for their properties – the objects are equal.
事实证明,C# 编译器会覆盖匿名类型的 Equals 和 GetHashCode。两个重写方法的实现使用类型上的所有公共属性来计算对象的哈希码并测试是否相等。如果相同匿名类型的两个对象的属性值都相同——这两个对象是相等的。
So it's totally safe to use the Distinct() method on a query that returns anonymous types.
因此,在返回匿名类型的查询上使用 Distinct() 方法是完全安全的。
回答by ern
If Alpha
and Bravo
both inherit from a common class, you will be able to dictate the equality check in the parent class by implementing IEquatable<T>
.
如果Alpha
和Bravo
都继承自一个公共类,您将能够通过实现IEquatable<T>
.
For example:
例如:
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];
}
}
回答by tvanfosson
I ran a little test and found that if the properties are value types, it seems to work ok. If they are not value types, then the type needs provide it's own Equals and GetHashCode implementations for it to work. Strings, I would think, would work.
我跑了一个小测试,发现如果属性是值类型,它似乎工作正常。如果它们不是值类型,则该类型需要提供它自己的 Equals 和 GetHashCode 实现才能使其工作。我认为字符串会起作用。
回答by tvanfosson
You can create your own Distinct Extension method which takes lambda expression. Here's an example
您可以创建自己的 Distinct Extension 方法,该方法采用 lambda 表达式。这是一个例子
Create a class which derives from IEqualityComparer interface
创建一个派生自 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();
}
}
Then create your Distinct Extension method
然后创建您的 Distinct Extension 方法
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));
}
}
and you can use this method find distinct items
您可以使用此方法查找不同的项目
var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
.Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
回答by tvanfosson
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();
Sorry for the messed up formatting earlier
抱歉之前弄乱了格式
回答by GeorgeBarker
Interesting that it works in C# but not in VB
有趣的是它在 C# 中有效,但在 VB 中无效
Returns the 26 letters:
返回 26 个字母:
var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();
Returns 52...
返回 52...
Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()
回答by Modather Sadik
Hey there i got the same problem and i found an solution. You have to implement the IEquatable interface or simply override the (Equals & GetHashCode) Methods. But this is not the trick, the trick coming in the GetHashCode Method. You should not return the hash code of the object of your class but you should return the hash of the property you want to compare like that.
嘿,我遇到了同样的问题,我找到了解决方案。您必须实现 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();
}
As you see i got an class called person got 3 properties (Name,Age,IsEgyptian"Because I am") In the GetHashCode i returned the hash of the Name property not the Person object.
如您所见,我得到了一个名为 person 的类,它有 3 个属性(Name、Age、IsEgyptian“因为我是”)在 GetHashCode 中,我返回了 Name 属性的哈希值,而不是 Person 对象。
Try it and it will work ISA. Thank you, Modather Sadik
试试看,它会在 ISA 上工作。谢谢你,莫达瑟萨迪克
回答by Alisson
In order for it to work in VB.NET, you need to specify the Key
keyword before every property in the anonymous type, just like this:
为了让它在 VB.NET 中工作,你需要Key
在匿名类型的每个属性之前指定关键字,就像这样:
myObjectCollection.Select(Function(item) New With
{
Key .Alpha = item.propOne,
Key .Bravo = item.propTwo
}).Distinct()
I was struggling with this, I thought VB.NET didn't support this type of feature, but actually it does.
我为此苦苦挣扎,我认为 VB.NET 不支持这种类型的功能,但实际上它确实支持。