为什么可以';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#规范的一部分,并且只有错误消息是错误的吗?还是应该允许这样做?
我缺少规范的哪一部分?(或者缺少编译器?)
不能覆盖私有虚拟方法和属性:
不能将虚拟修饰符与静态、抽象、,private或override修饰符。虚拟(C#参考)
另请参阅是否可以覆盖私有虚拟方法?。
没有virtual private
这样的东西。因此,由外部类定义的set
访问器不是virtual
,因此您不能覆盖它
对于方法,规范明确禁止private virtual
(C#规范的§10.6):
如果声明包含
private
修饰符,则声明不包含以下任何修饰符:virtual
、override
或abstract
。
关于财产(§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个内容。
Property
属性本身- 一个名为
get_Property()
的方法,它是getter - 方法名为
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
{ ... }
请注意,public
和virtual
关键字同时应用于getter和setter方法,但不应用于属性本身。
现在,通过将您的C#代码更改为:
public virtual object Property { get; private set; }
您已经告诉.NET,您希望getter和setter方法是虚拟的但是,则它会运行到private set
,并且访问级别会覆盖setter方法的public
和virtual
访问级别。因此,生成的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中不可能是private
和virtual
,所以编译器采用你给它的更受限制的访问级别
此外,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将被忽略"是有意义的。
希望这更有意义。。。