简体   繁体   中英

Why can't I override a private member in a nested class?

In C#, nested classes can access private members of the containing class.

Why is it I can't override such members? And why does the compiler get the error wrong?

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

The only error message I get is:

'Program.InheritanceTest.Child.Property.set': cannot override inherited member 'Program.InheritanceTest.Property.set' because it is not marked virtual, abstract, or override

The compiler is clearly getting something wrong, because the whole property is marked as virtual. The get method can override the inherited member.

Is this part of the C# spec, and only the error message is wrong? Or should this be allowed?

What part of the spec am I missing? (Or is the compiler missing?)

Private virtual methods and properties cannot be overridden:

You cannot use the virtual modifier with the static, abstract, private, or override modifiers. virtual (C# Reference)

Also see Can you override private virtual methods? .

There is no such thing as virtual private . Because of that, the set accessor defined by the outer class is not virtual and so you can't override it.

For methods, the specification explicitly forbids private virtual (§10.6 of the C# 4 spec):

If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual , override , or abstract .

As for properties (§10.7):

Property declarations are subject to the same rules as method declarations (§10.6) with regard to valid combinations of modifiers.

The only part of specification that seems to be relevant to virtual properties, where one of the accessors is private is (§10.7.5):

A virtual property declaration specifies that the accessors of the property are virtual. The virtual modifier applies to both accessors of a read-write property—it is not possible for only one accessor of a read-write property to be virtual.

This seems to be in contradiction with what actually happens: only the non- private accessor is made virtual. Unless I'm missing something, I think this is either a bug in documentation or in the compiler. I have created a Connect bug about this, let's see what Microsoft has to say.

For some more information about private virtual methods, see this answer from Eric Lippert .

By definition, private members are not overridable. If you want the setter to be overridable, you can mark it protected instead of 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; }
            }
        }
    }

To more specifically answer your question as to why :

Understand that when your C# code is compiled to IL code, there actually ends up being 3 things for 1 property.

  1. The Property property itself.
  2. A method named get_Property() which is the getter.
  3. A method names set_Property() which is the setter.

In your code, you have told .NET that "I want a virtual property. It then cascades that access level to the getter and setter methods. Actually, in IL code, properties don't specify virtual at all.

For the C# code:

public virtual object Property { get; set; }

The generated IL code is:

.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
    { ... }

Note that the public and virtual keywords are applied to both the getter and the setter methods, but not the property itself.

Now, by changing your C# code to:

public virtual object Property { get; private set; }

You have told .NET that you want your getter and setter methods to be virtual... however , then it runs into private set , and that access level overrides the public and virtual access level, for the setter method. So, the generated IL code becomes:

 .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
    { ... }

Note that now set_Property() is private , and no longer virtual . It is actually impossible to have a private virtual in .NET, because it doesn't make sense... that is like trying to say "no other class can see this... but derived classes can override this thing, which they can't see or access" which doesn't make sense. Derived classes can't override what they can't even see.

The protected keyword is the proper replacement in this case, because it tells .NET "Only myself and derived classes can see or access this, and derived classes can override this property."

So I guess the "short" answer would have just been "because things can't be private and virtual in .NET, so the compiler takes the more restricted access level that you gave it.

Also, IMO the error message is pretty correct.

'Program.InheritanceTest.Child.Property.set': cannot override inherited member 'Program.InheritanceTest.Property.set' because it is not marked virtual, abstract, or override

Note that it is saying 'Program.InheritanceTest.Property.set' so the ".set" at the end is referring to the eventual set_Property() method, not the Property property. And the set_Property() method is marked private only, because the .NET compiler saw that, and removed virtual from that method, for the reason mentioned above. I suppose it would make some sense to have a compiler warning or something saying that "virtual will be ignored for 'set'".

Hopefully that makes more sense...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM