此代码:
abstract class C
{
protected abstract void F(D d);
}
class D : C
{
protected override void F(D d) { }
void G(C c)
{
c.F(this);
}
}
生成此错误:
无法通过类型为“C”的限定符访问受保护的成员“C.F(D)”;限定符必须是'D'类型(或从中派生出来)
他们在想什么? (会改变那条规则会破坏某些东西吗?)除了让F公开之外,还有其他解决办法吗?
编辑:我现在明白为什么会这样做(谢谢Greg),但我仍然对理性感到有些困惑;给出:
class E : C
{
protected override void F(D d) { }
}
为什么不应该 D能够调用E.F?
编辑错误消息,因此我可能会在其中输入拼写错误。
答案 0 :(得分:36)
这不起作用的原因是因为C#不允许对受保护方法进行跨层次调用。假设有一个类E
也来自C
:
C
/ \
D E
然后,您尝试调用方法的引用实际上可能是类型E
的实例,因此该方法可以在运行时解析为E.F
。这在C#中是不允许的,因为D
无法调用E
的受保护方法,因为E
位于层次结构的另一个分支中,即
var d = new D();
var e = new E();
d.G(e); // oops, now this will call E.F which isn't allowed from D
这是有道理的,因为关键字protected
表示成员“is accessible within its class and by derived class instances”,而E.F不是D的成员。
答案 1 :(得分:17)
“protected”关键字表示只有从该类型派生的类型和类型才能访问该成员。 D与C无关,因此无法访问该成员。
如果您希望能够访问该成员,您有几个选择
修改强>
这个场景在C#规范的第3.5.3节中提到。
不允许这样做的原因是因为它允许跨层次调用。想象一下,除了D之外,还有另一个基类C称为E.如果您的代码可以编译它将允许D访问成员EF这种类型的场景在C#中是不允许的(我相信 CLR,但我不是100%知道的。)
EDIT2 为什么这很糟糕
警告,这是我的意见
现在允许这样做的原因是它很难推断出一个类的行为。访问修饰符的目标是让开发人员准确控制谁可以访问特定方法。想象一下下面的课程
sealed class MyClass : C {
override F(D d) { ... }
}
考虑如果F是一个有时间关键的功能会发生什么。根据目前的行为,我可以推断出我班级的正确性。毕竟只有两种情况会调用MyClass.F.
我可以检查这些调用,并得出关于MyClass如何运作的合理结论。
现在,如果C#允许跨层次保护访问,我就不能做出这样的保证。任何完全不同的程序集中的任何人都可以来自C.然后他们可以随意调用MyClass.F.这使我完全无法推断出我班级的正确性。
答案 2 :(得分:11)
即使D继承自C,D也无法访问C的受保护成员。 D可以访问D的受保护(和私有!)成员,所以如果你把D的另一个实例放在那里而不是C,一切都会工作。但正如格雷格所说,C可能真的是完全不同的东西,并且由于编译器不知道C究竟是什么,它必须阻止D访问D实际上无法访问的东西。
从C#编译器的角度解释这一点的一系列帖子:
答案 3 :(得分:2)
使用静态保护方法可以绕过此限制:
abstract class C
{
protected abstract void F (D d);
// Allows calling F cross-hierarchy for any class derived from C
protected static void F (C c, D d)
{
c.F(d);
}
}
class D : C
{
protected override void F (D d) { }
void G (C c)
{
// c.F(this);
F(c, this);
}
}
从安全角度来看这并不完美(任何人都可以从C
派生),但如果您关心的是从类{{1}的公共接口隐藏方法F
这个技巧可能很有用。
答案 4 :(得分:1)
要理解为什么这种行为有意义,让我们考虑为什么在面向对象的编程语言中我们需要访问修饰符。我们需要来限制可以使用特定类成员的范围。而这反过来又简化了对用法的搜索。
总结:
因此,如果编译器允许以所描述的方式从超类调用受保护的方法,我们最终可能会使用this answer中所述的受保护方法的跨层次调用。在这种情况下,人们必须搜索定义该成员的最多父类的所有子项。这会增加范围。
PS。 Java中实现了相同的行为。
答案 5 :(得分:0)
是的,有可能。我们很可能很快会有这样的例子。
为此,您必须执行以下操作:
public partial class CustomAppointmentEditDialog:EditAppointmentDialog { private RadComboBox cmbShowTimeAs = null;
public CustomAppointmentEditDialog()
{
InitializeComponent();
this.cmbShowTimeAs = this.Controls["cmbShowTimeAs"] as RadComboBox;
}
private void chkConfirmed_ToggleStateChanged(object sender, StateChangedEventArgs args)
{
this.cmbShowTimeAs.SelectedValue = (args.ToggleState == ToggleState.On) ?
(int)AppointmentStatus.Busy : (int)AppointmentStatus.Tentative;
}
}
在上面的代码中,我添加了一个额外的复选框,如果未选中,则将约会的状态(显示时间为)设置为暂定,如果选中,则设置为忙。访问组合框的奇怪方式是因为它目前是私有的。这将在即将到来的2009年第一季度发布中进行更改。
private IEditAppointmentDialog appointmentEditDialog = null;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.radScheduler1.AppointmentEditDialogShowing += new EventHandler<AppointmentEditDialogShowingEventArgs>(radScheduler1_AppointmentEditDialogShowing);
}
void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e)
{
if (this.appointmentEditDialog == null)
{
this.appointmentEditDialog = new CustomAppointmentEditDialog();
}
e.AppointmentEditDialog = this.appointmentEditDialog;
}
我希望这会有所帮助。如果您有其他问题,请随时给我回信。
答案 6 :(得分:0)
简而言之:即使您尝试从派生类中进行访问,也将访问实例的成员视为公开访问。因此,它被拒绝了。
到处都有很多答案,但是没有一个对我清楚:“为什么我不能从孩子那里访问父类的受保护成员”。以上是我在阅读这些令人困惑的答案之后再次查看代码后所理解的。
示例:
class Parent
{
protected int foo = 0;
}
// Child extends from Parent
class Child : Parent
{
public void SomeThing(Parent p)
{
// Here we're trying to access an instance's protected member.
// So doing this...
var foo = p.foo;
}
}
// (this class has nothing to do with the previous ones)
class SomeoneElse
{
public void SomeThing(Parent p)
{
// ...is the same as doing this (i.e. public access).
var foo = p.foo++;
}
}
您认为您可以访问p.foo
,因为您在子类中,但是您是从实例访问它的,就像公共访问一样,因此被拒绝了。
您被允许从类内部而不是从实例访问protected
成员(是的,我们知道):
class Child : Parent
{
public void SomeThing()
{
// I'm allowed to modify parent's protected foo because I'm
// doing so from within the class.
foo++;
}
}
最后,出于完整性考虑,只有在同一个类中,您才能访问实例的protected
甚至private
成员:
class Parent
{
protected int foo = 0;
private int bar = 0;
public void SomeThing(Parent p)
{
// I'm allowed to access an instance's protected and private
// members because I'm within Parent accessing a Parent instance
var foo = p.foo;
p.bar = 3;
}
}