È possibile evitare la riflessione sull'invocazione se si desidera confrontare in base ai tipi di proprietà staticamente conosciuti.
Questo fa affidamento su Expressions in 3.5 per eseguire il one off reflection in modo semplice, è possibile farlo meglio per ridurre lo sforzo per tipi estremamente annidati, ma questo dovrebbe andare bene per la maggior parte delle esigenze.
Se è necessario lavorare sui tipi di runtime, sarà necessario un certo livello di riflessione (anche se sarebbe conveniente se si memorizza di nuovo la cache per ogni accesso di proprietà e metodi di confronto) ma questo è intrinsecamente molto più complesso poiché i tipi di runtime su sub proprietà possono non corrispondere così, per la piena generalità si dovrebbe prendere in considerazione le regole come la seguente:
- considerano tipi non corrispondenti a non essere uguale
- semplice da capire e facile da implementare
- non è probabile che essere un'operazione utile
- Nel punto i tipi divergono utilizzare il
EqualityComparer<T>.Default
implementazione standard sui due FattorialeMenoUno ulteriore
- ancora semplice, po 'più difficile da implementare.
- considerare uguali se hanno un sottoinsieme di proprietà comuni che sono essi stessi uguali
- complicato, non proprio terribilmente significativo
- considerano uguali se condividono lo stesso sottoinsieme di proprietà (sulla base di nome e tipo) che sono uguali
- complicato, voce in Duck Typing
Ci sono una varietà di altre opzioni, ma questo dovrebbe essere spunto di riflessione sul perché l'analisi runtime completa è difficile.
(notare che io si 'foglia' guardia di terminazione sono cambiata per essere quello che io ritengo di essere superiore, se si desidera utilizzare solo tipo Sting/valore per qualche ragione si sentono liberi)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
class StaticPropertyTypeRecursiveEquality<T>
{
private static readonly Func<T,T, bool> actualEquals;
static StaticPropertyTypeRecursiveEquality()
{
if (typeof(IEquatable<T>).IsAssignableFrom(typeof(T)) ||
typeof(T).IsValueType ||
typeof(T).Equals(typeof(object)))
{
actualEquals =
(t1,t2) => EqualityComparer<T>.Default.Equals(t1, t2);
}
else
{
List<Func<T,T,bool>> recursionList = new List<Func<T,T,bool>>();
var getterGeneric =
typeof(StaticPropertyTypeRecursiveEquality<T>)
.GetMethod("MakePropertyGetter",
BindingFlags.NonPublic | BindingFlags.Static);
IEnumerable<PropertyInfo> properties = typeof(T)
.GetProperties()
.Where(p => p.CanRead);
foreach (var property in properties)
{
var specific = getterGeneric
.MakeGenericMethod(property.PropertyType);
var parameter = Expression.Parameter(typeof(T), "t");
var getterExpression = Expression.Lambda(
Expression.MakeMemberAccess(parameter, property),
parameter);
recursionList.Add((Func<T,T,bool>)specific.Invoke(
null,
new object[] { getterExpression }));
}
actualEquals = (t1,t2) =>
{
foreach (var p in recursionList)
{
if (t1 == null && t2 == null)
return true;
if (t1 == null || t2 == null)
return false;
if (!p(t1,t2))
return false;
}
return true;
};
}
}
private static Func<T,T,bool> MakePropertyGetter<TProperty>(
Expression<Func<T,TProperty>> getValueExpression)
{
var getValue = getValueExpression.Compile();
return (t1,t2) =>
{
return StaticPropertyTypeRecursiveEquality<TProperty>
.Equals(getValue(t1), getValue(t2));
};
}
public static bool Equals(T t1, T t2)
{
return actualEquals(t1,t2);
}
}
per i test Ho usato quanto segue:
public class Foo
{
public int A { get; set; }
public int B { get; set; }
}
public class Loop
{
public int A { get; set; }
public Loop B { get; set; }
}
public class Test
{
static void Main(string[] args)
{
Console.WriteLine(StaticPropertyTypeRecursiveEquality<String>.Equals(
"foo", "bar"));
Console.WriteLine(StaticPropertyTypeRecursiveEquality<Foo>.Equals(
new Foo() { A = 1, B = 2 },
new Foo() { A = 1, B = 2 }));
Console.WriteLine(StaticPropertyTypeRecursiveEquality<Loop>.Equals(
new Loop() { A = 1, B = new Loop() { A = 3 } },
new Loop() { A = 1, B = new Loop() { A = 3 } }));
Console.ReadLine();
}
}
Non dovrebbe essere (t1 è ValueType | t1 è stringa)? Con || se la prima condizione fallisce, la seconda non viene testata. System.String è un tipo di riferimento, non un tipo di valore. –