As part of the refactoring I was doing to the load code for crawler projects I needed a way of verifying that new code was loading data correctly. As it would be extremely time consuming to manually compare the objects, I used Reflection to compare the different objects and their properties. This article briefly describes the process and provides a complete helper function you can use in your own projects.
This code is loosely based on a Stack Overflow question, but I have heavily modified and expanded the original concept.
Obtaining a list of properties
The ability to analyze assemblies and their component pieces is directly built into the .NET Framework, and something I really appreciate - I remember the nightmares of trying to work with COM type libraries in Visual Basic many years ago!
Type class represents a type declaration in your project,
such as a class or enumeration. You can either use the
method of any object to get its underlying type, or use the
typeof keyword to access a type from its type name.
Once you have a type, you can call the
GetProperties method to
return a list of
PropertyInfo objects representing the
available properties of the type. Several methods, including
GetProperties, accept an argument of
flags allow you to define the type of information return, such
as public members or instance members.
In this case, I want all public instance members which can be read from and which are not included in a custom ignore list.
Retrieving the value of a property
PropertyInfo class has a
GetValue method that can be
used to read the value of a property. Its most basic usage is to
pass in the instance object (or null if you want to read a
static property) and any index parameters (or null if no index
parameters are supported).
The sample function described in this article doesn't currently support indexed properties.
Determining if a property can be directly compared
Some properties are simple types, such as an
int or a
and are very easy to compare. What happens if a property returns
some other object such as a collection of strings, or a complex
In this case, I try and see if the type supports
by calling the
IsAssignableFrom method. You need to call this
from the type you would like to create, passing in the source
I also check the
IsValueType properties of
the source type, although this is possibly redundant as all the
base types I've checked so far all support
Directly comparing values
Assuming that I can directly compare a value, first I check if one of the values is null - if one value is null and one false, I immediately return a mismatch.
IComparable is available, then I obtain an
instance of it from the first value and call its
method, passing in the second value.
IComparable is not supported, then I fallback to
If the values could not be directly compared, and do not
IEnumerable (as described in the next section) then
I assume the properties are objects and call the compare objects
function again on the properties.
This works nicely, but has one critical flaw - if you have a child object which has a property reference to a parent item, then the function will get stuck in a recursive loop. Currently the only workaround is to ensure that such parent properties are excluded via the ignore list functionality of the compare function.
If the direct compare check failed, but the property type
IEnumerable, then some Linq is used to obtain the
collection of items.
To save time, a count check is made and if the counts do not match (or one of the collections is null and the other is not), then an automatic mismatch is returned. If the counts do match, then all items are compared in the same manner as the parent objects.
I have tested this code on generic lists such as
and on strongly typed collections which inherit from
Collection<TValue> with success.
Below is the comparison code. Please note that it won't handle
all situations - as mentioned indexed properties aren't
supported. In addition, if you throw a complex object such as a
DataReader I suspect it will throw a fit on that. I also
haven't tested it on generic properties, it'll probably crash on
those too. But it has worked nicely for the original purpose I
wrote it for.
Also, as I was running this from a Console application, you may
wish to replace the calls to
Console.WriteLine with either
Debug.WriteLine or even return them as an out parameter.
I hope you find these helper methods useful, this article will be updated if and when the methods are expanded with new functionality.
- 2010-11-05 - First published
- 2020-11-21 - Updated formatting
Like what you're reading? Perhaps you like to buy us a coffee?