使用反射

时间:2017-02-27 08:53:35

标签: c# generics reflection comparison

我正在将简单的通用比较器放在一起进行单元测试 我不想使用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 个答案:

答案 0 :(得分: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属性。对于&#34;缺少&#34;我使用"MISSING"字符串的元素,如果你想用调试器看看它就没关系,但有更好的方法可以做到这一点(添加另一个属性&#34; Missing&#34 ;例如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;