2017-02-27 14 views
0

単体テストのための簡単な汎用比較器をまとめています。
IComparableインターフェイスを使用したくないのは、それを実装する必要があるクラスがあまりにも多く、ユニットテストでのみ使用されるため、リフレクションのパフォーマンスに問題はありません。リフレクションを使用した一般的な比較者

は、これまでのところ私はこれを持っている:

public IEnumerable<NonEqualProperty> Compare<T>(T first, T second) where T : class 
     { 
      var list = new List<NonEqualProperty>(); 
      var type = first.GetType(); 
      var properties = type.GetProperties(); 

      var basicTypes = properties.Where(p => !p.PropertyType.IsClass && !p.PropertyType.IsInterface 
                || p.PropertyType == typeof(string)); 


      foreach (var prop in basicTypes) 
      { 
       var value1 = prop.GetValue(first, null); 
       var value2 = prop.GetValue(second, null); 

       if (value1 != null && value2 != null && value1.Equals(value2) || value1 == null && value2 == null) 
        continue; 

       list.Add(new NonEqualProperty(prop.Name, value1, value2)); 
      } 

      var enumerableTypes = 
       from prop in properties 
       from interfaceType in prop.PropertyType.GetInterfaces() 
       where interfaceType.IsGenericType 
       let baseInterface = interfaceType.GetGenericTypeDefinition() 
       where prop.PropertyType != typeof(string) && baseInterface == typeof(IEnumerable<>) || baseInterface == typeof(IEnumerable) 
       select prop; 

      foreach (var prop in enumerableTypes) 
      { 
       var collection = prop.GetValue(first, null); 
      } 


      return list; 
     } 

だから、すべての単純タイプ+文字列の作品の比較。

今、IEnumerableを反復したいと思います(クラスは常に列挙できますが、そうでない場合は大事です)、再帰を使用して値を比較します。このようなもの:

foreach (var prop in enumerableTypes) 
{ 
    var typeOfItemsInList= ...; 
    var collection1 = (IEnumerable<typeOfItemsInList>) prop.GetValue(first, null); 
    var collection2 = (IEnumerable<typeOfItemsInList>) prop.GetValue(second, null); 

    for (int i = 0; i < collection1.Count; i++) 
    { 
     var item1 = collection1[i]; 
     var item2 = collection2[i]; 

     Compare<typeOfItemsInList>(item1, item2, list); 
    } 

} 

どうすれば達成できますか?

ここではリスト内の項目の順序や順序は考慮されていません。後で修正します。これに似た

+1

私はあなたの質問を見て時間を持っていませんが、私は前に、このような比較演算子で働いてきたし、これを追加したい:例えば(Null許容型を忘れてはいけませんint?)、日付と浮動小数点数などは、格納メカニズムと丸め誤差の違いによって比較に失敗する可能性があります。彼らは周りに来て、そうでなければ何かの点であなたを驚かせます:) – john

+2

質問は何ですか? –

+0

NUnitなどのテストライブラリを使用するだけではどうですか?これは、あなたが探している機能などを提供します。 – mireigi

答えて

1

何か:

public static IEnumerable<NonEqualProperty> Compare<T>(T first, T second) where T : class { 
    var list = new List<NonEqualProperty>(); 
    var type = first.GetType(); 
    var properties = type.GetProperties(); 

    var basicTypes = properties.Where(p => !p.PropertyType.IsClass && !p.PropertyType.IsInterface 
              || p.PropertyType == typeof(string)); 

    foreach (var prop in basicTypes) { 
     var value1 = prop.GetValue(first, null); 
     var value2 = prop.GetValue(second, null); 

     if (object.Equals(value1, value2)) 
      continue; 

     list.Add(new NonEqualProperty(prop.Name, value1, value2)); 
    } 

    var enumerableTypes = 
     from prop in properties 
     where prop.PropertyType == typeof(IEnumerable) || 
      prop.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)) 
     select prop; 

    foreach (var prop in enumerableTypes) { 
     var value1 = (IEnumerable)prop.GetValue(first, null); 
     var value2 = (IEnumerable)prop.GetValue(second, null); 

     if (object.Equals(value1, value2)) 
      continue; 

     if (value1 == null || value2 == null) { 
      list.Add(new NonEqualProperty(prop.Name, value1, value2)); 
      continue; 
     } 

     IEnumerator enu1 = null, enu2 = null; 

     try { 
      try { 
       enu1 = value1.GetEnumerator(); 
       enu2 = value2.GetEnumerator(); 

       int ix = -1; 

       while (true) { 
        bool next1 = enu1.MoveNext(); 
        bool next2 = enu2.MoveNext(); 
        ix++; 

        if (!next1) { 
         while (next2) { 
          list.Add(new NonEqualProperty(prop.Name + "_" + ix, "MISSING", enu2.Current)); 
          ix++; 
          next2 = enu2.MoveNext(); 
         } 

         break; 
        } 

        if (!next2) { 
         while (next1) { 
          list.Add(new NonEqualProperty(prop.Name + "_" + ix, enu1.Current, "MISSING")); 
          ix++; 
          next1 = enu1.MoveNext(); 
         } 

         break; 
        } 

        if (enu1.Current != null) { 
         var type1 = enu1.Current.GetType(); 

         if ((type1.IsClass || type1.IsInterface) && type1 != typeof(string)) { 
          continue; 
         } 
        } 

        if (enu2.Current != null) { 
         var type2 = enu2.Current.GetType(); 

         if ((type2.IsClass || type2.IsInterface) && type2 != typeof(string)) { 
          continue; 
         } 
        } 

        if (!object.Equals(enu1.Current, enu2.Current)) { 
         list.Add(new NonEqualProperty(prop.Name + "_" + ix, enu1.Current, enu2.Current)); 
        } 
       } 
      } finally { 
       var disp2 = enu2 as IDisposable; 

       if (disp2 != null) { 
        disp2.Dispose(); 
       } 
      } 
     } finally { 
      var disp1 = enu1 as IDisposable; 

      if (disp1 != null) { 
       disp1.Dispose(); 
      } 
     } 
    } 

    return list; 
} 

私はIEnumerable非ジェネリックインターフェイスを使用しています。各要素について型テストを行っていることに注意してください。

(異なる要素が0と1であればそうSomeCollection_0SomeCollection_1のように)私はprop.Nameに直接差のixを保存していNonEqualPropertyクラスを変更しないように。通常はIndexプロパティをNonEqualPropertyに追加します。「欠落している」要素については、"MISSING"文字列を使用しています。これは、デバッガで見たい場合はOKですが、それを行うためのより良い方法があります例えばNonEqualPropertyに「欠落」プロパティ、または

public static readonly object Missing = new object(); 

を行い、欠損値のためにそれを使用

ようIEnumerableインタフェースを使用せず、他の方法があります。

private static IEnumerable<NonEqualProperty> CompareCollection<T>(IEnumerable<T> firstColl, IEnumerable<T> secondColl) { 
    var list = new List<NonEqualProperty>(); 

    // Do the comparison 

    return list; 
} 

private static MethodInfo CompareCollectionMethod = typeof(Program).GetMethod("CompareCollection", BindingFlags.Static | BindingFlags.NonPublic); 
は、

その後、

var value1 = (IEnumerable)prop.GetValue(first, null); 
    var value2 = (IEnumerable)prop.GetValue(second, null); 

    if (object.Equals(value1, value2)) 
     continue; 

    if (prop.PropertyType != typeof(IEnumerable)) { 
     var ienumt = prop.PropertyType.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)).FirstOrDefault(); 

     if (ienumt != null) { 
      var t = ienumt.GetGenericArguments(); // T of IEnumerable<T> 

      if ((t[0].IsClass || t[0].IsInterface) && t[0] != typeof(string)) { 
       continue; 
      } 

      var method = CompareCollectionMethod.MakeGenericMethod(t); 
      var result = (IEnumerable<NonEqualProperty>)method.Invoke(null, new[] { value1, value2 }); 
      list.AddRange(result); 
      continue; 
     } 
    } 

    if (value1 == null || value2 == null) { 
     list.Add(new NonEqualProperty(prop.Name, value1, value2)); 
     continue; 
    } 

    // continue with the code for non-generic IEnumerable 

    IEnumerator enu1 = null, enu2 = null; 
+0

有望見える:) - 私はしかし、もっと近くに見てみる必要があります。 – quin61

+0

申し訳ありません。私は何が必要なのか、ちょっとした調整が必要です。 – quin61

関連する問題