Some Opinions on this code block ?



Hi There

I am doing some unit testing at the moment, and the majority of the leg work
involves taking two objects (expected vs actual) and verifying that their
properties are equal. The objects that I am testing and their properties are
diverse, including enums, basic system types, arrays and nested types.

The objects themselves were not written by me, and do not have any overrides
for equals, compare ... etc.

I have written a utility class that accepts two objects and returns true or
false to indicate if all the properties of the two objects are equal.

I moved over to C# 8 months ago, with no previous experience in OO or .NET,
so this function is my first attempt at combinding reflection with recursion.
I would greatly appreciate any feedback or ideas.

The code appears long, but it is actually a self contained module with
testing classes prebuilt. You can just start a new console project, copy the
code block in and then run it.

Thanks !!

using System;
using System.Collections;
using System.Reflection;
using System.Diagnostics;

namespace UnitTestingUtilities
{
static class Program
{
[STAThread]
static void Main ( )
{
TesterClass tc1 = new TesterClass ( );
TesterClass tc2 = new TesterClass ( );
bool testResult = ObjectComparer.AreTheseObjectEqual (tc1, tc2 );
Debug.WriteLine ( "Test returned should be TRUE, and it is actually: " +
testResult.ToString ( ) );

tc1.Array_ntc[2].Array_byteVar[0] = 0;
tc1.Array_ntc[2].Array_byteVar[1] = 0;
tc1.Array_ntc[2].Array_byteVar[2] = 0;

testResult = ObjectComparer.AreTheseObjectEqual (tc1, tc2 );
Debug.WriteLine ( "Test returned should be FALSE, and it is actually: " +
testResult.ToString ( ) );

tc1 = new TesterClass ( );
tc2 = new TesterClass ( );
tc1.Array_ntc[0].Array_enEx[0] = enumExample.asd;
tc1.Array_ntc[0].Array_enEx[1] = enumExample.asd;
tc1.Array_ntc[0].Array_enEx[2] = enumExample.asd;
testResult = ObjectComparer.AreTheseObjectEqual (tc1, tc2 );
Debug.WriteLine ( "Test returned should be FALSE, and it is actually: " +
testResult.ToString ( ) );

tc1 = new TesterClass ( );
tc2 = new TesterClass ( );
tc1.BoolVar = false;
tc2.BoolVar = true;
testResult = ObjectComparer.AreTheseObjectEqual (tc1, tc2 );
Debug.WriteLine ( "Test returned should be FALSE, and it is actually: " +
testResult.ToString ( ) );

tc1 = new TesterClass ( );
tc2 = new TesterClass ( );
tc1.StringVar = "asd";
tc2.StringVar = "yui";
testResult = ObjectComparer.AreTheseObjectEqual (tc1, tc2 );
Debug.WriteLine ( "Test returned should be FALSE, and it is actually: " +
testResult.ToString ( ) );
}
}
public static class ObjectComparer
{
/// <summary>
/// This function compares any two objects properties.
/// It will recursively search down through nested properties and compare
predefined system types such as
/// char, string, bool, DateTime, short, int, long, float, double ... etc
/// </summary>
/// <param name="thisType">The type of the two objects</param>
/// <param name="leftObject"></param>
/// <param name="rightObject"></param>
/// <returns>True to indicate that the objects are equal. False
otherwise</returns>
public static bool AreTheseObjectEqual (object leftObject, object
rightObject )
{
Type thisType = leftObject.GetType ( );
if (rightObject.GetType() != thisType)
throw new ArgumentException("The two objects must be of the same type");
else if (leftObject == null && rightObject == null)
return true; //Both are null, so they are equal
else if (leftObject == null || rightObject == null)
return false; //One is null, the other is not null, so they are not equal
else if (IsObjectOneOfPredefinedDotNetTypesOrEnum ( leftObject ))
{
//We have a basic system type or enum, which needs to be evaluate directly
if (false == CompareTwoObjectsUsingPreDefinedDotNetTypesOrEnum (
leftObject, rightObject ))
return false;
}
else
{
//We have multiple properties to deal with, so iterate through them
PropertyInfo[] fields = thisType.GetProperties ( );
foreach (PropertyInfo propInfo in fields)
{
object lhs = propInfo.GetValue ( leftObject, null );
object rhs = propInfo.GetValue ( rightObject, null );

if (IsObjectOneOfPredefinedDotNetTypesOrEnum ( lhs ))
{
//We have a basic system type, which needs to be evaluate directly
//Note ... even if we have an array of a basic system type - such as
strings - this condition does not evaluate true (which is what we want)
if (false == CompareTwoObjectsUsingPreDefinedDotNetTypesOrEnum ( lhs,
rhs ))
return false;
}
else if (propInfo.PropertyType.IsArray)
{
//Property is an array, need to iterate through the elements and
compare them individually

IEnumerator ieLHS = ((System.Array)lhs).GetEnumerator ( );
IEnumerator ieRHS = ((System.Array)rhs).GetEnumerator ( );

while (ieLHS.MoveNext ( ))
{
//Now we have the individual object in the LHS array and RHS array
if (ieRHS.MoveNext ( ) == false)
return false;
else if (ieLHS.Current == null && ieRHS.Current == null)
return true;
else if (ieLHS.Current == null || ieRHS.Current == null)
return false;
else
{
//Recursively call function,
if (false == AreTheseObjectEqual (ieLHS.Current, ieRHS.Current ))
return false;
}
}
if (ieRHS.MoveNext ( ) == true)
return false;
}
else if (propInfo.PropertyType.GetProperties ( ).Length > 0)
{
//Propery is a nested type, so recursively call function,
if (false == AreTheseObjectEqual (lhs, rhs ))
return false;
}
else
{
//Should not end up here
throw new Exception ( "Logical Flow Error" );
}
}
}
return true;
}
private static bool IsObjectOneOfPredefinedDotNetTypesOrEnum ( object lhs )
{
if (lhs.GetType ( ).IsEnum)
return true;
else if (lhs is string)
return true;
else if (lhs is char)
return true;
else if (lhs is bool)
return true;
else if (lhs is DateTime)
return true;
else if (lhs is sbyte)
return true;
else if (lhs is byte)
return true;
else if (lhs is short)
return true;
else if (lhs is ushort)
return true;
else if (lhs is int)
return true;
else if (lhs is uint)
return true;
else if (lhs is long)
return true;
else if (lhs is ulong)
return true;
else if (lhs is decimal)
return true;
else if (lhs is float)
return true;
else if (lhs is double)
return true;
else
return false;
}
private static bool CompareTwoObjectsUsingPreDefinedDotNetTypesOrEnum (
object lhs, object rhs )
{
if (lhs.GetType ( ).IsEnum)
{
if (lhs.ToString ( ) != rhs.ToString ( ))
return false;
}
else if (lhs is string)
{
if (false == rhs is string)
return false;
if ((string)lhs != (string)rhs)
return false;
}
else if (lhs is char)
{
if (false == rhs is char)
return false;
if ((char)lhs != (char)rhs)
return false;
}
else if (lhs is bool)
{
if (false == rhs is bool)
return false;
if ((bool)lhs != (bool)rhs)
return false;
}
else if (lhs is DateTime)
{
if (false == rhs is DateTime)
return false;
if ((DateTime)lhs != (DateTime)rhs)
return false;
}
else if (lhs is sbyte)
{
if (false == rhs is sbyte)
return false;
if ((sbyte)lhs != (sbyte)rhs)
return false;
}
else if (lhs is byte)
{
if (false == rhs is byte)
return false;
if ((byte)lhs != (byte)rhs)
return false;
}
else if (lhs is short)
{
if (false == rhs is short)
return false;
if ((short)lhs != (short)rhs)
return false;
}
else if (lhs is ushort)
{
if (false == rhs is ushort)
return false;
if ((ushort)lhs != (ushort)rhs)
return false;
}
else if (lhs is int)
{
if (false == rhs is int)
return false;
if ((int)lhs != (int)rhs)
return false;
}
else if (lhs is uint)
{
if (false == rhs is uint)
return false;
if ((uint)lhs != (uint)rhs)
return false;
}
else if (lhs is long)
{
if (false == rhs is long)
return false;
if ((long)lhs != (long)rhs)
return false;
}
else if (lhs is ulong)
{
if (false == rhs is ulong)
return false;
if ((ulong)lhs != (ulong)rhs)
return false;
}
else if (lhs is decimal)
{
if (false == rhs is decimal)
return false;
if ((decimal)lhs != (decimal)rhs)
return false;
}
else if (lhs is float)
{
if (false == rhs is float)
return false;
if ((float)lhs != (float)rhs)
return false;
}
else if (lhs is double)
{
if (false == rhs is double)
return false;
if ((double)lhs != (double)rhs)
return false;
}
else
{
//Cannot cast to one of the PreDefined system types, so throw exception
throw new ArgumentException ( "Missing this Type: " + lhs.GetType (
).ToString ( ) );
}
return true;
}
}
public enum enumExample { asd, ert, iop };
public class TesterClass
{
private NestedTesterClass ntc = new NestedTesterClass ( );

public NestedTesterClass Ntc
{
get { return ntc; }
set { ntc = value; }
}


private enumExample enEx = enumExample.asd;

internal enumExample EnEx
{
get { return enEx; }
set { enEx = value; }
}

private enumExample[] array_enEx = new enumExample[3] { enumExample.asd,
enumExample.ert, enumExample.iop };

public enumExample[] Array_enEx
{
get { return array_enEx; }
set { array_enEx = value; }
}


private NestedTesterClass[] array_ntc = new NestedTesterClass[3] { new
NestedTesterClass ( ), new NestedTesterClass ( ), new NestedTesterClass ( ) };

public NestedTesterClass[] Array_ntc
{
get { return array_ntc; }
set { array_ntc = value; }
}

//System Types
private string stringVar = "blah";

public string StringVar
{
get { return stringVar; }
set { stringVar = value; }
}
private char charVar = 'c';

public char CharVar
{
get { return charVar; }
set { charVar = value; }
}
private bool boolVar = true;

public bool BoolVar
{
get { return boolVar; }
set { boolVar = value; }
}
private DateTime dateTimeVar = DateTime.Parse ( "12 Nov 2005" );

public DateTime DateTimeVar
{
get { return dateTimeVar; }
set { dateTimeVar = value; }
}
private sbyte sbyteVar = -15;

public sbyte SbyteVar
{
get { return sbyteVar; }
set { sbyteVar = value; }
}
private byte byteVar = 105;

public byte ByteVar
{
get { return byteVar; }
set { byteVar = value; }
}
private short shortVar = -23;

public short ShortVar
{
get { return shortVar; }
set { shortVar = value; }
}
private ushort ushortVar = 123;

public ushort UshortVar
{
get { return ushortVar; }
set { ushortVar = value; }
}
private int intVar = 19201;

public int IntVar
{
get { return intVar; }
set { intVar = value; }
}
private uint uintVar = 20000;

public uint UintVar
{
get { return uintVar; }
set { uintVar = value; }
}
private long longVar = 304050;

public long LongVar
{
get { return longVar; }
set { longVar = value; }
}
private ulong ulongVar = 60123;

public ulong UlongVar
{
get { return ulongVar; }
set { ulongVar = value; }
}
private decimal decimalVar = 1123.456M;

public decimal DecimalVar
{
get { return decimalVar; }
set { decimalVar = value; }
}
private float floatVar = 123432.22F;

public float FloatVar
{
get { return floatVar; }
set { floatVar = value; }
}
private double doubleVar = 13.43645456;

public double DoubleVar
{
get { return doubleVar; }
set { doubleVar = value; }
}
private string[] array_stringVar = new string[3] { "aas", "qw", "qwee" };

public string[] Array_stringVar1
{
get { return array_stringVar; }
set { array_stringVar = value; }
}
private char[] array_charVar = new char[3] { 'a', 'd', 'e' };

public char[] Array_charVar
{
get { return array_charVar; }
set { array_charVar = value; }
}
private bool[] array_boolVar = new bool[3] { true, false, true };

public bool[] Array_boolVar
{
get { return array_boolVar; }
set { array_boolVar = value; }
}
private DateTime[] array_dateTimeVar = new DateTime[3] { DateTime.Parse (
"12 Nov 2005" ), DateTime.Parse ( "13 Nov 2005" ), DateTime.Parse ( "14 Nov
2005" ) };

public DateTime[] Array_dateTimeVar
{
get { return array_dateTimeVar; }
set { array_dateTimeVar = value; }
}
private sbyte[] array_sbyteVar = new sbyte[3] { 1, 2, 3 };

public sbyte[] Array_sbyteVar
{
get { return array_sbyteVar; }
set { array_sbyteVar = value; }
}
private byte[] array_byteVar = new byte[3] { 0, 1, 1 };

public byte[] Array_byteVar
{
get { return array_byteVar; }
set { array_byteVar = value; }
}
private short[] array_shortVar = new short[3] { 123, 543, 565 };

public short[] Array_shortVar
{
get { return array_shortVar; }
set { array_shortVar = value; }
}
private ushort[] array_ushortVar = new ushort[3] { 456, 678, 567 };

public ushort[] Array_ushortVar
{
get { return array_ushortVar; }
set { array_ushortVar = value; }
}
private int[] array_intVar = new int[3] { 345, 123, 687 };

public int[] Array_intVar
{
get { return array_intVar; }
set { array_intVar = value; }
}
private uint[] array_uintVar = new uint[3] { 675, 2, 123 };


public uint[] Array_uintVar
{
get { return array_uintVar; }
set { array_uintVar = value; }
}
private long[] array_longVar = new long[3] { 12235, 234, 354345 };

public long[] Array_longVar
{
get { return array_longVar; }
set { array_longVar = value; }
}
private ulong[] array_ulongVar = new ulong[3] { 1223123, 546456, 213123 };

public ulong[] Array_ulongVar
{
get { return array_ulongVar; }
set { array_ulongVar = value; }
}
private decimal[] array_decimalVar = new decimal[3] { 223.45M, 23.4M,
123.657M };

public decimal[] Array_decimalVar
{
get { return array_decimalVar; }
set { array_decimalVar = value; }
}
private float[] array_floatVar = new float[3] { 123.643F, 12.543F,
1233.435634F };


public float[] Array_floatVar
{
get { return array_floatVar; }
set { array_floatVar = value; }
}
private double[] array_doubleVar = new double[3] { 1223.123223, 23.64435,
213.67876 };

public double[] Array_doubleVar
{
get { return array_doubleVar; }
set { array_doubleVar = value; }
}
}
public class NestedTesterClass
{
private enumExample enEx = enumExample.asd;

internal enumExample EnEx
{
get { return enEx; }
set { enEx = value; }
}

private enumExample[] array_enEx = new enumExample[3] { enumExample.asd,
enumExample.ert, enumExample.iop };

public enumExample[] Array_enEx
{
get { return array_enEx; }
set { array_enEx = value; }
}

private string stringVar = "blah";

public string StringVar
{
get { return stringVar; }
set { stringVar = value; }
}
private char charVar = 'c';

public char CharVar
{
get { return charVar; }
set { charVar = value; }
}
private bool boolVar = true;

public bool BoolVar
{
get { return boolVar; }
set { boolVar = value; }
}
private DateTime dateTimeVar = DateTime.Parse ( "12 Nov 2005" );

public DateTime DateTimeVar
{
get { return dateTimeVar; }
set { dateTimeVar = value; }
}
private sbyte sbyteVar = -15;

public sbyte SbyteVar
{
get { return sbyteVar; }
set { sbyteVar = value; }
}
private byte byteVar = 105;

public byte ByteVar
{
get { return byteVar; }
set { byteVar = value; }
}
private short shortVar = -23;

public short ShortVar
{
get { return shortVar; }
set { shortVar = value; }
}
private ushort ushortVar = 123;

public ushort UshortVar
{
get { return ushortVar; }
set { ushortVar = value; }
}
private int intVar = 19201;

public int IntVar
{
get { return intVar; }
set { intVar = value; }
}
private uint uintVar = 20000;

public uint UintVar
{
get { return uintVar; }
set { uintVar = value; }
}
private long longVar = 304050;

public long LongVar
{
get { return longVar; }
set { longVar = value; }
}
private ulong ulongVar = 60123;

public ulong UlongVar
{
get { return ulongVar; }
set { ulongVar = value; }
}
private decimal decimalVar = 1123.456M;

public decimal DecimalVar
{
get { return decimalVar; }
set { decimalVar = value; }
}
private float floatVar = 123432.22F;

public float FloatVar
{
get { return floatVar; }
set { floatVar = value; }
}
private double doubleVar = 13.43645456;

public double DoubleVar
{
get { return doubleVar; }
set { doubleVar = value; }
}
private string[] array_stringVar = new string[3] { "aas", "qw", "qwee" };

public string[] Array_stringVar1
{
get { return array_stringVar; }
set { array_stringVar = value; }
}
private char[] array_charVar = new char[3] { 'a', 'd', 'e' };

public char[] Array_charVar
{
get { return array_charVar; }
set { array_charVar = value; }
}
private bool[] array_boolVar = new bool[3] { true, false, true };

public bool[] Array_boolVar
{
get { return array_boolVar; }
set { array_boolVar = value; }
}
private DateTime[] array_dateTimeVar = new DateTime[3] { DateTime.Parse (
"12 Nov 2005" ), DateTime.Parse ( "13 Nov 2005" ), DateTime.Parse ( "14 Nov
2005" ) };

public DateTime[] Array_dateTimeVar
{
get { return array_dateTimeVar; }
set { array_dateTimeVar = value; }
}
private sbyte[] array_sbyteVar = new sbyte[3] { 1, 2, 3 };

public sbyte[] Array_sbyteVar
{
get { return array_sbyteVar; }
set { array_sbyteVar = value; }
}
private byte[] array_byteVar = new byte[3] { 0, 1, 1 };

public byte[] Array_byteVar
{
get { return array_byteVar; }
set { array_byteVar = value; }
}
private short[] array_shortVar = new short[3] { 123, 543, 565 };

public short[] Array_shortVar
{
get { return array_shortVar; }
set { array_shortVar = value; }
}
private ushort[] array_ushortVar = new ushort[3] { 456, 678, 567 };

public ushort[] Array_ushortVar
{
get { return array_ushortVar; }
set { array_ushortVar = value; }
}
private int[] array_intVar = new int[3] { 345, 123, 687 };

public int[] Array_intVar
{
get { return array_intVar; }
set { array_intVar = value; }
}
private uint[] array_uintVar = new uint[3] { 675, 2, 123 };


public uint[] Array_uintVar
{
get { return array_uintVar; }
set { array_uintVar = value; }
}
private long[] array_longVar = new long[3] { 12235, 234, 354345 };

public long[] Array_longVar
{
get { return array_longVar; }
set { array_longVar = value; }
}
private ulong[] array_ulongVar = new ulong[3] { 1223123, 546456, 213123 };

public ulong[] Array_ulongVar
{
get { return array_ulongVar; }
set { array_ulongVar = value; }
}
private decimal[] array_decimalVar = new decimal[3] { 223.45M, 23.4M,
123.657M };

public decimal[] Array_decimalVar
{
get { return array_decimalVar; }
set { array_decimalVar = value; }
}
private float[] array_floatVar = new float[3] { 123.643F, 12.543F,
1233.435634F };


public float[] Array_floatVar
{
get { return array_floatVar; }
set { array_floatVar = value; }
}
private double[] array_doubleVar = new double[3] { 1223.123223, 23.64435,
213.67876 };

public double[] Array_doubleVar
{
get { return array_doubleVar; }
set { array_doubleVar = value; }
}
}
}
.



Relevant Pages

  • Re: Help! Need to sorted collection accessible by key
    ... Equals() and GetHashCode, then return the hashcode of the key you want ... access with, and compare by the one you want to order by, and put it ... If two items that compare equal using the _hasher key do not compare ... hash to the same bucket in the hash table but will not be equal to ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Interesting list Validity (True/False)
    ... Python to compare those strings equal. ... "a equals d" is true. ... There may or may not be any coercion involved.) ... And if you ask any mathematician, he'll say that is equal to. ...
    (comp.lang.python)
  • Re: Reading specific memory address into variable
    ... cons_pi and it equals 3.14159265358979. ... for how many times they want to do that calculation to figure out pi (4/1 - ... which is where I was stuck trying to figure out a way to compare the two ... incrementing the accuracy variable by 1 and incrementing the multiply ...
    (comp.lang.cpp)
  • Re: do I need to override the equals() method?
    ... Make sure they are consistent with each other, i.e., if two instances compare equal, they must have the same hash code. ... It occurs to me that something I'd like in an IDE is for it to let me mark the fields that consititute my unique key, and then it can generate equals() and hashCode(). ... lists the local variables with check boxes, and also gives an option to ...
    (comp.lang.java.programmer)
  • Re: Interesting list Validity (True/False)
    ... The exceptions you mean are not exceptions to "'X==Y' means 'X equals ... "Obbjects of different types never compare equal". ... The exceptions being different numeric types and different string types, that have a special treatment; see section 5.9 in the Reference Manual for details." ...
    (comp.lang.python)