为什么对C#复制中的属性进行密封重写而不是从基本类型重写访问器? [英] Why does sealed override of a property in C# copy not overriden accessor from base type?

查看:78
本文介绍了为什么对C#复制中的属性进行密封重写而不是从基本类型重写访问器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C#中,重写自动属性并仅提供一个访问器会通过 PropertyInfo 丢失"另一个访问器,即使它是在基类中定义的.

In C# overriding auto-property and providing only one accessor makes reflection via PropertyInfo "lose" the other one, even though it is defined in base class.

乍一看可能看起来很奇怪,但是经过更详细的分析,它似乎合理.

It can look strange at a first glance, but seems to be reasonable after more detailed analysis.

但是,将 override 更改为 sealed Override 也会更改此行为,并允许获取所有访问者:

However, changing override to sealed override also changes this behavior and allows to get all accessors:

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?

推荐答案

在提供的方案中,编译器的类型会发生什么变化以进行密封覆盖?这种行为的原因是什么?

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中的一个或另一个丢失了怎么办?编译器是否应将基本类型的方法标记为已密封?

Because attributes such as "virtual" and "sealed" (or "final" in CLR parlance) apply to methods and not properties, the only way for the compiler to seal a property is to mark its methods as sealed. But, what if one or the other of the setter and getter is missing? Should the compiler mark the base type's method as sealed?

不,我认为显然不是.:)

No, I think obviously not. :)

因此,为了使编译器有一种方法将其标记为密封,即使您没有声明一个方法,它也必须创建一个方法.

So, in order for there to be a method for the compiler to mark as sealed, it has to make one, even though you didn't declare one.

恕我直言,看看信息反射所提供的信息以及代码实际编译的内容是有启发性的.这是一个基于您的方案的简单代码示例:

IMHO, it's instructive to look at both the information reflection gives you, as well as what the code actually compiles to. Here's a simple code example based on your scenario:

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

即基类声明三个虚拟属性,除了名称外,其他所有属性均相同.然后,派生类将覆盖其中的两个虚拟属性,将其中一个密封.

I.e. the base class declares three virtual properties, all identical except for the name. Then the derived class overrides two of those virtual properties, sealing one of them.

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

If you take a look at the differences between the descriptor objects returned by reflection for the properties in Derived, you'll notice some things:

  • 即使我们尚未为 P1 声明吸气剂,反射仍会返回一个,而 DeclaringType 属性将返回 Derived 类型.
  • 但是对于 P2 ,反射不会返回吸气剂(这与早先的问题).
  • 对于 P3 ,将再次返回一个吸气剂,但对于此代码, DeclaringType 将返回 Base 类型.
  • 对于 P1 吸气剂, MethodBase.Attributes 包括 MethodAttributes.Final ,表明该方法是密封的.这是编译器不能放在基本类型上的属性(出于明显的原因),因此必须在派生类型中实现该方法,以便该属性具有一定的生存空间.
  • Even though we haven't declared a getter for P1, reflection returns one anyway, with the DeclaringType property returning the Derived type.
  • But for P2, reflection does not return a getter (this relates to your earlier question).
  • For P3, a getter is returned again, but for this one, the DeclaringType returns the Base type.
  • For the P1 getter, the MethodBase.Attributes includes MethodAttributes.Final, indicating that the method is sealed. This is the attribute that the compiler can't put on the base type (for obvious reasons), and so had to implement the method in the derived type, so that the attribute would have some place to live.

最后,如果我们查看生成的代码,就会发现确实,编译器不仅为我们创建了此方法,而且实际上只是直接委托给基类getter:

Finally, if we look at the generated code, we find that indeed, not only has the compiler created this method for us, it does in fact just delegate directly to the base 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

这篇关于为什么对C#复制中的属性进行密封重写而不是从基本类型重写访问器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆