为什么在 C# 中对 属性 的密封覆盖不会从基类型覆盖访问器?

Why does sealed override of a property in C# copy not overriden accessor from base type?

在 C# 中覆盖 auto-属性 并仅提供一个访问器通过 PropertyInfo "lose" 反射另一个访问器,即使它是在基 class 中定义的。

乍一看可能很奇怪,但仔细分析后似乎

但是,将 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?这种行为的原因是什么?

What does compiler change in type to do sealed override in provided scenario? What is the reason of such behavior?

因为 "virtual" 和 "sealed"(或 CLR 说法中的 "final")等属性适用于方法而非属性,所以编译器密封 属性 是将其方法标记为密封的。但是,如果缺少 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; }
}

即base class 声明了三个虚拟属性,除了名称之外都相同。然后派生的 class 覆盖其中两个虚拟属性,密封其中一个。

如果您查看 return 通过反射为 Derived 中的属性编辑的描述符对象之间的差异,您会注意到一些事情:

  • 即使我们还没有为 P1 声明一个 getter,反射 return 无论如何,DeclaringType 属性 return输入 Derived 类型。
  • 但是对于P2,反射不是return而是getter(这与有关)。
  • 对于 P3,getter 再次被 return 编辑,但是对于这个 DeclaringType return 和 Base类型。
  • 对于P1getter,MethodBase.Attributes包含MethodAttributes.Final,说明该方法被封。这是编译器不能放在基类型上的属性(原因很明显),因此必须在派生类型中实现该方法,这样属性才有地方存在。

最后,如果我们查看生成的代码,我们会发现确实,编译器不仅为我们创建了这个方法,实际上它只是直接委托给基类 class 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