更新
此答案中的代码已成为 GitHub 上的存储库:Undefault.NET on GitHub
Steven 很好地解释了为什么它会这样工作。我不相信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> 的装饰器,当EqualityComparer<T> 被注入时,它确保Comparer<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); }
}