c# - 为什么 C# 副本中属性的密封覆盖不会从基类型覆盖访问器?
问题描述
在 C# 中,覆盖自动属性并仅提供一个访问器会通过PropertyInfo
“丢失”另一个访问器来进行反射,即使它是在基类中定义的。
乍一看可能很奇怪,但经过更详细的分析似乎是合理的。
但是,更改override
为sealed override
也会更改此行为并允许获取所有访问器:
using System.Reflection;
using NUnit.Framework;
[TestFixture]
public class PropertySealedOverrideReflectionTests
{
public class Base
{
public virtual object Override { get; set; }
public virtual object SealedOverride { get; set; }
}
public class Derived : Base
{
public override object Override { set => base.Override = value; }
public sealed override object SealedOverride { set => base.Override = value; }
}
[Test]
public void Override()
{
PropertyInfo overrideProperty = typeof(Derived).GetProperty(nameof(Derived.Override));
// ---
// getter from base class is "invisible" here
// ---
Assert.False(overrideProperty.CanRead);
Assert.Null(overrideProperty.GetMethod);
}
[Test]
public void SealedOverride()
{
PropertyInfo sealedOverrideProperty = typeof(Derived).GetProperty(nameof(Derived.SealedOverride));
// ---
// after changing to "sealed override" getter is in place
// ---
Assert.True(sealedOverrideProperty.CanRead);
Assert.NotNull(sealedOverrideProperty.GetMethod);
}
}
sealed override
在提供的场景中,编译器的类型会发生什么变化?这种行为的原因是什么?
解决方案
在提供的场景中,编译器的类型会发生什么变化以进行密封覆盖?这种行为的原因是什么?
因为诸如“虚拟”和“密封”(或 CLR 用语中的“最终”)之类的属性适用于方法而不是属性,所以编译器密封属性的唯一方法是将其方法标记为密封。但是,如果缺少一个或另一个 setter 和 getter 怎么办?编译器是否应该将基类型的方法标记为密封?
不,我认为显然不是。:)
所以,为了让编译器有一种方法可以标记为密封,它必须创建一个,即使你没有声明一个。
恕我直言,看看反射给你的信息以及代码实际编译成的内容都是有启发性的。这是一个基于您的场景的简单代码示例:
class Base
{
public virtual object P1 { get; set; }
public virtual object P2 { get; set; }
public virtual object P3 { get; set; }
}
class Derived : Base
{
public sealed override object P1 { set => base.P1 = value; }
public override object P2 { set => base.P2 = value; }
}
即基类声明了三个虚拟属性,除了名称之外都相同。然后派生类覆盖其中两个虚拟属性,密封其中一个。
如果你看一下反射返回的描述符对象之间的差异Derived
,你会注意到一些事情:
- 即使我们没有为 声明一个 getter
P1
,反射仍然返回一个,DeclaringType
属性返回Derived
类型。 - 但是对于
P2
,反射不会返回一个吸气剂(这与您之前的问题有关)。 - 对于
P3
,再次返回一个 getter,但对于这个,DeclaringType
返回Base
类型。 - 对于
P1
getter,MethodBase.Attributes
includesMethodAttributes.Final
表示该方法是密封的。这是编译器不能放在基类型上的属性(出于显而易见的原因),因此必须在派生类型中实现该方法,以便该属性有一些存在的地方。
最后,如果我们查看生成的代码,我们会发现确实,编译器不仅为我们创建了这个方法,它实际上只是直接委托给了基类 getter:
.method public hidebysig specialname virtual final
instance object get_P1() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance object TestSO57762322VirtualProperty.Base::get_P1()
IL_0006: ret
} // end of method Derived::get_P1
推荐阅读
- python - 如何使用 numpy 解决 mayavi 中的 double_scalars 中遇到的无效值
- ios - Swift 4 - Alamofire 4 上传带参数的图片
- javascript - 使用 var vs let 记录全局变量会产生不同的结果
- linker-errors - ns-3 waf 链接错误(未定义的引用)
- selenium - 如何使用 Selenium Webdriver 验证复选框是否被选中?
- safe-browsing - 谷歌安全浏览搜索返回威胁,但 API 没有
- javascript - FormData 仅发布一个图像文件
- javascript - 如何使用 Flask 为 POST 请求添加确认对话框?
- authentication - 通过 Power BI 使用凭据连接到 Web 数据源
- mongodb - 哪个索引对以下查询有用?