2012-05-04 10 views

答えて

66

Object.EqualsObject.GetHashCodeのメソッドをすべてのカスタムタイプでオーバーライドすることと組み合わせて、IEquatable<T>を実装します。コンポジット型の場合は、含まれる型の中で含まれる型 'Equalsを呼び出します。含まれているコレクションについては、各要素の内部でIEquatable<T>.EqualsまたはObject.Equalsを呼び出すSequenceEqual拡張メソッドを使用してください。このアプローチでは、明らかに型の定義を拡張する必要がありますが、その結果はシリアル化を含む一般的なソリューションよりも高速です。

を編集します。ここには、3つのレベルの入れ子があるような複雑な例があります。

値型の場合、通常はEqualsメソッドを呼び出すことができます。フィールドまたはプロパティが明示的に割り当てられていない場合でも、それらはデフォルト値を持ちます。

参照型の場合は、最初にReferenceEqualsを呼び出して、参照の等しいかどうかを確認する必要があります。これは、同じオブジェクトを参照しているときに効率が向上するためです。また、両方の参照がnullの場合も処理します。チェックに失敗した場合は、インスタンスのフィールドまたはプロパティがnullでないことを確認し(NullReferenceExceptionを避けるため)、Equalsメソッドを呼び出します。メンバーが正しく型付けされているので、IEquatable<T>.Equalsメソッドは、オーバーライドされたObject.Equalsメソッド(型キャストのために実行がわずかに遅くなります)をバイパスして直接呼び出されます。

Object.Equalsを無効にすると、Object.GetHashCodeも上書きされます。私は簡潔さのために以下ではそうしなかった。

public class Person : IEquatable<Person> 
{ 
    public int Age { get; set; } 
    public string FirstName { get; set; } 
    public Address Address { get; set; } 

    public override bool Equals(object obj) 
    { 
     return this.Equals(obj as Person); 
    } 

    public bool Equals(Person other) 
    { 
     if (other == null) 
      return false; 

     return this.Age.Equals(other.Age) && 
      (
       object.ReferenceEquals(this.FirstName, other.FirstName) || 
       this.FirstName != null && 
       this.FirstName.Equals(other.FirstName) 
      ) && 
      (
       object.ReferenceEquals(this.Address, other.Address) || 
       this.Address != null && 
       this.Address.Equals(other.Address) 
      ); 
    } 
} 

public class Address : IEquatable<Address> 
{ 
    public int HouseNo { get; set; } 
    public string Street { get; set; } 
    public City City { get; set; } 

    public override bool Equals(object obj) 
    { 
     return this.Equals(obj as Address); 
    } 

    public bool Equals(Address other) 
    { 
     if (other == null) 
      return false; 

     return this.HouseNo.Equals(other.HouseNo) && 
      (
       object.ReferenceEquals(this.Street, other.Street) || 
       this.Street != null && 
       this.Street.Equals(other.Street) 
      ) && 
      (
       object.ReferenceEquals(this.City, other.City) || 
       this.City != null && 
       this.City.Equals(other.City) 
      ); 
    } 
} 

public class City : IEquatable<City> 
{ 
    public string Name { get; set; } 

    public override bool Equals(object obj) 
    { 
     return this.Equals(obj as City); 
    } 

    public bool Equals(City other) 
    { 
     if (other == null) 
      return false; 

     return 
      object.ReferenceEquals(this.Name, other.Name) || 
      this.Name != null && 
      this.Name.Equals(other.Name); 
    } 
} 
+0

私はこのオブジェクトをRIA Services経由で入手します...これらのオブジェクトにIEquatable を使用してWPFクライアントの下で入手できますか? –

+0

あなたはクラスが自動生成されることを意味しますか?私はRIAサービスを使用していませんが、生成されたクラスは 'partial'として宣言されていると仮定します。この場合、フィールド/フィールドを参照する手作業で追加された部分クラス宣言によって'プロパティを自動生成されたものから削除します。 – Douglas

+0

「アドレスアドレス」が実際に「アドレス[]アドレス」の場合、どのように実装されますか? – guiomie

0

私はと言うでしょう:

Object1.Equals(Object2)

は、あなたが探しているものだろう。それは、オブジェクトが同じかどうか、つまりあなたが求めているように見えるかどうかを調べる場合です。

すべての子オブジェクトが同じかどうかを確認する場合は、Equals()メソッドのループで実行してください。

+2

、彼らは対等の非参照の等価の過負荷を与えた場合にのみ場合。 – user7116

+0

各クラスは独自の比較方法を実装する必要があります。作成者のクラスにEquals()メソッドのオーバーライドがない場合、System.Object()クラスの基本メソッドが使用され、ロジックにエラーが発生します。 – Dima

4

私はあなたが文字通り同じオブジェクトを指しているわけではないと仮定します

Object1 == Object2 
次の2つの

memcmp(Object1, Object2, sizeof(Object.GetType()) 

間のメモリの比較を行うことを考えるかもしれない。しかしそれはさえない

実際のコードはC#で:)すべてのデータがヒープ上に作成されている可能性があるため、メモリは連続していないため、2つのオブジェクトの等価性を無関係な方法で比較することはできません。一度に1つずつ、カスタム方法で各値を比較する必要があります。

クラスにIEquatable<T>インターフェイスを追加し、お使いのタイプにカスタムEqualsメソッドを定義することを検討してください。次に、その方法で、各値を手動でテストします。処理をやり直すことができる場合は、同封のタイプにIEquatable<T>を再度追加してください。

class Foo : IEquatable<Foo> 
{ 
    public bool Equals(Foo other) 
    { 
    /* check all the values */ 
    return false; 
    } 
} 
1

これを行う1つの方法は、関係する各タイプのEquals()を上書きすることです。たとえば、最上位レベルのオブジェクトは、Equals()をオーバーライドして、すべての5つの子オブジェクトのEquals()メソッドを呼び出します。これらのオブジェクトはすべて、Equals()をオーバーライドする必要があります。カスタムオブジェクトであると仮定して、トップレベルオブジェクトの等価チェックを実行するだけで階層全体を比較できるようになります。

1

IEquatable<T>Equalsのインタフェースを持つインタフェースを使用してください。

13

IEquatableを実装したくない場合は、常にReflectionを使用してすべてのプロパティを比較できます。値型の場合は比較してください。それらが参照型の場合は、関数を再帰的に呼び出して、その "内部"プロパティを比較します。

私はperformaceについては考えていませんが、単純さについてです。ただし、オブジェクトの正確な設計に依存します。あなたのオブジェクトの形状に応じて複雑になる可能性があります。しかし、そこにいくつかのソリューションは、あなたは、このように、そこに使用することができていること:あなたが最速の方法を意味し、最速で、それまたはコードを実装する場合

は私は知りませんそれは速く走る。必要があるかどうかを知る前に最適化すべきではありません。 Premature optimization is the root of all evil

+1

'IEquatable 'の実装は早すぎる最適化のケースと見なされることはほとんどありません。反射は大幅に遅くなります。カスタム値型の 'Equals'のデフォルト実装では、リフレクションを使用しています。マイクロソフト自身がパフォーマンスのためにオーバーライドすることを推奨します:「メソッドのパフォーマンスを向上させるために、特定の型の 'Equals'メソッドをオーバーライドし、型の等価性の概念をより詳細に表現してください。」 – Douglas

+1

実行する回数equalsメソッド:1、10、100、100、100万?それは大きな違いになります。何も実装せずに汎用ソリューションを使用することができれば、貴重な時間を無駄にすることはありません。遅すぎる場合は、IEquatableを実装する時間です(キャッシュ可能な、またはインテリジェントなGetHashCodeを作成しようとしているかもしれません)。Reflectionのスピードについては、それが遅くなることに同意する必要があります。つまりPropertyInfos Typesを再利用するかどうかなど)。 – JotaBe

+0

そのギブスには書類がありません... – Worthy7

44

は、両方のオブジェクトをシリアライズし、両方のオブジェクトをシリアライズし、だから、これを行うので、同じよう静的クラスを作成し、すべてのオブジェクトを拡張するための拡張機能を使用する@JoelFan

による結果の文字列を比較結果の文字列に

+2

おそらく、うまくいくはずです。ありがとうございました! –

+0

これは良いトリックだと思われる。パフォーマンスのペナルティはありますか? – osiris

+1

なぜそうなるのか分かりません。シリアライゼーションは通常最適化されたプロセスなので、どのような場合でもすべてのプロパティの値にアクセスする必要があります。 – JoelFan

6

を比較しますあなたが他のファイルで、この静的クラスを参照すると

using System; 
using System.IO; 
using System.Runtime.Serialization.Json; 
using System.Text; 

public static class MySerializer 
{ 
    public static string Serialize(this object obj) 
    { 
     var serializer = new DataContractJsonSerializer(obj.GetType()); 
     using (var ms = new MemoryStream()) 
     { 
      serializer.WriteObject(ms, obj); 
      return Encoding.Default.GetString(ms.ToArray()); 
     } 
    } 
} 

を(あなたがメソッドにオブジェクト、コレクション、などのANYTYPEを渡すことができます)、あなたはこれを行うことができます。

Person p = new Person { Firstname = "Jason", LastName = "Argonauts" }; 
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" }; 
//assuming you have already created a class person! 
string personString = p.Serialize(); 
string person2String = p2.Serialize(); 

これで、.Equalsを使用して比較することができます。 オブジェクトがコレクション内にあるかどうかを確認するために、これを使用します。それは本当にうまくいく。あなたはこの問題解決するために、拡張メソッド、再帰を使用することができます

+0

オブジェクトの内容が浮動小数点数の配列である場合はどうなりますか?それらを文字列に変換することは非常に非効率的であり、変換は 'CultrureInfo'で定義された変換の対象となります。これは、内部データがほとんど文字列と整数の場合にのみ機能します。それ以外の場合は災害になります。 – ja72

+0

新しいディレクターがC#を取り除いてPythonに置き換えるように指示した場合はどうなりますか?開発者として、もし何か問題がどこかに止まらなければならないかどうかを知る必要があります。問題を次の問題に解決してください。あなたが今までに時間を得たら、それに戻ってください... – ozzy432836

+1

Pythonは構文と使用法の点でMATLABに似ています。静的型安全な言語からPythonのようなミッシュマッシュスクリプトに移行するのは本当に良い理由です。 – ja72

10

:(オブジェクトが非常に複雑である場合)

public static bool DeepCompare(this object obj, object another) 
{  
    if (ReferenceEquals(obj, another)) return true; 
    if ((obj == null) || (another == null)) return false; 
    //Compare two object's class, return false if they are difference 
    if (obj.GetType() != another.GetType()) return false; 

    var result = true; 
    //Get all properties of obj 
    //And compare each other 
    foreach (var property in obj.GetType().GetProperties()) 
    { 
     var objValue = property.GetValue(obj); 
     var anotherValue = property.GetValue(another); 
     if (!objValue.Equals(anotherValue)) result = false; 
    } 

    return result; 
} 

public static bool CompareEx(this object obj, object another) 
{ 
if (ReferenceEquals(obj, another)) return true; 
if ((obj == null) || (another == null)) return false; 
if (obj.GetType() != another.GetType()) return false; 

//properties: int, double, DateTime, etc, not class 
if (!obj.GetType().IsClass) return obj.Equals(another); 

var result = true; 
foreach (var property in obj.GetType().GetProperties()) 
{ 
    var objValue = property.GetValue(obj); 
    var anotherValue = property.GetValue(another); 
    //Recursion 
    if (!objValue.DeepCompare(anotherValue)) result = false; 
} 
return result; 
} 

をまたはJSONを使用して比較する あなたはNewtonsoft.Jsonを使用することができます。

public static bool JsonCompare(this object obj, object another) 
{ 
    if (ReferenceEquals(obj, another)) return true; 
    if ((obj == null) || (another == null)) return false; 
    if (obj.GetType() != another.GetType()) return false; 

    var objJson = JsonConvert.SerializeObject(obj); 
    var anotherJson = JsonConvert.SerializeObject(another); 

    return objJson == anotherJson; 
} 
+0

最初の解決策は素晴らしいです!私はあなたがJSONをシリアライズしたり、オブジェクト自体にコードを追加したりする必要がないのが好きです。単体テストを比較するときに適しています。 objValueとanotherValueの両方がnullの場合に単純比較を追加することをお勧めしますか?これにより、nullを実行しようとするとNullReferenceExceptionがスローされます.Equals()// ReSharper once once RedundantJumpStatement if(objValue == anotherValue); //リファレンスガード else if(!objValue.Equals(anotherValue))Fail(期待値、実際); –

+1

'CompareEx'を再帰的に呼び出すのではなく、' DeepCompare'を使う理由はありますか? – apostolov

3

私はオブジェクトを比較するための関数の下でこれを見つけました。

static bool Compare<T>(T Object1, T object2) 
{ 
     //Get the type of the object 
     Type type = typeof(T); 

     //return false if any of the object is false 
     if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T))) 
     return false; 

    //Loop through each properties inside class and get values for the property from both the objects and compare 
    foreach (System.Reflection.PropertyInfo property in type.GetProperties()) 
    { 
      if (property.Name != "ExtensionData") 
      { 
       string Object1Value = string.Empty; 
       string Object2Value = string.Empty; 
       if (type.GetProperty(property.Name).GetValue(Object1, null) != null) 
        Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString(); 
       if (type.GetProperty(property.Name).GetValue(object2, null) != null) 
        Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString(); 
       if (Object1Value.Trim() != Object2Value.Trim()) 
       { 
        return false; 
       } 
      } 
    } 
    return true; 
} 

私はそれを使用しており、私にとってはうまくいきます。

-4

非常に簡単な方法は次のとおりです。

  1. は、あなたのオブジェクトのリストを作成します。
  2. このリストの交差が "偽"の場合、等しいです。シンプル:
  3. リファレンス:using System.Linq;

    class Program 
    { 
        public class JohnsonObject 
        { 
         public int Id { get; set; } 
         public string Description { get; set; } 
        } 
    
        static void Main() 
        { 
         JohnsonObject[] obj01 = new[] { new JohnsonObject { Id = 1, Description = "01" } }; 
         JohnsonObject[] obj02 = new[] { new JohnsonObject { Id = 1, Description = "01" } }; 
         Console.Write("Are equal? = {0}", !obj01.Intersect(obj02).Any()); 
        } 
    } 
    
1
public class GetObjectsComparison 
{ 
    public object FirstObject, SecondObject; 
    public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; 
} 
public struct SetObjectsComparison 
{ 
    public FieldInfo SecondObjectFieldInfo; 
    public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue; 
    public bool ErrorFound; 
    public GetObjectsComparison GetObjectsComparison; 
} 
private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison) 
{ 
    GetObjectsComparison FunctionGet = GetObjectsComparison; 
    SetObjectsComparison FunctionSet = new SetObjectsComparison(); 
    if (FunctionSet.ErrorFound==false) 
     foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions)) 
     { 
      FunctionSet.SecondObjectFieldInfo = 
      FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions); 

      FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject); 
      FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject); 
      if (FirstObjectFieldInfo.FieldType.IsNested) 
      { 
       FunctionSet.GetObjectsComparison = 
       new GetObjectsComparison() 
       { 
        FirstObject = FunctionSet.FirstObjectFieldInfoValue 
        , 
        SecondObject = FunctionSet.SecondObjectFieldInfoValue 
       }; 

       if (!ObjectsComparison(FunctionSet.GetObjectsComparison)) 
       { 
        FunctionSet.ErrorFound = true; 
        break; 
       } 
      } 
      else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue) 
      { 
       FunctionSet.ErrorFound = true; 
       break; 
      } 
     } 
    return !FunctionSet.ErrorFound; 
} 
+0

再帰の原則を使用します。 –

関連する問題