DeC*_*Caf 22 .net c# types equality equals
请考虑以下代码:
class MyType : TypeDelegator
{
public MyType(Type parent)
: base(parent)
{
}
}
class Program
{
static void Main(string[] args)
{
Type t1 = typeof(string);
Type t2 = new MyType(typeof(string));
Console.WriteLine(EqualityComparer<Type>.Default.Equals(t1, t2)); // <-- false
Console.WriteLine(EqualityComparer<Type>.Default.Equals(t2, t1)); // <-- true
Console.WriteLine(t1.Equals(t2)); // <-- true
Console.WriteLine(t2.Equals(t1)); // <-- true
Console.WriteLine(Object.Equals(t1, t2)); // <-- false
Console.WriteLine(Object.Equals(t2, t1)); // <-- true
}
}
Run Code Online (Sandbox Code Playgroud)
为什么各种版本的Equals会返回不同的结果?EqualityComparer.Default可能调用Object.Equals,因此这些结果匹配,尽管它们本身不一致.Equals的正常实例版本都返回true
.
当一个方法返回一个Type
实际继承的方法时,这显然会产生问题TypeDelegator
.想象一下,例如将这些类型作为键放在字典中,默认情况下使用EqualityComparer.Default进行比较.
有什么方法可以解决这个问题吗?我想在上面的代码中的所有方法返回true
.
以下代码返回System.RuntimeType
Type t1 = typeof(string);
Run Code Online (Sandbox Code Playgroud)
如果你看一下Type的代码就有:
public override bool Equals(Object o)
{
if (o == null)
return false;
return Equals(o as Type);
}
Run Code Online (Sandbox Code Playgroud)
但是,System.RuntimeType具有:
public override bool Equals(object obj)
{
// ComObjects are identified by the instance of the Type object and not the TypeHandle.
return obj == (object)this;
}
Run Code Online (Sandbox Code Playgroud)
如果您查看程序集,它会执行:cmp rdx,rcx,所以只需要直接内存比较.
您可以使用以下方法重现它:
bool a = t1.Equals((object)t2); // False
bool b = t1.Equals(t2); // True
Run Code Online (Sandbox Code Playgroud)
所以看起来RuntimeType重写了Type Equals方法来进行直接比较......看起来没有简单的解决方法(不提供比较器).
编辑添加:出于好奇,我看了一下RuntimeType的.NET 1.0和1.1实现.它们没有在RuntimeType中重写Equals,因此问题是在.NET 2.0中引入的.
此答案中的代码已成为 GitHub 上的存储库:GitHub 上的 Undefault.NET
史蒂文很好地解释了为什么它会这样工作。我不相信有解决方案Object.Equals
。然而,
EqualityComparer<T>.Default
通过反射配置默认相等比较器来解决此问题的方法。这个小技巧在每个应用程序生命周期只需要发生一次。初创公司将是执行此操作的好时机。使其工作的代码行是:
DefaultComparisonConfigurator.ConfigureEqualityComparer<Type>(new HackedTypeEqualityComparer());
Run Code Online (Sandbox Code Playgroud)
执行该代码后,EqualityComparer<Type>.Default.Equals(t2, t1))
将产生与以下相同的结果EqualityComparer<Type>.Default.Equals(t1,t2))
。
支持的基础设施代码包括:
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(); }
}
Run Code Online (Sandbox Code Playgroud)
此类使用反射来配置 的基础字段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)); } }
}
}
Run Code Online (Sandbox Code Playgroud)
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); }
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
919 次 |
最近记录: |