通过 C# 中的反射在对象内部爬行时防止堆栈溢出
我有一个名为 MatchNodes 的方法: IEnumerable
基本上从两个 T
对象获取每个属性和字段(通过反射,不包括来自基类的属性/字段)并比较它们,将结果作为 bool 的 IEnumerable 返回。
当它找到原始类型或字符串时,如果只是返回它们之间的 ==
。
当它找到从集合派生的类型时,它会迭代每个成员并为每个成员调用 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
将返回 true:
MyClassProp1
中的每个Class1
对象都具有相同的Str1
、两者均为 Int1
对象MyClassProp2
具有相同的Str1
,两个对象的Int1
MyStr
对于两个对象来说是相等的
,我不会比较 RandomClassWithMoreProperties
中的任何属性。
I have this method called MatchNodes: IEnumerable<bool> MatchNodes<T>(T n1, T n2)
Which basically gets every property and field from both T
objects (via reflection, and not including properties/fields from base classes) and compares them, returning the result as a IEnumerable of bools.
When it finds a primitive type or string, if just returns the ==
between them.
When it finds a type derived from a collection, it iterates each member and calls MatchNodes
for each of them (ouch).
When it finds any other type, it calls MatchNodes
for each property/field.
My solution is obviously asking for a stack overflow exception, but I don't have a clue on how make it better, because I have no idea how deep the objects will go.
Code (try not to cry please, it's ugly as hell):
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;
}
}
What I'm looking at is a way to crawl into the objects without calling my method recursively.
EDIT
To clarify, example:
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)
where n1.GetType()
and n2.GetType()
are Class2
would return true if:
- Every
Class1
object insideMyClassProp1
has the sameStr1
,Int1
for both objects MyClassProp2
has the sameStr1
,Int1
for both objectsMyStr
is equal for both objects
And I won't compare any properties from RandomClassWithMoreProperties
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可以使用堆栈或队列来存储要比较的属性。它是这样的:
如果您使用
Queue
而不是Stack
,您将获得BFS而不是DFS。此外,您可能应该跟踪HashSet
中已访问过的节点。您可能还需要添加检查以确保n1
和n2
的类型相同。You can use a stack or queue to store the properties you want to compare. It goes along these lines:
If you use a
Queue
instead of aStack
you get a BFS instead of a DFS. Also you should probably keep track of already visited nodes in aHashSet
. You also might want to add a check to make sure the types ofn1
andn2
are the same.这里的一个好方法是保留您所触摸过的对象的痕迹,并在您深入研究时将其向前传递。对于每个新对象,检查它是否在您已经见过的对象图中,如果是,则短路并跳出(您已经见过该节点)。堆栈可能是合适的。
通过比较非循环对象图,您不太可能出现堆栈溢出 - 当您最终遇到循环时,事情就会崩溃。
A good approach here is to keep a breadcrumb trail of objects that you've touched, and passing that forward as you delve deeper. For each new object, check to see whether it is in the graph of objects that you have already seen, and if it is, short circuit and bail out (you've already seen that node). A stack is probably appropriate.
You are not likely to get stack overflows by comparing an acyclic object graph- it's when you end up with loops that things blow up.
只需跟踪您已经访问过的对象,例如在
List
此外,任何递归可以使用您手动控制的堆栈来取消递归。
Just keep track of the objects you already visited, in a
List<object>
for example (orSet<>
or anything like that)...Also, any recursion can be un-recursed using the stack that you'll control manually.