为什么我不能覆盖嵌套类中的私有成员?

时间:2012-01-29 00:55:06

标签: c# inheritance

在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.InheritanceTest.Child.Property.set':无法覆盖继承的成员'Program.InheritanceTest.Property.set',因为它未标记为虚拟,抽象或覆盖

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

这是C#规范的一部分,只有错误信息是错误的吗?或者应该允许这样做?

我错过了哪个规格部分? (或者编译器丢失了吗?)

3 个答案:

答案 0 :(得分:3)

私有虚拟方法和属性无法被覆盖:

  

你不能将virtual modifier与static,abstract,   private或override修饰符。   virtual (C# Reference)

另见Can you override private virtual methods?

答案 1 :(得分:3)

没有virtual private之类的东西。因此,外部类定义的set访问器不是virtual,因此您无法覆盖它。

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

  

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

至于属性(§10.7):

  

对于有效的修饰符组合,属性声明与方法声明(第10.6节)的规则相同。

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

  

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

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

有关private virtual方法的更多信息,请参阅this answer from Eric Lippert

答案 2 :(得分:1)

根据定义,private成员不可覆盖。 如果您希望设置器可以覆盖,则可以将其标记为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个属性的内容。

  1. Property属性本身。
  2. 一个名为get_Property()的方法,它是getter。
  3. 方法名称set_Property()是设置者。
  4. 在你的代码中,你告诉.NET“我想要一个virtual属性。然后它将访问级别级联到getter和setter方法。实际上,在IL代码中,属性没有指定{{ 1}}。

    对于C#代码:

    virtual

    生成的IL代码是:

    public virtual object Property { get; set; }
    

    请注意,.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 { ... } public关键字既适用于getter方法,也适用于setter方法,但不适用于属性本身。

    现在,将C#代码更改为:

    virtual

    你告诉.NET你希望你的getter和setter方法是虚拟的...... 然而,然后它会运行到public virtual object Property { get; private set; } ,并且该访问级别会覆盖< / em> private setpublic访问级别,用于setter方法。因此,生成的IL代码变为:

    virtual

    请注意,现在 .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。实际上不可能在.NET中有virtual,因为它没有意义......就像试图说“没有其他类可以看到这个......但是派生类可以覆盖这个东西,他们无法看到或访问“哪个没有意义。派生类不能覆盖他们甚至看不到的内容。

    private virtual关键字在这种情况下是正确的替代,因为它告诉.NET“只有我自己和派生类可以查看或访问它,派生类可以覆盖此属性。”

    所以我认为“简短”的答案就是“因为.NET中的事情不能是protectedprivate,所以编译器会采用你给它更严格的访问级别

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

      

    'Program.InheritanceTest.Child.Property.set':无法覆盖继承的成员'Program.InheritanceTest.Property.set',因为它未标记为虚拟,抽象或覆盖

    请注意,它是virtual,所以最后的“.set”指的是最终的'Program.InheritanceTest.Property.set'方法,而不是set_Property()属性。并且Property方法仅标记为set_Property(),因为.NET编译器看到了该方法,并从该方法中删除了private,原因如上所述。我想有一个编译器警告或者说“虚拟将被'set'忽略”会有所帮助。

    希望这更有意义......