Reputation: 12624
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?)
Upvotes: 2
Views: 1939
Reputation: 43748
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.
Property
property itself.get_Property()
which is the getter.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...
Upvotes: 1
Reputation: 245038
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
, orabstract
.
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. Thevirtual
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.
Upvotes: 3
Reputation: 4036
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?.
Upvotes: 3