Обновление
Код из этого ответа стал хранилищем на GitHub: Undefault.NET на GitHub
Стивен дает хорошее объяснение того, почему это работает так, как работает. Я не верю, что есть решение для дела Object.Equals
. Однако
Я нашел способ исправить проблему в случае EqualityComparer<T>.Default
, настроив компаратор сравнения по умолчанию с отражением.
Этот небольшой взлом должен произойти только один раз за жизненный цикл приложения. Запуск будет хорошим временем для этого. Строка кода, которая заставит его работать:
DefaultComparisonConfigurator.ConfigureEqualityComparer<Type>(new HackedTypeEqualityComparer());
После выполнения этого кода EqualityComparer<Type>.Default.Equals(t2, t1))
даст тот же результат, что и EqualityComparer<Type>.Default.Equals(t1,t2))
(в вашем примере).
Код вспомогательной инфраструктуры включает в себя:
1. пользовательская IEqualityComparer<Type>
реализация
Этот класс обрабатывает сравнение на равенство так, как вы этого хотите.
public class HackedTypeEqualityComparer : EqualityComparer<Type> {
public override bool Equals(Type one, Type other){
return ReferenceEquals(one,null)
? ReferenceEquals(other,null)
: !ReferenceEquals(other,null)
&& ( (one is TypeDelegator || !(other is TypeDelegator))
? one.Equals(other)
: other.Equals(one));
}
public override int GetHashCode(Type type){ return type.GetHashCode(); }
}
2. Конфигуратор класс
Этот класс использует отражение для настройки базового поля для EqualityComparer<T>.Default
. В качестве бонуса этот класс также предоставляет механизм для манипуляции со значением Comparer<T>.Default
и обеспечивает совместимость результатов сконфигурированных реализаций. Существует также метод возврата конфигураций обратно к значениям по умолчанию.
public class DefaultComparisonConfigurator
{
static DefaultComparisonConfigurator(){
Gate = new object();
ConfiguredEqualityComparerTypes = new HashSet<Type>();
}
private static readonly object Gate;
private static readonly ISet<Type> ConfiguredEqualityComparerTypes;
public static void ConfigureEqualityComparer<T>(IEqualityComparer<T> equalityComparer){
if(equalityComparer == null) throw new ArgumentNullException("equalityComparer");
if(EqualityComparer<T>.Default == equalityComparer) return;
lock(Gate){
ConfiguredEqualityComparerTypes.Add(typeof(T));
FieldFor<T>.EqualityComparer.SetValue(null,equalityComparer);
FieldFor<T>.Comparer.SetValue(null,new EqualityComparerCompatibleComparerDecorator<T>(Comparer<T>.Default,equalityComparer));
}
}
public static void ConfigureComparer<T>(IComparer<T> comparer){
if(comparer == null) throw new ArgumentNullException("comparer");
if(Comparer<T>.Default == comparer) return;
lock(Gate){
if(ConfiguredEqualityComparerTypes.Contains(typeof(T)))
FieldFor<T>.Comparer.SetValue(null,new EqualityComparerCompatibleComparerDecorator<T>(comparer,EqualityComparer<T>.Default));
else
FieldFor<T>.Comparer.SetValue(null,comparer);
}
}
public static void RevertConfigurationFor<T>(){
lock(Gate){
FieldFor<T>.EqualityComparer.SetValue(null,null);
FieldFor<T>.Comparer.SetValue(null,null);
ConfiguredEqualityComparerTypes.Remove(typeof(T));
}
}
private static class FieldFor<T> {
private const string FieldName = "defaultComparer";
private const BindingFlags FieldBindingFlags = BindingFlags.NonPublic|BindingFlags.Static;
static FieldInfo comparer, equalityComparer;
public static FieldInfo Comparer { get { return comparer ?? (comparer = typeof(Comparer<T>).GetField(FieldName,FieldBindingFlags)); } }
public static FieldInfo EqualityComparer { get { return equalityComparer ?? (equalityComparer = typeof(EqualityComparer<T>).GetField(FieldName,FieldBindingFlags)); } }
}
}
3. совместимая IComparer<T>
реализация
В основном это декоратор для IComparer<T>
, который обеспечивает совместимость между Comparer<T>
и EqualityComparer<T>
при вводе EqualityComparer<T>
. Он гарантирует, что любые два значения, которые настроенная реализация IEqualityComparer<T>
считает равными, всегда будут иметь результат сравнения 0
.
public class EqualityComparerCompatibleComparerDecorator<T> : Comparer<T> {
public EqualityComparerCompatibleComparerDecorator(IComparer<T> comparer, IEqualityComparer<T> equalityComparer){
if(comparer == null) throw new ArgumentNullException("comparer");
if(equalityComparer == null) throw new ArgumentNullException("equalityComparer");
this.comparer = comparer;
this.equalityComparer = equalityComparer;
}
private readonly IComparer<T> comparer;
private readonly IEqualityComparer<T> equalityComparer;
public override int Compare(T left, T right){ return this.equalityComparer.Equals(left,right) ? 0 : comparer.Compare(left,right); }
}