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

C# 为什么同一属性的两个不同实例在这里相等?,c#,attributes,equality,C#,Attributes,Equality,我有两个类,它们用相同的属性修饰,但值不同。当我在LINQPad中转储它们时,我可以看到它们是不同的,但是当我执行x.Equals(y)时,它会生成true,即使Equals的实现实际上比较了属性值 此代码复制了此问题: void Main() { var a1 = typeof(T1).GetCustomAttribute<A2>().Dump(); var a2 = typeof(T3).GetCustomAttribute<A2>().Dump();

我有两个类,它们用相同的属性修饰,但值不同。当我在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 { }
void Main()
{
var a1=typeof(T1).GetCustomAttribute().Dump();
var a2=typeof(T3).GetCustomAttribute().Dump();
a1.等于(a2).Dump();
}
[A1(V=“I1”)]
接口I1
{
[A1(V=“I1.P1”)]
字符串P1{get;set;}
}

[A2(V=“T1”)]/属性类
A1
使用专用编译器生成的支持字段声明自动属性

现在,当方法反射到
A2
上以访问其所有实例字段(Attribute.Equals不反射到属性上)时,它将不会“看到”在
A1
中声明的私有支持字段,因为基类型的私有成员不能通过派生类型访问。(另见此处:)

因此,当尝试使用Attribute.Equals实现比较类型
A2
的两个实例时,结果将是
true
(因为两个属性实例的类型是相同的
A2
,并且实例没有任何可以通过
A2
类型访问的字段)


可能的解决方案(取决于手头的实际应用程序场景)可能是(除其他外)在属性类中使用公共字段而不是公共属性,或者可能覆盖基本属性类中的
Equals()
方法(
A1
)它反映并比较了类型、所有公共字段以及两个属性实例的所有公共属性。

我不知道LinqPad的Dump方法的详细信息,但请注意,您没有将属性对象分配给变量a1/a2,而是分配了Dump方法返回的值/对象。为什么要这样做因此,获取您所获得的内容在很大程度上取决于该转储()的行为/实现方法…@elgonzo我不知道LinqPad转储方法的细节-我可以看出这是因为你的理论完全错误。
Dump
是透明的,并且返回它收到的相同的
t
。嗯,好的。我刚刚启动了一个VS测试项目(.NET framework 4.6.1)并进行了深入研究。
属性
类实现的Equal方法似乎无法识别继承的字段。(您可以通过将A1中的属性属性设置为虚拟,然后在A2中对其进行简单重写来快速验证。这将使Equal返回为false).现在,我不知道为什么会这样,也不知道相同的行为是否也会在.NET Core中被观察到,因此没有答案。(不幸的是,文档中没有提到这个特定的行为…)但是
a1==a2
但是
a1.Equals(a2)
返回true。这表明属性的默认值Equals在这里不正确。@WiktorZychla我想它使用了默认的
=
对象的运算符,因为
属性似乎没有覆盖它。至少我在源代码中找不到它。我想我会重新设计我的属性,这样我就可以e一个伪基1,我将所有其他属性放在其他属性中,并添加一个用于处理公共属性的接口。重写
Equals
可能会更加棘手。@t3chb0t在比较两个
MemberInfo
实例时要稍微打破封装的情况下,更好的比较方法是考虑比较中的
反射类型
UserQuery+A2 
TypeId = typeof(A2) 
V      = T1 

UserQuery+A2 

TypeId = typeof(A2) 
V      = T3 

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