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 inside MyClassProp1 has the same Str1,Int1 for both objects
MyClassProp2 has the same Str1,Int1 for both objects
MyStr is equal for both objects
And 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.
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.
Just keep track of the objects you already visited, in a List<object> for example (or Set<> or anything like that)...
Also, any recursion can be un-recursed using the stack that you'll control manually.
Related
I currently have two objects (of the same type) that may represent any primitive value such as string, int, datetime etc.
var valueX = ...;
var valueY = ...;
Atm I compare them on string level like this
var result = string.Compare(fieldValueX.ToString(), fieldValueY.ToString(), StringComparison.Ordinal);
But I need to compare them on type level (as ints if those happen to be ints
int i = 0;
int j = 2;
i.CompareTo(j);
, as dates if they happen to be date etc), something like
object.Compare(x,y);
That returns -1,0,1 in the same way. What are the ways to achieve that ?
Thanks for your answers, the correct way was to check if the object implements IComparable and if it does - make a typecast and call CompareTo
if (valueX is IComparable)
{
var compareResult = ((IComparable)valueX).CompareTo((IComparable)valueY);
}
Object1.Equals(obj1, obj2) wont work unless #object is referencing the same object.
EG:
var obj1 = new MyObject();
var obj2 = new MyObject();
This will return "False" for Object1.Equals(obj1, obj2) as they are different ref's
var obj1 = new MyObject();
var obj2 = obj1;
This will return "True" for Object1.Equals(obj1, obj2) as they are the same ref.
Solution:
You will most likely need to write an extension method that overrides Object.Equals. either create a custom object comparer for a specific type (See here for custom object comparer:) or you can dynamically go through each property and compare.
There's several options to do this.
Override Object.Equal
You can override the Object.Equal() method in the class, and then determine what makes the objects equal there. This can also let you cleverly decide what to compare, since it appears those objects can be multiple data types. Inside this override, you'll need to handle each possible case. You can read more about this option here:
https://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx
It should be noted by default, Object.Equal() will compare your objects references.
Implement IComparable
IComparable is a neat interface that gives an object Compare. As the comments mention, this will let you define how to compare the objects based on whatever criteria you want.
This option gets covered here: https://msdn.microsoft.com/en-us/library/system.icomparable(v=vs.110).aspx
Implement CompareBy() Methods
Alternatively, you can implement methods for each possible type, ie CompareByInt() or CompareByString(), but this method depends on you knowing what you're going to have when you go to do it. This will also have the negative effect of making code more difficult to maintain, as there's many more methods involved.
You can write a GeneralComparer with a Compare method, overloaded as necessary.
For types that must perform a standard comparison you can use EqualityComparer<T>.Default; for other types you write your own comparison function. Here's a sample:
static class GeneralComparer
{
public static int Compare(int x, int y)
{
//for int, use the standard comparison:
return EqualityComparer<int>.Default.Equals(x, y);
}
public static int Compare(string x, string y)
{
//for string, use custom comparison:
return string.Compare(x, y, StringComparison.Ordinal);
}
//overload for DateTime
//overload for MyType
//overload for object
//...
}
The correct overload is chosen at runtime.
There's a drawback: if you declare two int (or other specific types) as object, the object overload is called:
object a = 2;
object b = 3;
//this will call the "Compare(object x, object y)" overload!
int comparison = GeneralComparer.Compare(a, b);
converting the objects to dictionary, then following math set(s) concept subtract them, result items should be empty in case they are identically.
public static IDictionary<string, object> ToDictionary(this object source)
{
var fields = source.GetType().GetFields(
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source) ?? string.Empty
);
var properties = source.GetType().GetProperties(
BindingFlags.GetField |
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null) ?? string.Empty
);
return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
}
public static bool EqualsByValue(this object source, object destination)
{
var firstDic = source.ToFlattenDictionary();
var secondDic = destination.ToFlattenDictionary();
if (firstDic.Count != secondDic.Count)
return false;
if (firstDic.Keys.Except(secondDic.Keys).Any())
return false;
if (secondDic.Keys.Except(firstDic.Keys).Any())
return false;
return firstDic.All(pair =>
pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
);
}
public static bool IsAnonymousType(this object instance)
{
if (instance == null)
return false;
return instance.GetType().Namespace == null;
}
public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
{
var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
foreach (var item in source.ToDictionary())
{
var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
if (item.Value.IsAnonymousType())
return item.Value.ToFlattenDictionary(key, propsDic);
else
propsDic.Add(key, item.Value);
}
return propsDic;
}
originalObj.EqualsByValue(messageBody); // will compare values.
source of the code
I have an Entity Framework 4 project that has built up some brute-force searching code that I'd like to reduce to more generic, and more manageable chunks.
One of my Partial Classes, the Run object, contains Navigation Properties (Entity Collections) to other objects (Run.Nodes, Run.Arcs), as well as Scalars (GUID, Version #), and singlular Navigation Properties (Entity Objects - Run.TimeRange).
Run.Nodes is a Base Class collection of NodeBase, with derived classes of NodeTypeA, NodeTypeB, and NodeTypeC.
Using Reflection:
public EntityObject FindDiscriminant<T>(T needle) where T : EntityObject
{
Boolean test = false;
Type sourceType = this.GetType();
String needleString = needle.GetType().BaseType.Name.ToString();
String needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
//If we don't match anything that means that the object itself is a base class, so we need to try again
if (needleStringLookup == null)
{
needleString = needle.GetType().Name.ToString();
needleStringLookup = typeDict.Where(o => o.Key == needleString).FirstOrDefault().Value;
}
var needleProperty = Type.GetType(sourceType.FullName).GetProperty(needleStringLookup);
var runValue = needleProperty.GetValue(this, null);
if (runValue.GetType().ToString().Contains("EntityCollection"))
{
foreach (var obj in (runValue as EntityCollection<T>).ToList())
{
test = (obj as T).Discriminant(needle);
if (test == true)
return obj;
}
}
else
{
test = (runValue as EntityObject).Discriminant(needle);
if (test == true)
return (T)runValue;
}
return null;
}
This method works great for EntityCollections (except NodeBase). If I try and look for a node of NodeTypeC in Run.Nodes, runValue will be an EntityCollection of 173 NodeBase objects. But when I try and iterate over it (.ToList()), I get this error:
System.ArgumentNullException was unhandled
Value cannot be null.
Parameter name: source
My workaround is to check to see of the EntityCollection is of type NodeBase, and have an if statement to handle it, and substitute EntityCollection).ToList() for EntityCollection).ToList()
Any suggestions?
An update to my question, for anyone searching this. The code has changed dramatically, and I'm now using Delegates as SearchActions, and have a generic FindSomething routine that uses those delegates instead of having several search routines each using their own type of input.
The things to note are:
The method of detection for determining if my object I pulled with
reflection is an EntityObject or an EntityCollection
I use a private method to iterate over the EntityCollection that I
pass from my generic FindSomething routine. This takes care of the
base-class comparisons
By having the private method to call, I avoid having to use casting on the EntityCollection - this goes away: (runValue as EntityCollection) as well as (obj as T)
I have created a dynamic Object Dictionary when I instantiate our
application - I go through our collection of objects and map objects
and the properties we care about so I don't have to brute force
through an entire object every search
I use dynamic instead of var - I love dynamic! And I no longer cast
before doing a search.
The function is recursive - the SearchAction delegate gets called
again during the iteration code in the IterateThroughEntityCollection
method.
Good? Bad? Comments? Feedback? It works for me, and it's fast.
Here's the revised code:
private EntityObject FindSomething<T>(Run haystack, T needle, SearchAction<T> sa)
{
//First, assume we haven't found anything
Boolean test = false;
//Next, go through all the objects in a run and see if we find anything
//No need to go through Arcs, if the needle is a type of NodeBase, etc.
Type oldRunElementProperty = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Key;
PropertyInfo runValuePropertyToChange = TypeReference.RunElementDictionary.Where(o => o.Key == type).Single().Value;
dynamic runValue = runValuePropertyToChange.GetValue(haystack, null);
//Check to see if we're dealing with an EntityCollection or an EntityObject. If it is an EntityObject, we can set that value
//directly. If it is a collection, we need to use the generic method
if (runValuePropertyToChange.PropertyType.IsGenericType && runValuePropertyToChange.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
{
EntityObject result = IterateThroughEntityCollection(runValue, needle, sa);
if (result != null) return result;
}
else
{
test = sa(runValue, needle); if (test == true) return runValue;
}
return null;
}
Private EntityCollection iterator.
private EntityObject IterateThroughEntityCollection<T,U>(EntityCollection<T> haystack, U needle, SearchAction<U> sa) where T: EntityObject
{
Boolean test = false;
foreach(dynamic obj in haystack)
{
test = sa(obj, needle);
if (test == true) return obj;
}
return null;
}
I have a method that is supposed to check whether there is exactly one element in a collection that holds true for some predicate (given as a Func).
public bool ExistsUnique(Func<T, bool> p)
{
var tempCol = from i in MyCollection where p(i) select i;
return (tempCol.Count() == 1);
}
The problem with this is that when a second element that also holds true for the predicate
is found (for example two of the same string exists in a collection) the count is still 1. Which means it either overrides the first element or never adds the second because it already exists.
Any ideas as to how I can fix this method?
thx
/Peter
You can use the Single() method provided by LINQ like this:
public bool ExistsUnique(Func<T, bool> p)
{
try
{
var temp = myCollection.Single(x => p(x));
}
catch(Exception e)
{
// log exception
return false;
}
return true;
}
"Returns the only element of a sequence that satisfies a specified condition, and throws
an exception if more than one such element exists."
From http://msdn.microsoft.com/en-us/library/bb535118.aspx
EDIT
To avoid throwing an exception, you may also use the SingleOrDefault() method:
public bool ExistsUnique(Func<T, bool> p)
{
return myCollection.SingleOrDefault(x => p(x)) != null;
}
There must be some other problem. I'd suspect your predicate. For example, this returns a count of 2, as expected:
List<string> MyCollection = new List<string>()
{
"hello",
"hello"
};
var tempCol = from i in MyCollection where i == "hello" select i;
int count = tempCol.Count();
I doubt that it's the way you're calling it, either. The following works (returns false):
static List<string> MyCollection = new List<string>()
{
"hello",
"hello"
};
static bool ExistsUnique(Func<string, bool> p)
{
var tempCol = from i in MyCollection where p(i) select i;
return tempCol.Count() == 1;
}
static void DoIt()
{
bool isUnique = ExistsUnique((s) => s.Equals("hello"));
Console.WriteLine(isUnique);
}
Are you sure tempCol has looped completely through MyCollection?
is Count() a method that forces the complete loop or is it lazy?
Does for example tempCol.ToList().Count give the correct result?
This implementation would make it so you don't have to actually enumerate the entire collection, so will save you some execution time.
public bool ExistsUnique(Func<T, bool> p)
{
return MyCollection.Where(i => p(i)).Take(2).Count() == 1;
}
The Take(2) limits the Count to only enumerate the first two meeting the criteria.
I am writing tests against our Caching mechanism and I want to be sure that what goes into the cache is the same as what comes out, ie that all properties match. Here is a fictional example of how I would like it to work
[Test]
public void add_client_model_member_to_cache_then_retreve()
{
//arrange
MemcachedClient client = new MemcachedClient();
Member member = GetMember();
client.Store(StoreMode.Set, "membertest", member);
// act
var result = client.Get<Member>("membertest");
// assert
Assert.AreMatch(result, member);
}
It is not feasible to perform Assert.AreEqual on each property as there will be many of these tests with many properties in each.
Wow, thanks Jon. I think you answered that in under one minute. Here are my resulting solution for any interested parties. I implemented as Jon suggested (I think) however I got into a little trouble with array properties and as such my solution only handles arrays of ints (all I currently require).
It also got to be a fairly slippery slope if I try to check deeper than one level. I am sure this can be achieved however for my purposes it is not required.
private bool AreMatch(object initial, object result)
{
if (initial.Equals(result))
return true;
foreach (var property in initial.GetType().GetProperties())
{
var initialPropValue = property.GetValue(initial,null);
var resultPropValue = result.GetType().GetProperty(property.Name).GetValue(result,null);
if (property.PropertyType.IsArray)
{
if ((initialPropValue != null && resultPropValue != null) && !Enumerable.SequenceEqual((int[])initialPropValue, (int[])resultPropValue))
return false;
}
else if (!object.Equals(initialPropValue, resultPropValue))
{
return false;
}
else if (initialPropValue != null && property.PropertyType.IsClass)
{
// Don't go deeper than one level, this got me into trouble
//if (!AreMatch(initialPropValue, resultPropValue))
// return false;
}
}
return true;
}
The method above can be used in the following situations
[Test]
public void cached_result_is_same_as_original()
{
//arrange
Member member = GetMember();
client.Store(StoreMode.Set, "membertest", member);
// act
var result = client.Get<Member>("membertest");
// assert
Assert.IsTrue(AreMatch(member, result));
}
[Test]
public void cached_result_is_same_as_original()
{
//arrange
Member member = GetMember();
client.Store(StoreMode.Set, "membertest", member);
// act
var result = client.Get<Member>("membertest");
result.FirstName = "Result is different";
// assert
Assert.IsFalse(AreMatch(member, result));
}
Well, you could certainly write something to recurse through all the properties, and call object.Equals on the result of fetching the property value from the expected and actual ones. Use Type.GetProperties() to get the properties themselves, and PropertyInfo.GetValue to get the value.
It'll be somewhat crude, but you could always tweak it if necessary.
Let's say I have a complex .NET class, with lots of arrays and other class object members. I need to be able to generate a deep clone of this object - so I write a Clone() method, and implement it with a simple BinaryFormatter serialize/deserialize - or perhaps I do the deep clone using some other technique which is more error prone and I'd like to make sure is tested.
OK, so now (ok, I should have done it first) I'd like write tests which cover the cloning. All the members of the class are private, and my architecture is so good (!) that I haven't needed to write hundreds of public properties or other accessors. The class isn't IComparable or IEquatable, because that's not needed by the application. My unit tests are in a separate assembly to the production code.
What approaches do people take to testing that the cloned object is a good copy? Do you write (or rewrite once you discover the need for the clone) all your unit tests for the class so that they can be invoked with either a 'virgin' object or with a clone of it? How would you test if part of the cloning wasn't deep enough - as this is just the kind of problem which can give hideous-to-find bugs later?
You method of testing will depend on the type of solution you come up with. If you write some custom cloning code and have to manually implement that in each cloneable type then you should really test the cloning of each one of those types. Alternatively, if you decide to go a more generic route (where the aforementioned reflection would likely fit in), your tests would only need to test the specific scenarios that you cloning system will have to deal with.
To answer your specific questions:
Do you write (or rewrite once you discover the need for the clone) all your unit tests for the class so that they can be invoked with either a 'virgin' object or with a clone of it?
You should have tests for all the methods that can be performed on both the original and cloned objects. Note that it should be pretty easy to set up a simple test design to support this without manually updating the logic for each test.
How would you test if part of the cloning wasn't deep enough - as this is just the kind of problem which can give hideous-to-find bugs later?
It depends on the cloning method you choose. If you have to manually update the cloneable types then you should test that each type is cloning all (and only) the members you expect. Whereas, if you are testing a cloning framework I would create some test cloneable types to test each scenario you need to support.
There's a really obvious solution that doesn't take nearly as much work:
Serialize the object into a binary format.
Clone the object.
Serialize the clone into a binary format.
Compare the bytes.
Assuming that serialization works - and it better because you are using it to clone - this should be easy to maintain. In fact, it will be encapsulated from changes to the structure of your class completely.
I'd just write a single test to determine if the clone was correct or not. If the class isn't sealed, you can create a harness for it by extending it, and then exposing all your internals within the child class. Alternatively, you could use reflection (yech), or use MSTest's Accessor generators.
You need to clone your object and then go through every single property and variable that your object has and determine if it was copied correctly or cloned correctly.
I like to write unit tests that use one of the builtin serializers on the original and the cloned object and then check the serialized representations for equality (for a binary formatter, I can just compare the byte arrays). This works great in cases where the object is still serializable, and I'm only changing to a custom deep clone for perf reasons.
Furthermore, I like to add a debug mode check to all of my Clone implementations using something like this
[Conditional("DEBUG")]
public static void DebugAssertValueEquality<T>(T current, T other, bool expected,
params string[] ignoredFields) {
if (null == current)
{ throw new ArgumentNullException("current"); }
if (null == ignoredFields)
{ ignoredFields = new string[] { }; }
FieldInfo lastField = null;
bool test;
if (object.ReferenceEquals(other, null))
{ Debug.Assert(false == expected, "The other object was null"); return; }
test = true;
foreach (FieldInfo fi in current.GetType().GetFields(BindingFlags.Instance)) {
if (test = false) { break; }
if (0 <= Array.IndexOf<string>(ignoredFields, fi.Name))
{ continue; }
lastField = fi;
object leftValue = fi.GetValue(current);
object rightValue = fi.GetValue(other);
if (object.ReferenceEquals(null, leftValue)) {
if (!object.ReferenceEquals(null, rightValue))
{ test = false; }
}
else if (object.ReferenceEquals(null, rightValue))
{ test = false; }
else {
if (!leftValue.Equals(rightValue))
{ test = false; }
}
}
Debug.Assert(test == expected, string.Format("field: {0}", lastField));
}
This method relies on an accurate implementation of Equals on any nested members, but in my case anything that is cloneable is also equatable
I would usually implement Equals() for comparing the two objects in depth. You might not need it in your production code but it might still come in handy later and the test code is much cleaner.
Here is a sample of how I implemented this a while back, although this will need to be tailored to the scenario. In this case we had a nasty object chain that could easily change and the clone was used as a very critical prototype implementation and so I had to patch (hack) this test together.
public static class TestDeepClone
{
private static readonly List<long> objectIDs = new List<long>();
private static readonly ObjectIDGenerator objectIdGenerator = new ObjectIDGenerator();
public static bool DefaultCloneExclusionsCheck(Object obj)
{
return
obj is ValueType ||
obj is string ||
obj is Delegate ||
obj is IEnumerable;
}
/// <summary>
/// Executes various assertions to ensure the validity of a deep copy for any object including its compositions
/// </summary>
/// <param name="original">The original object</param>
/// <param name="copy">The cloned object</param>
/// <param name="checkExclude">A predicate for any exclusions to be done, i.e not to expect IPolicy items to be cloned</param>
public static void AssertDeepClone(this Object original, Object copy, Predicate<object> checkExclude)
{
bool isKnown;
if (original == null) return;
if (copy == null) Assert.Fail("Copy is null while original is not", original, copy);
var id = objectIdGenerator.GetId(original, out isKnown); //Avoid checking the same object more than once
if (!objectIDs.Contains(id))
{
objectIDs.Add(id);
}
else
{
return;
}
if (!checkExclude(original))
{
Assert.That(ReferenceEquals(original, copy) == false);
}
Type type = original.GetType();
PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
FieldInfo[] fieldInfos = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo memberInfo in propertyInfos)
{
var getmethod = memberInfo.GetGetMethod();
if (getmethod == null) continue;
var originalValue = getmethod.Invoke(original, new object[] { });
var copyValue = getmethod.Invoke(copy, new object[] { });
if (originalValue == null) continue;
if (!checkExclude(originalValue))
{
Assert.That(ReferenceEquals(originalValue, copyValue) == false);
}
if (originalValue is IEnumerable && !(originalValue is string))
{
var originalValueEnumerable = originalValue as IEnumerable;
var copyValueEnumerable = copyValue as IEnumerable;
if (copyValueEnumerable == null) Assert.Fail("Copy is null while original is not", new[] { original, copy });
int count = 0;
List<object> items = copyValueEnumerable.Cast<object>().ToList();
foreach (object o in originalValueEnumerable)
{
AssertDeepClone(o, items[count], checkExclude);
count++;
}
}
else
{
//Recurse over reference types to check deep clone success
if (!checkExclude(originalValue))
{
AssertDeepClone(originalValue, copyValue, checkExclude);
}
if (originalValue is ValueType && !(originalValue is Guid))
{
//check value of non reference type
Assert.That(originalValue.Equals(copyValue));
}
}
}
foreach (FieldInfo fieldInfo in fieldInfos)
{
var originalValue = fieldInfo.GetValue(original);
var copyValue = fieldInfo.GetValue(copy);
if (originalValue == null) continue;
if (!checkExclude(originalValue))
{
Assert.That(ReferenceEquals(originalValue, copyValue) == false);
}
if (originalValue is IEnumerable && !(originalValue is string))
{
var originalValueEnumerable = originalValue as IEnumerable;
var copyValueEnumerable = copyValue as IEnumerable;
if (copyValueEnumerable == null) Assert.Fail("Copy is null while original is not", new[] { original, copy });
int count = 0;
List<object> items = copyValueEnumerable.Cast<object>().ToList();
foreach (object o in originalValueEnumerable)
{
AssertDeepClone(o, items[count], checkExclude);
count++;
}
}
else
{
//Recurse over reference types to check deep clone success
if (!checkExclude(originalValue))
{
AssertDeepClone(originalValue, copyValue, checkExclude);
}
if (originalValue is ValueType && !(originalValue is Guid))
{
//check value of non reference type
Assert.That(originalValue.Equals(copyValue));
}
}
}
}
}