Compare two objects using serialization C#
The primary problem is that it is inefficient
As an example imagine this Equals function
public bool Equals(T x, T y)
{
return x.Prop1 == y.Prop1
&& x.Prop2 == y.Prop2
&& x.Prop3 == y.Prop3
&& x.Prop4 == y.Prop4
&& x.Prop5 == y.Prop5
&& x.Prop6 == y.Prop6;
}
if prop1 are not the same then the other 5 compares never need to be checked, if you did this with JSON you would have to convert the entire object into a JSON string then compare the string every time, this is on top of serialization being an expensive task all on its own.
Then the next problem is serialization is designed for communication e.g. from memory to a file, across a network, etc. If you have leveraged serialization for comparison you can degrade your ability to use it for it normal use, i.e. you can't ignore fields not required for transmission because ignoring them might break your comparer.
Next JSON in specific is Type-less which means than values than are not in anyway shape or form equal may be mistaken for being equal, and in the flipside values that are equal may not compare as equal due to formatting if they serialize to the same value, this is again unsafe and unstable
The only upside to this technique is that is requires little effort for the programmer to implement
You probably going to keep adding a bounty to the question until somebody tells you that it is just fine to do this. So you got it, don't hesitate to take advantage of the NewtonSoft.Json library to keep the code simple. You just need some good arguments to defend your decision if your code is ever reviewed or if somebody else takes over the maintenance of the code.
Some of the objections they may raise, and their counter-arguments:
This is very inefficient code!
It certainly is, particularly GetHashCode() can make your code brutally slow if you ever use the object in a Dictionary or HashSet.
Best counter-argument is to note that efficiency is of little concern in a unit test. The most typical unit test takes longer to get started than to actually execute and whether it takes 1 millisecond or 1 second is not relevant. And a problem you are likely to discover very early.
You are unit-testing a library you did not write!
That is certainly a valid concern, you are in effect testing NewtonSoft.Json's ability to generate a consistent string representation of an object. There is cause to be alarmed about this, in particular floating point values (float and double) are never not a problem. There is also some evidence that the library author is unsure how to do it correctly.
Best counter-argument is that the library is widely used and well maintained, the author has released many updates over the years. Floating point consistency concerns can be reasoned away when you make sure that the exact same program with the exact same runtime environment generates both strings (i.e. don't store it) and you make sure the unit-test is built with optimization disabled.
You are not unit-testing the code that needs to be tested!
Yes, you would only write this code if the class itself provides no way to compare objects. In other words, does not itself override Equals/GetHashCode and does not expose a comparator. So testing for equality in your unit test exercise a feature that the to-be-tested code does not actually support. Something that a unit test should never do, you can't write a bug report when the test fails.
Counter argument is to reason that you need to test for equality to test another feature of the class, like the constructor or property setters. A simple comment in the code is enough to document this.
Sorry, I cannot write comments yet so I will write in here.
By serializing your objects to JSON, you are basically changing all of your objects to another data type and so everything that applies to your JSON library will have an impact on your results.
So if there is a tag like [ScriptIgnore] in one of the objects, your code will simply ignore it since it has been omitted from your data.
Also, the string results can be the same for objects that are not the same. like this example.
static void Main(string[] args)
{
Xb x1 = new X1()
{
y1 = 1,
y2 = 2
};
Xb x2 = new X2()
{
y1 = 1,
y2= 2
};
bool result = new Comparator<Xb>().Equals(x1, x2);
}
}
class Xb
{
public int y1 { get; set; }
}
class X1 : Xb
{
public short y2 { get; set; }
}
class X2 : Xb
{
public long y2 { get; set; }
}
So as you see x1 has a different type from x2 and even the data type of the y2 is different for those two, but the json results will be the same.
Other than that, since both x1 and x2 are from type Xb, I could call your comparator without any problems.