Почему подклассы не могут создавать новые объекты с защищенным конструктором базового класса? - PullRequest
9 голосов
/ 03 июня 2011

Я портирую некоторый код Java на C # и столкнулся с этой идиомой, используемой для копирования объектов:

class Base
{
    int x;
    public Base(int x) { this.x = x; }
    protected Base(Base other) { x = other.x; }
}

class Derived : Base
{
    Base foo;
    public Derived(Derived other)
        : base(other)
    {
        foo = new Base(other.foo); // Error CS1540
    }
}

Ошибка CS1540:

Невозможно получить доступ к защищенному члену Base.Base (Base) через спецификатор типа «Base»;квалификатор должен иметь тип «Производный» (или производный от него)

Я понимаю цель этой ошибки: она предотвращает доступ к защищенным элементам родственных типов.Но Base.Base (Base), очевидно, не будет вызываться на типе одного брата!Это просто не включено в спецификацию, или я пропускаю какую-то причину, по которой это небезопасно?

РЕДАКТИРОВАТЬ: Gah, идиома была new Base(other.foo) не new Base(other)

Ответы [ 4 ]

4 голосов
/ 03 июня 2011

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

Например, скажем, мы получаем из Base с Derived, как указано выше. Если бы правила были не такими, как вы, вы можете добавить такой метод к Derived:

public static void CallProtectedMethod(Base baseInstance)
{
    baseInstance.ProtectedMethod();
}

и теперь любой может вызвать его так:

Derived.CallProtectedMethod(baseInstance);

в то время как прямой подход терпит неудачу:

baseInstance.ProtectedMethod();

В этом случае baseInstance может действительно иметь тип Base и не иметь ничего общего с Derived. Чтобы предотвратить это и гарантировать, что методы protected останутся protected, если экземпляр действительно не имеет типа Derived, вызов этих методов через другой экземпляр недопустим.

Аналогично для protected конструкторов:

public static Base CreateProtectedBase()
{
    return new Base();
}

и теперь любой может вызвать его так:

var baseInstance = Derived.CreateProtectedBase();

в то время как прямой подход терпит неудачу:

var baseInstance = new Base();
4 голосов
/ 03 июня 2011

Разделы 3.5.2 и 3.5.3 спецификации языка разъясняют это, для удобства я выложу 3.5.2 (короче!) И позволю вам найти 3.5.3 самостоятельно.

В интуитивно понятных терминах при обращении к типу или члену M выполняются следующие шаги, чтобы гарантировать, что доступ разрешен:

  • Сначала, если M объявленвнутри типа (в отличие от модуля компиляции или пространства имен) возникает ошибка времени компиляции, если этот тип недоступен.
  • Затем, если M общедоступен, доступ разрешен.
  • В противном случае, если M защищен внутри, доступ разрешается, если он происходит в программе, в которой объявлен M, или если он происходит в классе, производном от класса, в котором объявлен M, и имеет место через тип производного класса.(§3.5.3).
  • В противном случае, если M защищен, доступ разрешается, если он происходит в пределах класса, в котором объявлен M, или если он происходит в пределах класса, производного от классав котором M объявлен и имеет место через производный тип класса (§3.5.3).
  • В противном случае, если M является внутренним, доступ разрешается, если он происходит в пределахпрограмма, в которой объявлен M.
  • В противном случае, если M является частным, доступ разрешается, если он происходит внутри типа, в котором объявлен M.
  • В противном случае тип или член недоступныи возникает ошибка времени компиляции.

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

3 голосов
/ 03 июня 2011

недоступен, вы можете проверить этот пост для деталей: Многие вопросы: Защищенные конструкторы

1 голос
/ 03 июня 2011

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

class Base
{
     protected int x;
}
class Derived : Base
{
    Base foo;
    void testMethod()
    {
        base.x = 5;
        foo.X = 5;// this throws an error 
    }
}

Измените спецификатор доступа конструктора, чтобы он работал, или вместо создания объекта базового класса (Base) используйте base.X

...