Введение в ООП с примерами на C#. Часть пятая. Всё о модификаторах доступа
18К открытий19К показов
Рассказывает Akhil Mittal
В прошлых статьях серии “Введение в ООП” мы рассматривали полиморфизм (а также нюансы использования его на практике), наследование и абстрактные классы. В этой части я постараюсь раскрыть все тонкости использования модификаторов доступа, которые знаю сам. Продолжаем погружаться в ООП!
Что такое модификаторы доступа?
Давайте в этот раз возьмём определение из Википедии (в русской Википедии статьи access modifiers нет, поэтому здесь приводим свой перевод — прим. перев.):
Модификаторы доступа (или спецификаторы доступа) — ключевые слова в объектно-ориентированных языках, которые задают (внезапно!) параметры доступа для классов, методов и прочих элементов. Модификаторы доступа — специфичная часть языков программирования для облегчения инкапсуляции компонентов.
Модификаторы public, private, protected
Каждый раз, когда мы создаём класс, мы хотим иметь возможность определять, кто и откуда может взаимодействовать с его членами. Иными словами, нам иногда нужно ограничивать доступ к некоторым членам класса. Есть одно простое правило — члены одного класса всегда имеют доступ друг к другу. Если же говорить про доступ извне, то стоит запомнить, что модификатор доступа по умолчанию — private, т.е. все члены класса доступны только изнутри него самого.
Традиционно сразу переходим к практике. Давайте попробуем выполнить следующий код:
Результатом выполнения этого кода будет:
Modifiers BBB
Modifiers AAA
BBB()
отмечен как public
, соответственно его можно вызывать откуда угодно. Метод AAA()
же никак не отмечен, значит, он является приватным. Однако для члена того же класса (ведь AAA()
и BBB()
принадлежат одному классу, верно?) это не имеет никакого значения.
Теперь попробуем получить доступ к AAA()
напрямую:
Вывод:
‘AccessModifiers.Modifiers.AAA()’ is inaccessible due to its protection level
Для внешних вызовов модификатор private
— непреодолимая преграда. То же самое можно сказать и о модификаторе protected
.
Модификаторы доступа и наследование
Снова попробуем выполнить код:
Запускаем код и видим…
‘AccessModifiers.ModifiersBase.AAA()’ is inaccessible due to its protection level
Приватные члены недоступны даже дочерним классам. Публичные члены доступны всем, это понятно. Модификатор же protected
по сути и обозначает, что член доступен только дочерним классам — вызов CCC()
в примере выше не вызывает никаких ошибок.
Модификатор Internal для классов
Давайте рассмотрим следующий сценарий: мы создаём в новой библиотеке классов (назовём её AccessModifiersLibrary
) класс ClassA
и помечаем его как internal
:
Теперь в созданном ранее файле попробуем выполнить:
Compile time error: ‘AccessModifiersLibrary.ClassA’ is inaccessible due to its protection level
Мы встретили эту ошибку из-за спецификатора доступа internal
, который обозначает, что ClassA
доступен только внутри AccessModifiersLibrary
и ниоткуда больше. Впрочем, если мы уберём этот модификатор, ничего не изменится — internal
является спецификатором по умолчанию.
Модификаторы для пространств имён
Давайте попробуем сделать с предыдущим кодом следующее:
Конечно, это не скомпилируется:
Compile time error: A namespace declaration cannot have modifiers or attributes
Все пространства имён по умолчанию являются публичными, и мы не можем добавить к их объявлению никаких модификаторов, включая ещё один public
.
Приватные классы
Если мы попробуем скомпилировать код, приведённый выше, то получим ошибку:
Compile time error: Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal
Всё правильно: классы могут быть либо public, либо internal.
Подробнее о модификаторах членов класса
Что будет, если мы захотим назначить члену класса больше одного модификатора доступа?
Будет ошибка компиляции:
Compile time error: More than one protection modifier
А как поведёт себя язык, если мы создадим public
метод в internal
классе?
Вывод после компиляции:
‘AccessModifiersLibrary.ClassA’ is inaccessible due to its protection level
Как много ошибок… Дело в том, что какими бы модификаторами не обладали члены internal
класса, их всё равно нельзя вызвать оттуда, где не виден сам класс. А что будет, если мы попробуем сделать наоборот — вызвать private
или internal
метод у public
класса?
‘AccessModifiersLibrary.ClassA’ does not contain a definition for ‘MethodClassA’ and no extension method ‘MethodClassA’ accepting a first argument of type ‘AccessModifiersLibrary.ClassA’ could be found (are you missing a using directive or an assembly reference?)
Не-а, всё равно не работает. А если изменим модификатор метода на internal
?
‘AccessModifiersLibrary.ClassA’ does not contain a definition for ‘MethodClassA’ and no extension method ‘MethodClassA’ accepting a first argument of type ‘AccessModifiersLibrary.ClassA’ could be found (are you missing a using directive or an assembly reference?)
Увы, так делать тоже нельзя.
Модификатор protected internal
Этот код компилируется без ошибок. Модификатор internal proteted
(как не слишком сложно догадаться) даёт понять, что метод доступен как для вызовов из того же файла, в котором он объявлен, так и для вызовов из дочерних классов.
Protected поля
Здесь всё будет немного сложнее. Давайте напишем следующий код:
Если мы его запустим, то получим ошибку:
Cannot access protected member ‘AccessModifiers.AAA.a’ via a qualifier of type ‘AccessModifiers.AAA’; the qualifier must be of type ‘AccessModifiers.BBB’ (or derived from it)
Совершенно неочевидно, правда? Компилятор ругается на строчку aaa.a = 100
из метода MethodBBB
. Почему никаких ошибок не вызывает метод MethodAAA
понять достаточно просто — поле a
объявлено в том же файле, в том же классе, в котором к нему и происходит обращение, это не может быть ошибкой. Почему в классе BBB
доступен член bbb.a
тоже понятно — модификатор protected
прямо разрешает использовать члены родительского класса в дочернем как свои. Почему же вызов aaa.a = 100
из метода MethodBBB
под запретом? Пожалуй, это стоит просто запомнить.
(От редакции) Скорее всего, это сделано, чтобы нельзя было делать следующим образом:
Приоритет модификаторов
Compile time error: Inconsistent accessibility: base class ‘AccessModifiers.AAA’ is less accessible than class ‘AccessModifiers.BBB’
К дочернему классу не может быть большего доступа, чем к родительскому. Как вы понимаете, public
предоставляет гораздо больший доступ, чем модификатор по умолчанию internal
. Причём нельзя делать даже так:
Inconsistent accessibility: return type ‘AccessModifiers.AAA’ is less accessible than method ‘AccessModifiers.BBB.MethodB()’
Или так:
Inconsistent accessibility: field type ‘AccessModifiers.AAA’ is less accessible than field ‘AccessModifiers.BBB.aaa’
Подведём итоги:
- Модификатор доступа по умолчанию для членов класса —
private
; - Модификатор доступа
internal
значит, что доступ разрешён только из того же файла; - У пространств имён нет и не может быть модификаторов доступа (можно считать, что они все
public
); - Классы могут иметь только два модификатора доступа —
internal
(по умолчанию) иpublic
- Модификатор
protected internal
значит, что доступ есть как из того же файла, так и из дочерних классов - Родительский класс не может быть менее доступен, чем дочерний
- Возвращаемое значение метода не может быть менее доступно, чем сам метод
- Поле не может быть более доступно, чем его тип
Работу с константами и sealed
классами (которая тоже осуществляется за счёт модификаторов доступа) мы разберём в следующей статье.
18К открытий19К показов