Почему я не могу получить доступ к защищенным членам 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 будет называться.

  1. где он вызывается в C
  2. где я явно вызываю его в 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.


Да, это возможно. Скорее всего, очень скоро у нас будет такой пример.

для этого вы должны сделать следующее:

  1. наследуйте форму по умолчанию (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.

  1. подписаться на 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; 
    } 

надеюсь, это поможет. Не стесняйтесь писать мне, если у вас есть дополнительные вопросы.