Вы можете избежать отражения при вызове, если вы счастливы сравнить на основе статических свойств типов свойств.
Это зависит от выражений в 3.5, чтобы сделать одно отражение простым способом, это можно сделать лучше, чтобы уменьшить усилия для крайне вложенных типов, но это должно быть хорошо для большинства потребностей.
Если вы должны работать с типами времени выполнения, потребуется некоторый уровень отражения (хотя это было бы дешево, если бы вы снова кэшировали каждый доступ к свойствам и методам сравнения), но это по своей сути намного сложнее, поскольку типы времени выполнения на sub свойства могут не совпадать с тем, для полной общности вы должны учитывать правила, как следующее:
- рассмотреть несогласованные типы нЕ быть равны
- легко понять и легко осуществить
- скорее всего, не быть полезная операция
- В точке типов расходятся использовать стандартную
EqualityComparer<T>.Default
реализацию на два и рекурсию не далее
- снова просто, несколько сложнее реализовать.
- считают равными, если они имеют общий набор свойств, которые сами по себе являются равными
- сложно, на самом деле не очень значимым
- считают равными, если они имеют то же подмножество свойств (на основе название и тип), которые сами по себе равны
- сложный, направляясь в утиную печать
Существует множество других вариантов, но это должно быть пищей для размышлений о том, почему полный анализ времени выполнения сложный.
(обратите внимание, что я изменил тебе «лист» прекращение охраны, то, что я считаю, чтобы быть выше, если вы хотите просто использовать тип жала/значение по какой-то причине не стесняйтесь)
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);
}
}
для тестирования Я использовал следующее:
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();
}
}
Не должно быть (t1 является ValueType | t1 является строкой)? С || если первое условие не выполняется, второе не тестируется. System.String - это ссылочный тип, а не тип значения. –