首页 > 解决方案 > Why are two different instances of the same attribute equal here?

问题描述

I have two classes that are decorated with the same attribute but different values. When I dump them in LINQPad I can see that they are different but when I do x.Equals(y) then it yields true even though the implementation of Equals actually compares property values.

This code reproduces this issue:

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 { }

And these are the results:

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

UserQuery+A2 

TypeId = typeof(A2) 
V      = T3 

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

What am I missing here and how can I properly compare them?

标签: c#attributesequality

解决方案


The A1 attribute class declares an auto-property with a private compiler-generated backing field.

Now, when the 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#?)

Thus, when trying to compare two instances of type A2 - which itself does not declare any fields - using the Attribute.Equals implementation, the result will be true (because the type of the two attribute instances is the same type A2, and the instances do not feature any fields that would be accessible through the A2 type).

Possible solutions (depending on the actual application scenario at hand) could be (among other things) using public fields instead of public properties in attribute classes, or perhaps overriding the Equals() method in your base attribute class (A1) which reflects and compares the type, all public fields and additionally all public properties of two attribute instances.


推荐阅读