在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#规范的一部分,只有错误信息是错误的吗?或者应该允许这样做?
我错过了哪个规格部分? (或者编译器丢失了吗?)
答案 0 :(得分:3)
私有虚拟方法和属性无法被覆盖:
你不能将virtual modifier与static,abstract, private或override修饰符。 virtual (C# Reference)
答案 1 :(得分:3)
没有virtual private
之类的东西。因此,外部类定义的set
访问器不是virtual
,因此您无法覆盖它。
对于方法,规范明确禁止private virtual
(C#4规范的第10.6节):
如果声明包含
private
修饰符,则声明不包含以下任何修饰符:virtual
,override
或abstract
。
至于属性(§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个属性的内容。
Property
属性本身。get_Property()
的方法,它是getter。set_Property()
是设置者。在你的代码中,你告诉.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 set
和public
访问级别,用于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中的事情不能是protected
和private
,所以编译器会采用你给它更严格的访问级别
另外,IMO的错误信息非常正确。
'Program.InheritanceTest.Child.Property.set':无法覆盖继承的成员'Program.InheritanceTest.Property.set',因为它未标记为虚拟,抽象或覆盖
请注意,它是virtual
,所以最后的“.set”指的是最终的'Program.InheritanceTest.Property.set'
方法,而不是set_Property()
属性。并且Property
方法仅标记为set_Property()
,因为.NET编译器看到了该方法,并从该方法中删除了private
,原因如上所述。我想有一个编译器警告或者说“虚拟将被'set'忽略”会有所帮助。
希望这更有意义......