我正在将简单的通用比较器放在一起进行单元测试 我不想使用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);
}
}
我将如何实现这一目标?
此处不考虑不相等的计数或列表中的项目顺序 - 我稍后会修复它。
答案 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_0
,SomeCollection_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;