C# 使用反射的通用比较器

C# 使用反射的通用比较器,c#,generics,reflection,comparison,C#,Generics,Reflection,Comparison,我正在为单元测试准备一个简单的通用比较器。 我不想使用IComparable接口,因为需要实现它的类太多了,而且它只在单元测试中使用,所以反射的性能不是问题 到目前为止,我有: public IEnumerable<NonEqualProperty> Compare<T>(T first, T second) where T : class { var list = new List<NonEqualProperty>

我正在为单元测试准备一个简单的通用比较器。 我不想使用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);
    }

}
我将如何实现这一点


这里不考虑不相等的计数或列表中项目的顺序-我稍后会解决它。

类似于此:

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接口的方法,如:

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;

虽然我没有时间看你的问题,但我以前使用过这样的比较器,我想补充一点:不要忘记可为null的类型,例如int?由于存储机制和舍入错误的不同,日期和浮点数等可能无法进行比较。他们会在某个时候出现并让你惊讶:问题是什么?为什么不使用NUnit这样的测试库呢?它提供了您正在寻找的功能,等等。不要使用IEnumerable,请使用基本IEnumerable非泛型类型。这要容易得多。@mireigi你能给我提供一个链接/名称,指向NUnit的特定组件/类吗?我从来没用过NUnit,但也许现在我会试试:看起来很有希望:-我得仔细看看。好吧。。经过几次调整后,它应该正是我所需要的,谢谢:
    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;