为什么可以';t覆盖嵌套类中的私有成员

本文关键字:成员 嵌套 覆盖 为什么 | 更新日期: 2023-09-27 18:21:38

在C#中,嵌套类可以访问包含类的私有成员。

为什么我不能凌驾于这些成员之上?为什么编译器会出错?

    private abstract class InheritanceTest
    {
        public virtual object Property
        {
            get { return null; }
            private set { }
        }
        public class Child : InheritanceTest
        {
            public override object Property
            {
                get { return null; }
                private set { base.Property = null; } // the base.Property = null statement here is just to show that there isn't an error message for accesing a parent private member.
            }
        }
    }

我得到的唯一错误信息是:

"Program.InitheritanceTest.Child.Property.set":无法重写继承的成员"Program.Innheritancetest.Property.set",因为它未标记为虚拟、抽象或重写

编译器显然出了问题,因为整个属性都标记为虚拟。get方法可以重写继承的成员。

这是C#规范的一部分,并且只有错误消息是错误的吗?还是应该允许这样做?

我缺少规范的哪一部分?(或者缺少编译器?)

为什么可以';t覆盖嵌套类中的私有成员

不能覆盖私有虚拟方法和属性

不能将虚拟修饰符与静态、抽象、,private或override修饰符。虚拟(C#参考)

另请参阅是否可以覆盖私有虚拟方法?。

没有virtual private这样的东西。因此,由外部类定义的set访问器不是virtual,因此您不能覆盖它

对于方法,规范明确禁止private virtual(C#规范的§10.6):

如果声明包含private修饰符,则声明不包含以下任何修饰符:virtualoverrideabstract

关于财产(§10.7):

关于修饰符的有效组合,属性声明受与方法声明(§10.6)相同的规则约束。

规范中唯一似乎与virtual属性相关的部分,其中一个访问器是private,是(§10.7.5):

virtual属性声明指定该属性的访问者是虚拟的。virtual修饰符适用于读写属性的两个访问器——读写属性不可能只有一个访问器是虚拟的。

这似乎与实际情况相矛盾:只有非private访问器是虚拟的。除非我遗漏了什么,否则我认为这要么是文档中的错误,要么是编译器中的错误。我已经为此创建了一个Connect错误,让我们看看微软有什么要说的。

有关private virtual方法的更多信息,请参阅Eric Lippert的回答。

根据定义,private成员是不可重写的。如果希望setter是可重写的,可以将其标记为protected而不是private

   private abstract class InheritanceTest
    {
        public virtual object Property
        {
            get { return null; }
            protected set { }
        }
        public class Child : InheritanceTest
        {
            public override object Property
            {
                get { return null; }
                protected set { base.Property = null; }
            }
        }
    }

更具体地回答您关于为什么的问题:

要明白,当您的C#代码被编译为IL代码时,1个属性实际上有3个内容。

  1. Property属性本身
  2. 一个名为get_Property()的方法,它是getter
  3. 方法名为set_Property(),它是setter

在您的代码中,您告诉.NET"我想要一个virtual属性。然后它将该访问级别级联到getter和setter方法。实际上,在IL代码中,属性根本没有指定virtual

对于C#代码:

public virtual object Property { get; set; }

生成的IL代码为:

.property instance object Property() { ... }
.method public hidebysig newslot specialname virtual 
    instance object  get_Property() cil managed
    { ... }
.method public hidebysig newslot specialname virtual 
    instance object  set_Property() cil managed
    { ... }

请注意,publicvirtual关键字同时应用于getter和setter方法,但不应用于属性本身。

现在,通过将您的C#代码更改为:

public virtual object Property { get; private set; }

您已经告诉.NET,您希望getter和setter方法是虚拟的但是,则它会运行到private set,并且访问级别会覆盖setter方法的publicvirtual访问级别。因此,生成的IL代码变为:

 .property instance object Property() { ... }
.method public hidebysig newslot specialname virtual 
    instance object  get_Property() cil managed
    { ... }
.method private hidebysig newslot specialname
    instance object  set_Property() cil managed
    { ... }

注意,现在set_Property()private,而不再是virtual。实际上,在.NET中不可能有private virtual,因为它没有意义。。。这就像试图说"没有其他类可以看到这个……但派生类可以覆盖这个东西,他们看不到或无法访问",这是没有意义的。派生类不能覆盖它们甚至看不到的内容。

在这种情况下,protected关键字是合适的替换,因为它告诉.NET"只有我自己和派生类才能看到或访问它,派生类可以覆盖此属性。"

所以我想"简短"的答案应该是"因为在.NET中不可能是privatevirtual,所以编译器采用你给它的更受限制的访问级别

此外,IMO的错误信息是非常正确的。

"Program.InitheritanceTest.Child.Property.set":无法重写继承的成员"Program.Innheritancetest.Property.set",因为它未标记为虚拟、抽象或重写

请注意,它说的是'Program.InheritanceTest.Property.set',所以末尾的".set"指的是最终的set_Property()方法,而不是Property属性。并且set_Property()方法仅标记为private,因为.NET编译器看到了这一点,并出于上述原因从该方法中删除了virtual。我想,编译器发出警告或说"对于‘set’,virtual将被忽略"是有意义的。

希望这更有意义。。。