Reputation: 21
My class looks something like this:
public class Test
{
public A[] ArrayA { get;set; }
}
public class A
{
public string P1 { get;set; }
public int P2 { get;set; }
}
Suppose I have two objects of Test class. I want to use reflection to compare the two, assuming that the array length of both ArrayA properties are the same in both objects. For example:
Test a = new Test();
Test b = new Test();
// fill them with some info.
var props = typeof(Test).GetProperties();
foreach (var prop in props)
{
var arrayA = prop.GetValue(a);
var arrayB = prop.GetValue(b);
for (int i = 0; i < arrayA.Lenght; i++)
{
var item1 = arrayA.GetValue(i);
var item2 = arrayB.GetValue(i);
// Do Comparison of item1 and item2
}
}
So here's where I run into a problem. item1 and item2 are both of type object. I can't seem to get the original type from it. I can get the Type from the array, how do I use that to get the properties I want in item1 and item2? I want to use reflection to grab the property information of item1 and item2 in order to make the comparison. Is this doable?
FYI. I'm not interested in any other solutions outside of using reflection. I already how to do those.
Upvotes: -1
Views: 90
Reputation: 488
Reflection is a very cool technology, but it's extremely slow at runtime so it's better not to use it. It's being slowly replaced by compiler code generation.
I used the sharplab.io to show what exactly happens when you change a single word in your class definition. If you use a record
instead of a class
the compiler will do all the heavy lifting for you.
public record A
{
public string P1 { get;set; }
public int P2 { get;set; }
}
The actual class that gets compiled looks like this:
[NullableContext(1)]
[Nullable(0)]
public class A : IEquatable<A>
{
[CompilerGenerated]
private string <P1>k__BackingField;
[CompilerGenerated]
private int <P2>k__BackingField;
[CompilerGenerated]
protected virtual Type EqualityContract
{
[CompilerGenerated]
get
{
return typeof(A);
}
}
public string P1
{
[CompilerGenerated]
get
{
return <P1>k__BackingField;
}
[CompilerGenerated]
set
{
<P1>k__BackingField = value;
}
}
public int P2
{
[CompilerGenerated]
get
{
return <P2>k__BackingField;
}
[CompilerGenerated]
set
{
<P2>k__BackingField = value;
}
}
[CompilerGenerated]
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("A");
stringBuilder.Append(" { ");
if (PrintMembers(stringBuilder))
{
stringBuilder.Append(' ');
}
stringBuilder.Append('}');
return stringBuilder.ToString();
}
[CompilerGenerated]
protected virtual bool PrintMembers(StringBuilder builder)
{
RuntimeHelpers.EnsureSufficientExecutionStack();
builder.Append("P1 = ");
builder.Append((object)P1);
builder.Append(", P2 = ");
builder.Append(P2.ToString());
return true;
}
[NullableContext(2)]
[CompilerGenerated]
public static bool operator !=(A left, A right)
{
return !(left == right);
}
[NullableContext(2)]
[CompilerGenerated]
public static bool operator ==(A left, A right)
{
if ((object)left != right)
{
if ((object)left != null)
{
return left.Equals(right);
}
return false;
}
return true;
}
[CompilerGenerated]
public override int GetHashCode()
{
return (EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(<P1>k__BackingField)) * -1521134295 + EqualityComparer<int>.Default.GetHashCode(<P2>k__BackingField);
}
[NullableContext(2)]
[CompilerGenerated]
public override bool Equals(object obj)
{
return Equals(obj as A);
}
[NullableContext(2)]
[CompilerGenerated]
public virtual bool Equals(A other)
{
if ((object)this != other)
{
if ((object)other != null && EqualityContract == other.EqualityContract && EqualityComparer<string>.Default.Equals(<P1>k__BackingField, other.<P1>k__BackingField))
{
return EqualityComparer<int>.Default.Equals(<P2>k__BackingField, other.<P2>k__BackingField);
}
return false;
}
return true;
}
[CompilerGenerated]
public virtual A <Clone>$()
{
return new A(this);
}
[CompilerGenerated]
protected A(A original)
{
<P1>k__BackingField = original.<P1>k__BackingField;
<P2>k__BackingField = original.<P2>k__BackingField;
}
public A()
{
}
}
As you see every method that needs to be overwritten to support value equality has been written by the compiler. No need to do it manually. Add as many properties as you like, the compiler will generate code for all of them.
You can just use ==
to compare two instances and they will be equal if all their properties are equal.
[EDIT] Stole an example from @Jamiec
using System;
using System.Linq;
public class Test: IEquatable<Test>
{
public A[] ArrayA { get;set; }
public override bool Equals(object test) => true;
public static bool operator ==(Test left, Test right)
=> left.Equals(right);
public static bool operator !=(Test left, Test right)
=> !left.Equals(right);
public bool Equals(Test test)
=> ArrayA.SequenceEqual(test.ArrayA);
public override int GetHashCode()
=> ArrayA != null ? HashCode.Combine(ArrayA.Select(a => a.GetHashCode()).ToArray()) : 0;
}
public record A
{
public string P1 { get;set; }
public int P2 { get;set; }
}
public class Program
{
public static void Main()
{
Test a = new(){ ArrayA = new A[]{ new(){ P1 = "Foo", P2=1 },new(){ P1 = "Bar", P2=1 }} };
Test b = new(){ ArrayA = new A[]{ new(){ P1 = "Foo", P2=1 },new(){ P1 = "Bar", P2=1 }} };
Test c = new(){ ArrayA = new A[]{ new(){ P1 = "Foo", P2=1 },new(){ P1 = "Bar", P2=2 }} };
Console.WriteLine(a == b); // True
Console.WriteLine(a == c); // False
}
}
Upvotes: 0
Reputation: 136154
While it is possible to do in reflection it will be slow, and you'll find yourself battling all kinds of edge cases when your object(s) start to get complicated. But for your simple example which has an array and an objects with simple string/int properties the following works how (I think) you expected.
public static bool AreEqual(object a, object b)
{
var props = a.GetType().GetProperties();
foreach (var prop in props)
{
if(prop.PropertyType.IsArray)
{
var arrayA = (Array)prop.GetValue(a);
var arrayB = (Array)prop.GetValue(b);
if(!AreArraysEqual(arrayA, arrayB))
{
return false;
}
}
else
{
if(!prop.GetValue(a).Equals(prop.GetValue(b)))
{
return false;
}
}
}
return true;
}
public static bool AreArraysEqual(Array a, Array b)
{
for(var i=0;i<a.Length;i++)
{
if(!AreEqual(a.GetValue(i),b.GetValue(i)))
{
return false;
}
}
return true;
}
This can then be tested as follows:
Test a = new Test(){ ArrayA = new[]{ new A{P1 = "Foo", P2=1},new A{P1 = "Bar", P2=2}} };
Test b = new Test(){ ArrayA = new[]{ new A{P1 = "Foo", P2=1},new A{P1 = "Bar", P2=2}} };
Test c = new Test(){ ArrayA = new[]{ new A{P1 = "Foo", P2=2},new A{P1 = "Bar", P2=2}} };
Console.WriteLine(AreEqual(a,b)); // true
Console.WriteLine(AreEqual(a,c)); // false
Live example: https://dotnetfiddle.net/FN8ZLu
Upvotes: 1