为什么同一属性的两个不同实例在这里相等?

Why are two different instances of the same attribute equal here?

我有两个 类 装饰有相同的属性但不同的值。当我将它们转储到 LINQPad 中时,我可以看到它们是不同的,但是当我执行 x.Equals(y) 时,它会产生 true,即使 Equals 的实现实际上比较了 属性 值。

这段代码重现了这个问题:

void Main()
{
    var a1 = typeof(T1).GetCustomAttribute<A2>().Dump();
    var a2 = typeof(T3).GetCustomAttribute<A2>().Dump();
    a1.Equals(a2).Dump();
}


[A1(V = "I1")]
interface I1
{
    [A1(V = "I1.P1")]
    string P1 { get; set; }
}

[A2(V = "T1")] // <-- typeof(T1).GetCustomAttribute<A2>()
class T1 : I1
{
    [A1(V = "T1.P1")]
    public virtual string P1 { get; set; }
}

class T2 : T1 { }

[A1(V = "T3"), A2(V = "T3")] // <-- typeof(T3).GetCustomAttribute<A2>()
class T3 : T2
{
    [A1(V = "T3.P1")]
    public override string P1 { get; set; }
}

class A1 : Attribute { public string V { get; set; } }
class A2 : A1 { }

结果如下:

UserQuery+A2 
TypeId = typeof(A2) 
V      = T1 

UserQuery+A2 

TypeId = typeof(A2) 
V      = T3 

True // <-- a1.Equals(a2).Dump();

我在这里遗漏了什么以及如何正确比较它们?

A1 属性 class 声明了一个带有私有 compiler-generated 支持字段的 auto-property。

现在,当 Attribute.Equals method reflects over A2 to access all its instance fields (Attribute.Equals does not reflect over properties), it will not "see" the private backing field declared in A1, since private members of a base type are not accessible through a derived type. (See also here: Are private members inherited in C#?)

因此,当尝试使用 Attribute.Equals 实现比较类型 A2 的两个实例时(它本身不声明任何字段),结果将是 true(因为两个属性实例的类型是相同的类型 A2,并且这些实例不具有可通过 A2 类型访问的任何字段)。

可能的解决方案(取决于手头的实际应用场景)可能是(除其他事项外)使用 public 字段而不是属性 classes 中的 public 属性,或者也许覆盖反映和比较类型的基本属性 class (A1) 中的 Equals() 方法,此外,所有 public 字段 两个属性实例的所有 public 个属性。