为什么我不能访问受C#保护的成员,除此之外?

此代码:

abstract class C { protected abstract void F(D d); } class D : C { protected override void F(D d) { } void G(C c) { cF(this); } } 

产生这个错误:

不能通过'C'types的限定符访问受保护成员'CF(D)'; 限定词必须是“D”型(或从中派生出来)

他们在想什么呢? (会改变这个规则打破了什么?)除了公开F吗?


编辑:我现在得到这是为什么的原因(感谢格雷格 ),但我仍然有点困惑的理性; 给定:

 class E : C { protected override void F(D d) { } } 

为什么D不能够调用EF?


错误消息被编辑,所以我可能会在那里input一个错字。

“保护”关键字意味着只有从该types派生的types和types才能访问成员。 D与C没有任何关系,因此不能访问该成员。

如果您希望能够访问该成员,则有几个选项

  • 公开
  • 使其内部。 这将允许任何types访问相同的程序集内的成员(或其他程序集,如果你添加朋友的)
  • 从C导出D.

编辑

这个场景在C#规范的3.5.3节中被调用。

这是不允许的原因是因为它会允许跨层次调用。 想象一下,除了D之外,还有另外一个C的基类叫E.如果你的代码可以编译,它会允许D访问成员EF这种types的情况在C#中是不允许的(我相信 CLR, 100%知道)。

编辑2为什么这是不好的

警告,这是我的意见

现在允许的原因是很难推断一个类的行为。 访问修饰符的目标是让开发人员能够控制谁可以访问特定的方法。 想象下面的课

 sealed class MyClass : C { override F(D d) { ... } } 

考虑一下如果F是一个有点时间关键的函数会发生什么。 以目前的行为,我可以推论我class的正确性。 毕竟,只有两种情况下MyClass.F将被调用。

  1. 在C中被调用的地方
  2. 我明确地在MyClass中调用它

我可以检查这些调用,并对MyClass的function做出合理的结论。

现在,如果C#允许跨层次保护访问,我不能做出这样的保证。 任何一个完全不同的程序集中的任何人都可以从C中派生出来,然后他们可以随意调用MyClass.F。 这使得完全不可能推断我class的正确性。

这是行不通的原因是因为C#不允许跨层次调用受保护的方法。 说有一个E类也是来自C

  C / \ DE 

那么你试图调用该方法的引用实际上可能是Etypes的一个实例,因此该方法可以在运行时parsing为EF 。 这在C#中是不允许的,因为D不能调用E的受保护的方法,因为E在层次结构的另一个分支中,即

 var d = new D(); var e = new E(); dG(e); // oops, now this will call EF which isn't allowed from D 

这是有道理的,因为关键字protected表示成员“ 可以在其类和派生类实例中访问 ”,EF不是D的成员。

即使D是从Cinheritance的,D也不能访问C的受保护成员。 D可以访问D的protected(和private!)成员,所以如果你把D的另一个实例放在那里,而不是C,那么所有的东西都可以工作。 但是,正如Greg所说的那样,C实际上可能是完全不同的,因为编译器不知道C是什么,所以必须防止D访问D实际上不能访问的东西。

一系列的post从C#编译器的angular度解释了这一点:

  • 为什么我无法从派生类访问受保护的成员
  • 为什么我不能从派生类访问受保护的成员,第二部分:为什么我可以?

这种限制可以通过使用静态保护方法来绕过:

 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) { cF(d); } } class D : C { protected override void F (D d) { } void G (C c) { // cF(this); F(c, this); } } 

从安全的angular度来看这是不完美的(任何人都可以从C派生出来),但是如果你关心的是从类C的公共接口隐藏方法F ,这个技巧可能是有用的。

为了理解为什么这种行为是有道理的,让我们考虑为什么我们在面向对象的编程语言中需要访问修饰符。 我们需要他们限制一个特定的class级成员可以使用的范围 。 而这又简化了search的用法。

总结:

  • find所有需要search整个项目公共成员的用法(这对于独立开发者使用的库来说是不够的)
  • find需要通过容器类及其所有子类进行search的受保护成员的所有用法
  • find需要通过容器类search的私人成员的所有用法。

因此,如果编译器允许以所描述的方式从超类调用protected方法,那么我们最终可以用本答案中所描述的受保护方法的跨层次调用。 在这种情况下,必须search定义会员的最父母class级的所有孩子。 这将增加范围。

PS。 在Java中实现相同的行为。

是的,这是可能的。 我们很可能很快会有这样一个例子。

要做到这一点,你必须做到以下几点:

  1. inheritance默认窗体(EditAppointmentDialog)并进行自定义(甚至可以使用winformsdevise器)。

公共部分类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; } } 

在上面的代码中,我添加了一个额外的checkbox,并将约会的状态(显示时间为)设置为暂时(如果未选中)或设置为繁忙(如果选中)。 访问combobox的奇怪方式是因为它目前是私人的。 这将在即将到来的2009年第一季度发布中发生变化。

  1. 订阅RadScheduler的AppointmentEditDialogShowing事件并将默认表单replace为您自定义的表单:

私人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; } 

我希望这有帮助。 如果您还有其他问题,请不要犹豫,给我回信。