2017-02-27 80 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

虽然我没有时间看你的问题,我已经与之前这样的比较器工作,想补充一点:不要忘了空类型(例如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非通用接口。请注意,我正在对每个元素进行类型测试。

为了不改变NonEqualProperty类我节省的差的ix直接在prop.Name(所以像SomeCollection_0SomeCollection_1如果不同的元素是0和1)。通常我会添加一个Index属性到NonEqualProperty对于“missing”元素,我使用的是"MISSING"字符串,如果您想用调试器查看它,那也没关系,但有更好的方法可以做到这一点(添加另一个财产“失踪”到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