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:
Class1
object inside MyClassProp1
has the same Str1
,Int1
for both objectsMyClassProp2
has the same Str1
,Int1
for both objectsMyStr
is equal for both objectsAnd I won't compare any properties from RandomClassWithMoreProperties
.
You can use a stack or queue to store the properties you want to compare. It goes along these lines:
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
}
If you use a Queue
instead of a Stack
you get a BFS instead of a DFS. Also you should probably keep track of already visited nodes in a HashSet
. You also might want to add a check to make sure the types of n1
and n2
are the same.