オブジェクトのコレクションがあります。正確なタイプは重要ではありません。それから、特定のプロパティのペアの一意のペアをすべて抽出します。
myObjectCollection.Select(item=>new
{
Alpha = item.propOne,
Bravo = item.propTwo
}
).Distinct();
私の質問は次のとおりです:この場合、Distinctはデフォルトのオブジェクト等しいを使用します(各オブジェクトは新しいため、私にとっては役に立たないでしょう)または異なる等しいを行うように指示することができます=>等しいインスタンス)?これができない場合、その結果を達成する方法はありますか?
K. Scott Allenの素晴らしい投稿を読んでください。
短い答え(そして私は引用):
C#コンパイラは、匿名型のEqualsおよびGetHashCodeをオーバーライドします。 2つのオーバーライドされたメソッドの実装では、型のすべてのパブリックプロパティを使用して、オブジェクトのハッシュコードを計算し、等しいかどうかをテストします。同じ匿名タイプの2つのオブジェクトのプロパティがすべて同じ値である場合、オブジェクトは等しくなります。
したがって、匿名型を返すクエリでDistinct()メソッドを使用することは完全に安全です。
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();
前のめちゃくちゃなフォーマットでごめんなさい
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()
私は少しテストを実行しましたが、プロパティが値型であれば、うまくいくようです。それらが値型でない場合、その型は、それが機能するために独自のEqualsおよびGetHashCode実装を提供する必要があります。文字列は機能すると思います。
ラムダ式を取る独自の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();
}
}
次に、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));
}
}
このメソッドを使用して、個別のアイテムを見つけることができます
var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
.Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
ちょっとそこに同じ問題があり、解決策を見つけました。 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 "Because I am")を取得しました。GetHashCodeでは、Personオブジェクトではなく、Nameプロパティのハッシュを返しました。
試してみると、ISAで機能します。ありがとう、Modather Sadik
VB.NETで機能させるには、次のように、匿名型のすべてのプロパティの前にKey
キーワードを指定する必要があります。
myObjectCollection.Select(Function(item) New With
{
Key .Alpha = item.propOne,
Key .Bravo = item.propTwo
}).Distinct()
私はこれに苦労していました。VB.NETはこのタイプの機能をサポートしていなかったと思っていましたが、実際にはサポートしています。
Alpha
とBravo
の両方が共通のクラスから継承する場合、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];
}
}