Почему я не могу получить доступ к защищенным членам C #, кроме как так? - PullRequest
37 голосов
/ 20 февраля 2009

Этот код:

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?


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

Ответы [ 7 ]

36 голосов
/ 20 февраля 2009

Причина, по которой это не работает, заключается в том, что 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 означает, что член " доступен в пределах своего класса и экземплярами производного класса ", а E.F не является членом D.

17 голосов
/ 20 февраля 2009

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

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

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

EDIT

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

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

РЕДАКТИРОВАТЬ2 Почему это плохо

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

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

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

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

  1. Где он вызывается в C
  2. Где я явно вызываю его в MyClass

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

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

11 голосов
/ 20 февраля 2009

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

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

2 голосов
/ 27 июня 2014

Это ограничение можно обойти, используя статический защищенный метод:

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

1 голос
/ 21 апреля 2010

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

Подведем итог:

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

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

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

0 голосов
/ 17 марта 2019

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


Есть много ответов тут и там, но ни один из них не объяснил мне, "почему я не могу получить доступ к защищенным членам родительского класса от ребенка". Это то, что я понял после того, как снова посмотрел на мой код после прочтения этих запутанных ответов.

Пример:

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;
    }
}
0 голосов
/ 02 февраля 2016

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

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

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

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

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

private IEditAppointmentDialog назначениюEditDialog = 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; 
    } 

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...