web-dev-qa-db-ja.com

Linq Except with custom IEqualityComparer

以下の例のように、2つの汎用リストの違いを見つけようとしています。 t1とt2には同じプロパティが含まれていますが、これらは同じオブジェクトではないため、IEqualityComparerを実装する必要があります。

これはこの例で動作しているようですが、実際のクラスには他にもいくつかのプロパティがあり、他のいくつかのクラスでも同じようにする必要があります。

それで、私は車輪を再発明しているかどうか疑問に思っていましたか?

2つのオブジェクトのすべてのプロパティを比較する簡単な方法はありますか?現時点では、単純な型を含むクラスを処理するだけで十分ですが、他のクラスのインスタンスを含むクラスを処理する比較機能があればいいでしょう。

void Main()
{
    var t1 = new Sizes { Name = "Test" , Size = 1} ;
    var t2 = new Sizes { Name = "Test" , Size = 1} ;

    var list1 = new List<Sizes>();
    var list2 = new List<Sizes>();
    list1.Add(t1);
    list2.Add(t2);

    var differences = list2.Except(list1 , new SizesComparer());    
    // differences should be empty.
}


public class Sizes  
{
    public string Name { get;  set; }
    public int    Size { get;  set; }
}

public class SizesComparer : IEqualityComparer<Sizes>   
{
    bool IEqualityComparer<Sizes>.Equals(Sizes x, Sizes y)
    {            
        return (x.Name.Equals(y.Name) && x.Size.Equals(y.Size));        
    }

    int IEqualityComparer<Sizes>.GetHashCode(Sizes obj)
    {
        if (Object.ReferenceEquals(obj, null))
            return 0;               

        return obj.Name.GetHashCode() + obj.Size;       
    }
}
28
sgmoore

私が最終的に使用した解決策は速く説明することはできませんでしたが、それは私の心配ではなく、再利用でき、特定のクラスに制限されないという点で私が望むことを行います。

Newtonsoft.Jsonライブラリを使用して、オブジェクトを文字列にシリアル化し、結果を比較します。これには、匿名クラスとネストされたクラスを操作するという利点もあります。

比較が機能する方法は、最初に両方のオブジェクトでGetHashCodeを呼び出し、それらが一致する場合はEqualsを呼び出すことを想定しています。これは、このルーチンでは、一致するオブジェクトが2回シリアル化されることを意味します。

public class JSonEqualityComparer<T> : IEqualityComparer<T>
{   
    public bool Equals(T x, T y)
    {           
        return String.Equals
        ( 
            Newtonsoft.Json.JsonConvert.SerializeObject(x), 
            Newtonsoft.Json.JsonConvert.SerializeObject(y)
        );                  
    }

    public int GetHashCode(T obj)
    {                           
        return Newtonsoft.Json.JsonConvert.SerializeObject(obj).GetHashCode();          
    }               
}       


public static partial class LinqExtensions
{
    public static IEnumerable<T> ExceptUsingJSonCompare<T>
        (this IEnumerable<T> first, IEnumerable<T> second)
    {   
        return first.Except(second, new JSonEqualityComparer<T>());
    }
}

これを使用するには、ExceptをExceptUsingJSonCompareと交換します。次に例を示します。

var differences = list2.ExceptUsingJSonCompare(list1); 
4
sgmoore

あなたは次のようなことを試すことができます:

var differences = list2.Where(l2 => 
    !list1.Any(l1 => l1.Name == l2.Name && l1.Size == l2.Size));

または、必要に応じて:

var differences = list2.Where(l2 => 
    list1.All(l1 => l1.Name != l2.Name || l1.Size != l2.Size));
52
Chris Snowden

私が通常行く最善の方法は、各オブジェクトに一意/複合キーを設定することです。そうすれば、キー部分のみを比較し、他のビットを比較する必要はありません。

クラスの各パブリックプロパティ(非常に一般的なもの)を通過して比較するReflectionを使用することもできますが、速度は遅くなります。

0
Bek Raupov