Подтвердить что ты не робот

Почему я не могу получить доступ к С# защищенным членам, кроме этого?

Этот код:

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" (или получен из него)

О чем они думали в мире? (Изменяет ли это правило что-то?) И есть ли способ обойти это, кроме того, что F публично?


Изменить: теперь я получаю причину, почему это (спасибо Грег), но я все еще немного недоумеваю относительно рациональности; Дано:

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

Почему бы D не иметь возможность называть E.F?


Сообщение об ошибке отредактировано, поэтому я мог бы поставить там опечатку.

4b9b3361

Ответ 1

Ключ "protected" означает, что доступ к элементу может получить только тип и типы, которые происходят из этого типа. D не имеет отношения к C, поэтому не может получить доступ к члену.

У вас есть несколько вариантов, если вы хотите иметь доступ к этому члену

  • Сделать общедоступным
  • Сделайте его внутренним. Это позволит любым типам доступа к члену внутри той же сборки (или другим сборкам, если вы добавите друга).
  • Вывести D из C

ИЗМЕНИТЬ

Этот сценарий вызывается в разделе 3.5.3 спецификации С#.

Причина, по которой это недопустимо, заключается в том, что это позволит использовать вызовы с перекрестной иерархией. Представьте себе, что в дополнение к D был еще один базовый класс C с именем E. Если ваш код мог бы скомпилировать его, он бы позволил D получить доступ к элементу EF. Этот тип сценария не разрешен в С# (и я считаю, что CLR, но я не знаю, t 100% знают).

EDIT2 Почему это плохо?

Предостережение, это мое мнение

Причина, по которой это сейчас разрешена, очень затрудняет рассуждение о поведении класса. Цель модификаторов доступа - дать разработчику контроль над тем, кто может получить доступ к определенным методам. Представьте себе следующий класс

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

Рассмотрим, что произойдет, если F - критически важная функция времени. С текущим поведением я могу рассуждать о правильности моего класса. В конце концов, есть только два случая, когда MyClass.F будет вызываться.

  • Где он вызывается в C
  • Если я явно вызываю его в MyClass

Я могу проверить эти вызовы и прийти к разумному выводу о том, как работает MyClass.

Теперь, если С# разрешает защищенный доступ к иерархической иерархии, я не могу гарантировать такую ​​гарантию. Любой, кто находится в совершенно другой сборке, может быть получен и получен из C. Затем они могут позвонить MyClass.F по своему усмотрению. Это делает совершенно невозможным рассуждение о правильности моего класса.

Ответ 2

Причина, по которой это не работает, заключается в том, что С# не разрешает вызовы защищенных методов по иерархической иерархии. Скажем, существовал класс E, также полученный из C:

  C
 / \
D   E

Тогда ссылка, которую вы пытаетесь вызвать метод on, может фактически быть экземпляром типа E, и, таким образом, метод может разрешаться во время выполнения до E.F. Это не разрешено в С#, поскольку 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 означает член " доступно в пределах своего класса и экземпляров производного класса", а EF не является член D.

Ответ 3

Даже если D наследуется от C, D не может получить доступ к C-защищенным членам. D может получить доступ к D защищенным (и частным!) Членам, поэтому, если вы добавите другой экземпляр D вместо C, все будет работать. Но, как сказал Грег, C действительно может быть чем-то совершенно другим, и поскольку компилятор не знает, что такое C на самом деле, он должен помешать D получить доступ к чему-то, к которому D фактически не сможет получить доступ.

Ряд сообщений, объясняющих это с точки зрения компилятора С#:

Ответ 4

Это ограничение можно обойти с помощью статического защищенного метода:

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, этот трюк может быть полезно.

Ответ 5

Чтобы понять, почему такое поведение имеет смысл, рассмотрим, почему нам нужны модификаторы доступа вообще в объектно-ориентированных языках программирования. Нам нужны они , чтобы ограничить область, в которой может использоваться конкретный член класса. А это, в свою очередь, упрощает поиск обычаев.

Подводя итог:

  • чтобы найти все способы использования общедоступного участника, нужно выполнить поиск по целым проектам (и этого недостаточно для библиотеки, которая используется независимыми разработчиками)
  • чтобы найти все способы использования защищенного элемента, нужно выполнить поиск через класс контейнера и все его подклассы
  • чтобы найти все виды использования частного члена, нужно выполнить поиск через контейнерный класс.

Итак, если компилятор позволил описанному способу вызывать защищенный метод из суперкласса, мы могли бы получить перекрестный иерархический вызов защищенных методов, как описано в этом ответе. И в такой ситуации нужно было искать всех детей самого родительского класса, определяющего участника. И это увеличит масштаб.

PS. Такое же поведение реализовано в Java.

Ответ 6

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

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

  • Наследовать форму по умолчанию (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; 
    } 
} 

В приведенном выше коде я добавил дополнительный флажок и установил статус (показать время как) назначения в предварительном порядке, если он не установлен или занят, если он установлен. Странный способ доступа к полем со списком состоит в том, что он является частным в настоящее время. Это будет изменено для предстоящего выпуска Q1 2009.

  1. Подпишитесь на AppointmentEditDialogПоказать событие RadScheduler и заменить форму по умолчанию на вашу индивидуальную:

приватное назначение IEditAppointmentDialogEditDialog = 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; 
    } 

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