C# 使用反射的通用比较器
我正在为单元测试准备一个简单的通用比较器。 我不想使用IComparable接口,因为需要实现它的类太多了,而且它只在单元测试中使用,所以反射的性能不是问题 到目前为止,我有: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>
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;