我有这个方法叫做MatchNodes: IEnumerable<bool> MatchNodes<T>(T n1, T n2)
它基本上从两个T
对象(通过反射,不包括基类的属性/字段)获得每个属性和字段,并比较它们,返回结果作为一个IEnumerable of bools。
当它找到一个基本类型或字符串时,if只返回它们之间的==
。
当它找到从集合派生的类型时,它迭代每个成员并为每个成员调用MatchNodes
(哎哟)。
当发现任何其他类型时,它为每个属性/字段调用MatchNodes
。
我的解决方案显然是请求堆栈溢出异常,但我不知道如何使它更好,因为我不知道对象将进入多深。
代码(请尽量不要哭,它很难看):
public static IEnumerable<bool> MatchNodes<T>(T n1, T n2)
{
Func<PropertyInfo, bool> func= null;
if (typeof(T) == typeof(String))
{
String str1 = n1 as String;
String str2 = n2 as String;
func = new Func<PropertyInfo, bool>((property) => str1 == str2);
}
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(typeof(T)))
{
System.Collections.IEnumerable e1 = (System.Collections.IEnumerable)n1;
System.Collections.IEnumerable e2 = (System.Collections.IEnumerable)n2;
func = new Func<PropertyInfo, bool>((property) =>
{
foreach (var v1 in e1)
{
if (e2.GetEnumerator().MoveNext())
{
var v2 = e2.GetEnumerator().Current;
if (((IEnumerable<bool>)MatchNodes(v1, v2)).All(b => b == true))
{
return false;
}
}
else
{
return false;
}
}
if (e2.GetEnumerator().MoveNext())
{
return false;
}
else return true;
});
}
else if (typeof(T).IsPrimitive || typeof(T) == typeof(Decimal))
{
func = new Func<PropertyInfo, bool>((property) => property.GetValue(n1, null) == property.GetValue(n2, null));
}
else
{
func = new Func<PropertyInfo, bool>((property) =>
((IEnumerable<bool>)MatchNodes(property.GetValue(n1, null),
property.GetValue(n2, null))).All(b => b == true));
}
foreach (PropertyInfo property in typeof(T).GetProperties().Where((property) => property.DeclaringType == typeof(T)))
{
bool result =func(property);
yield return result;
}
}
我正在寻找的是一种无需递归调用我的方法就可以爬进对象的方法。
编辑
说明一下,例如:
public class Class1 : RandomClassWithMoreProperties{
public string Str1{get;set;}
public int Int1{get;set;}
}
public class Class2{
public List<Class1> MyClassProp1 {get;set;}
public Class1 MyClassProp2 {get;set;}
public string MyStr {get;set;}
}
MatchNodes(n1,n2)
,其中n1.GetType()
和n2.GetType()
是Class2
,如果:
-
MyClassProp1
中的每个Class1
对象都有相同的Str1
,Int1
-
MyClassProp2
对于两个对象具有相同的Str1
,Int1
-
MyStr
对于两个对象是相等的
我不会比较RandomClassWithMoreProperties
中的任何属性
可以使用堆栈或队列来存储要比较的属性。它是这样的:
var stack = new Stack<Tuple<object, object>>();
// prime the stack
foreach (var prop in n1.GetType().GetProperties())
{
stack.Push(Tuple.Create(prop.GetValue(n1), prop.GetValue(n2));
}
while (stack.Count > 0)
{
var current = stack.Pop();
// if current is promitive: compare
// if current is enumerable: push all elements as Tuples on the stack
// else: push all properties as tuples on the stack
}
如果你使用Queue
而不是Stack
,你得到的是BFS而不是DFS。此外,您可能还应该跟踪HashSet
中已经访问过的节点。您可能还需要添加一个检查,以确保n1
和n2
的类型相同。
这里的一个好方法是保留您所接触过的对象的蛛皮迹,并在您深入研究时传递它。对于每个新对象,检查它是否在您已经看到的对象图中,如果是,则短路并跳出(您已经看到了该节点)。栈可能是合适的。
你不太可能通过比较一个无循环的对象图得到堆栈溢出——当你以循环结束时,事情就会爆炸。
只需跟踪您已经访问过的对象,例如在List<object>
(或Set<>
或类似的东西)…
此外,任何递归都可以使用您手动控制的堆栈来反递归。