Почему я не могу получить доступ к защищенным членам C#, кроме этого?
этот код:
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. F (D)" через классификатор типа "C"; классификатор должен иметь тип " D " (или производный от него)
Edit: теперь я понимаю, почему это (спасибо Грег) но я все еще немного озадачен рациональным; учитывая:
class E : C
{
protected override void F(D d) { }
}
почему не стоит D быть в состоянии быть в состоянии назвать Е. Ф?
сообщение об ошибке редактируется, поэтому я мог бы поставить опечатку там.
6 ответов
ключевое слово "protected" означает, что только тип и типы, производные от этого типа, могут получить доступ к члену. D не имеет отношения к C, поэтому не может получить доступ к члену.
у вас есть несколько вариантов, если вы хотите иметь доступ к этому члену
- сделать его публичным
- сделать его внутренним. Это позволит любым типам получить доступ к члену в той же сборке (или другим сборкам, если вы добавите друга)
- вывести D из C
редактировать
этот сценарий вызывается в разделе 3.5.3 спецификации C#.
причина, по которой это не разрешено, заключается в том, что это позволит для перекрестных вызовов иерархии. Представьте, что в дополнение к D был еще один базовый класс C, называемый E. Если бы ваш код мог компилироваться, он позволил бы D получить доступ к члену E. F. Этот тип сценария не разрешен в C# (и I верить CLR, но я не 100% знать.)
EDIT2 почему это плохо
будьте осторожны, это мое мнение
причина, по которой это теперь разрешено, заключается в том, что очень сложно рассуждать о поведении класса. Цель модификаторов доступа-дать разработчику контроль над тем, кто именно может получить доступ к определенным методам. Представьте себе следующий класс
sealed class MyClass : C {
override F(D d) { ... }
}
рассмотрим, что произойдет, если F является несколько критической функцией времени. С текущим поведением я могу рассуждать о правильности моего занятия. Ведь есть только два случая, когда MyClass.F будет называться.
- где он вызывается в C
- где я явно вызываю его в MyClass
Я могу изучить эти вызовы и прийти к разумному выводу о том, как функционирует MyClass.
теперь, если C# разрешает кросс-иерархический защищенный доступ, я не могу сделать такую гарантию. Любой человек в совершенно другой сборке может прийти и получить из C. Тогда они могут позвонить в MyClass.F по желанию. Это делает совершенно невозможным рассуждать о правильности моего класса.
причина, по которой это не работает, заключается в том, что C# не разрешает кросс-иерархический вызов защищенных методов. Скажем, был класс E
это также производное от C
:
C
/ \
D E
ссылка вы пытаетесь вызвать метод на самом деле может быть экземпляром типа E
и, таким образом, метод может разрешить во время выполнения E.F
. Это не разрешено в C# as 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
означает, что член "доступен в пределах своего класса и экземплярами производного класса " а Э. Ф. не является членом Д.
хотя D наследуется от C, D не может получить доступ к защищенным членам C. D может получить доступ к защищенным (и частным!) члены, поэтому, если вы поместите туда другой экземпляр D вместо C, все будет работать. Но, как заявил Грег, C действительно может быть чем-то совершенно другим, и поскольку компилятор не знает, что такое C, он должен предотвратить доступ D К чему-то, что D на самом деле не может получить доступ.
серия сообщений, объясняющих это из компилятора C# перспектива:
это ограничение можно обойти, используя статический метод protected:
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
), но если все, что вас волнует, это метод скрытия F
из открытого интерфейса класса C
этот трюк может быть полезен.
чтобы понять, почему такое поведение имеет смысл, давайте рассмотрим, зачем нужны модификаторы доступа в объектно-ориентированных языках программирования. Мы нуждаемся в них чтобы ограничить область, в которой может использоваться конкретный член класса. А это, в свою очередь, упрощает поиск обычаев.
подведем итоги:
- чтобы найти все обычаи public член один должен искать через весь проект (и этого не достаточно случае библиотеки, которая используется независимыми разработчиками)
- чтобы найти все обычаи защищенный член один должен искать через класс контейнера и все его подклассы
- чтобы найти все обычаи частная член один должен искать через контейнер класс.
поэтому, если компилятор разрешил вызывать защищенный метод из суперкласса описанным способом, мы могли бы закончить кросс-иерархический вызов защищенных методов, как описано в ответ. И в такой ситуации нужно было искать всех детей самого родительского класса, который определяет член. И это увеличит масштаб.
PS. Такое же поведение реализовано в Java.
Да, это возможно. Скорее всего, очень скоро у нас будет такой пример.
для этого вы должны сделать следующее:
- наследуйте форму по умолчанию (EditAppointmentDialog) и выполните настройку (для этого вы даже можете использовать конструктор winforms).
открытый частичный класс 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;
}
}
в приведенном выше коде I добавьте дополнительный флажок и установите статус (показать время как) встречи в предварительный, если он не установлен или занят, если он установлен. Странный способ доступа к выпадающему списку заключается в том, что в настоящее время он является частным. Это будет изменено для предстоящего выпуска Q1 2009.
- подписаться на AppointmentEditDialogShowing событие RadScheduler и заменить форму по умолчанию с настроенным один:
частный 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;
}
надеюсь, это поможет. Не стесняйтесь писать мне, если у вас есть дополнительные вопросы.