2011-12-22 12 views
1

私は、このメソッドと呼ばMatchNodes有する:基底クラスからのプロパティ/フィールドを含まない基本的反射を介して(両方Tオブジェクトからすべてのプロパティフィールドを取得IEnumerable<bool> MatchNodes<T>(T n1, T n2)防止スタックオーバーフロー

、および)、それらを比較し、その結果をIEnumerableのboolとして返します。

プリミティブ型または文字列を検出した場合、それらの間に==が返されます。

コレクションから派生した型を検出すると、各メンバーを繰り返し処理し、それぞれのメンバーに対してMatchNodesを呼び出します(ud)。

他の型が見つかると、プロパティ/フィールドごとにMatchNodesが呼び出されます。

私の解決策は明らかにスタックオーバーフローの例外を求めていますが、オブジェクトの深さがどの程度深くなっているかわからないため、どのように改善するのかという手がかりはありません。

コード(それは地獄のように醜いです、してください泣かないようにしてみてください)

public static IEnumerable<bool> MatchNodes<T>(T n1, T n2) 
    { 
     Func<PropertyInfo, bool> func= null; 

     if (typeof(T) == typeof(String)) 
     { 
      String str1 = n1 as String; 
      String str2 = n2 as String; 
      func = new Func<PropertyInfo, bool>((property) => str1 == str2); 
     } 
     else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(typeof(T))) 
     { 
      System.Collections.IEnumerable e1 = (System.Collections.IEnumerable)n1; 
      System.Collections.IEnumerable e2 = (System.Collections.IEnumerable)n2; 
      func = new Func<PropertyInfo, bool>((property) => 
      { 
       foreach (var v1 in e1) 
       { 
        if (e2.GetEnumerator().MoveNext()) 
        { 
         var v2 = e2.GetEnumerator().Current; 
         if (((IEnumerable<bool>)MatchNodes(v1, v2)).All(b => b == true)) 
         { 
          return false; 
         } 
        } 
        else 
        { 
         return false; 
        } 
       } 
       if (e2.GetEnumerator().MoveNext()) 
       { 
        return false; 
       } 
       else return true; 
      }); 
     } 
     else if (typeof(T).IsPrimitive || typeof(T) == typeof(Decimal)) 
     { 
      func = new Func<PropertyInfo, bool>((property) => property.GetValue(n1, null) == property.GetValue(n2, null)); 
     } 
     else 
     { 
      func = new Func<PropertyInfo, bool>((property) => 
        ((IEnumerable<bool>)MatchNodes(property.GetValue(n1, null), 
        property.GetValue(n2, null))).All(b => b == true)); 
     } 

     foreach (PropertyInfo property in typeof(T).GetProperties().Where((property) => property.DeclaringType == typeof(T))) 
     { 
      bool result =func(property); 
      yield return result; 
     } 

    } 

私は見てよ、何が再帰的に私のメソッドを呼び出すことなく、オブジェクトにクロールするための方法です。明確にするために

EDIT

、例:MyClassProp1内部のすべてのClass1オブジェクトが同じを持って

  • n1.GetType()n2.GetType()があればClass2はtrueを返しますされている

    public class Class1 : RandomClassWithMoreProperties{ 
        public string Str1{get;set;} 
        public int Int1{get;set;} 
    } 
    
    public class Class2{ 
        public List<Class1> MyClassProp1 {get;set;} 
        public Class1 MyClassProp2 {get;set;} 
        public string MyStr {get;set;} 
    } 
    

    MatchNodes(n1,n2) 0、両方のオブジェクトのInt1

  • MyClassProp2両方のオブジェクトのInt1
  • MyStrが両方のオブジェクト

用等しく、IはRandomClassWithMorePropertiesから任意の特性を比較しないであろう、同じStr1を有しています。

+0

メソッドを自分で書き直すことなく、再帰的に記述できるメソッドもループとして書き込むことができます。難しいことは自明ではないかもしれませんが、それは既知のCSの基礎であり、自分自身を採用しています。 – user978122

+3

ループとしても、オブジェクトグラフのサイクルを監視する必要があります。また、あなたの再帰呼び出しは、 'T'ジェネリックパラメータとして' object'を使います。これは 'DeclaringType == typeof(T)'をチェックしているので、おそらくあなたが望むものではありません。あなたは正確に何をしようとしていますか?おそらくもっと良い方法があります。 –

+0

また、2つの文字列を与えるとどうなりますか? 'typeof(String).GetProperties()'のforeachプロパティは、 'str1 == str2'である' func'を呼び出します。これはおそらくあなたがすることではありません。 –

答えて

1

スタックまたはキューを使用して、比較するプロパティを格納できます。これは、これらの線に沿って行く:

var stack = new Stack<Tuple<object, object>>(); 

// prime the stack 
foreach (var prop in n1.GetType().GetProperties()) 
{ 
    stack.Push(Tuple.Create(prop.GetValue(n1), prop.GetValue(n2)); 
} 

while (stack.Count > 0) 
{ 
    var current = stack.Pop(); 

    // if current is promitive: compare 
    // if current is enumerable: push all elements as Tuples on the stack 
    // else: push all properties as tuples on the stack 
} 

あなたが代わりにあなたの代わりにDFSのBFSを取得StackQueueを使用している場合。また、すでに訪れたノードをHashSetで追跡しておくべきでしょう。また、n1n2のタイプが同じであることを確認するチェックを追加することもできます。

+0

ありがとう、それは私の問題を解決します。私は答えとして受け入れるだろうが、私は私の解決策を過度に複雑にしたと思う。私は20〜30のクラスのためにそれを行う必要があるので、私は各オブジェクトを手動で比較するかもしれません。 –

0

ここでの良いアプローチは、あなたが触れたオブジェクトのブレッドクラムトレイルを保持し、それをもっと深く探っていくことです。新しいオブジェクトごとに、既に見たオブジェクトのグラフにあるかどうかを確認し、存在する場合は、短絡が発生していることを確認してください(すでにノードを見ています)。スタックがおそらく適切です。

非周期的なオブジェクトグラフを比較することで、スタックオーバーフローが発生する可能性は低くなります。

0

だけ

また、任意の再帰は非再帰スタックを使用することができます...例えばList<object>(またはSet<>またはそのようなもの)で、すでに訪問したオブジェクトを追跡すること、あなたはよ手動で制御します。