Директивы условной компиляции
Директивы условной компиляции используются для условного включения или исключения частей исходного файла.
pp-conditional:
pp-if-section pp-elif-sectionsopt pp-else-sectionopt pp-endif
pp-if-section:
whitespaceopt # whitespaceopt if whitespace pp-expression pp-new-line conditional-sectionopt
pp-elif-sections:
pp-elif-section
pp-elif-sections pp-elif-section
pp-elif-section:
whitespaceopt # whitespaceopt elif whitespace pp-expression pp-new-line conditional-sectionopt
pp-else-section:
whitespaceopt # whitespaceopt else pp-new-line conditional-sectionopt
pp-endif:
whitespaceopt # whitespaceopt endif pp-new-line
conditional-section:
input-section
skipped-section
skipped-section:
skipped-section-part
skipped-section skipped-section-part
skipped-section-part:
skipped-charactersopt new-line
pp-directive
skipped-characters:
whitespaceopt not-number-sign input-charactersopt
not-number-sign:
Any input-character except #
В соответствии с синтаксисом, директивы условной компиляции должны быть записаны как наборы, состоящие из (по порядку) директивы #if, нуля или более директив #elif, нуля или одной директивы #else и директивы #endif. Между этими директивами находятся условные разделы исходного кода. Каждый раздел управляется непосредственно предшествующей директивой. Условный раздел может содержать вложенные директивы условной компиляции при условии, что они образуют полные наборы.
ПП условное выражение выделяет не более одного из содержащихся условных разделов для обычной лексической обработки:
· ПП выражения директив #if и #elif вычисляются по порядку, пока одна из них не выдаст значение true. Если выражение выдает true, выбирается условный раздел соответствующей директивы;
· если все ПП выражения дают false и если имеется директива #else, выбирается условный раздел директивы #else;
· иначе условный раздел не выбирается.
Выбранный условный раздел, если он есть, обрабатывается как обычный раздел ввода: в исходном коде, содержащемся в этом разделе, должна соблюдаться лексическая грамматика; лексемы создаются из исходного кода в разделе, а препроцессорные директивы в разделе имеют предписанные действия.
Оставшиеся условные разделы, если они есть, обрабатываются как пропущенные разделы: за исключением препроцессорных директив, исходный код в разделе не обязан соблюдать лексическую грамматику; в этом разделе не создаются лексемы из исходного кода, а препроцессорные директивы должны быть лексически правильными, но в других отношениях они не обрабатываются. Внутри условного раздела, который обрабатывается как пропущенный раздел, любые вложенные условные разделы (содержащиеся во вложенных конструкциях #if...#endif и #region...#endregion) также обрабатываются как пропущенные разделы.
В следующем примере показано, как могут вкладываться директивы условной компиляции:
#define Debug // Debugging on
#undef Trace // Tracing off
class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#if Trace
WriteToLog(this.ToString());
#endif
#endif
CommitHelper();
}
}
За исключением препроцессорных директив, пропущенный исходный код не является предметом лексического анализа. Например, следующее допустимо, несмотря на незавершенный комментарий в разделе #else:
#define Debug // Debugging on
class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#else
/* Do something else
#endif
}
}
При этом следует учитывать, что препроцессорные директивы должны быть лексически правильными даже в пропущенных разделах исходного кода.
Препроцессорные директивы не обрабатываются, если они находятся внутри элементов ввода в несколько строк. Например, программа
class Hello
{
static void Main() {
System.Console.WriteLine(@"hello,
#if Debug
world
#else
Nebraska
#endif
");
}
}
имеет результатом:
hello,
#if Debug
world
#else
Nebraska
#endif
В особенных случаях обрабатываемый набор препроцессорных директив может зависеть от вычисления ПП-выражения. Пример:
#if X
/*
#else
/* */ class Q { }
#endif
в любом случае создается тот же самый поток лексем (class Q { }), независимо от того, определен X или нет. Если X определен, обрабатываются только директивы #if и #endif из-за многострочного комментария. Если X не определен, в набор директив входят три директивы (#if, #else и #endif).
Директивы диагностики
Директивы диагностики используются для явного создания сообщений об ошибках и предупреждениях, передаваемых тем же способом, что и другие ошибки и предупреждения времени компиляции.
pp-diagnostic:
whitespaceopt # whitespaceopt error pp-message
whitespaceopt # whitespaceopt warning pp-message
pp-message:
new-line
whitespace input-charactersopt new-line
Пример:
#warning Code review needed before check-in
#if Debug && Retail
#error A build can't be both debug and retail
#endif
class Test {...}
в любом случае выдается предупреждение ("Code review needed before check-in") и создается ошибка времени компиляции ("A build can’t be both debug and retail"), если определены оба условных символа Debug и Retail. Обратите внимание, что ПП-сообщение может содержать произвольный текст; в частности, оно не должно содержать правильно сформированные лексемы; об этом свидетельствует использование одиночной кавычки в слове can’t.
Директивы областей
Директивы областей используются для явного выделения областей исходного кода.
pp-region:
pp-start-region conditional-sectionopt pp-end-region
pp-start-region:
whitespaceopt # whitespaceopt region pp-message
pp-end-region:
whitespaceopt # whitespaceopt endregion pp-message
В области не вложены семантические значения; области используют программисты и автоматические средства, чтобы пометить раздел исходного кода. Сообщение, указанное в директиве #region или #endregion, также не имеет семантического значения, оно служит только для идентификации области. В соответствующих директивах #region и #endregion могут содержаться разные ПП_сообщения.
Лексическая обработка области
#region
...
#endregion
точно соответствует лексической обработке директивы условной компиляции следующей формы:
#if true
...
#endif
Директивы строк
Директивы строк можно использовать для изменения номеров строк и имен исходных файлов, которые сообщаются в выходных данных компилятора, например в предупреждениях и ошибках, или используются в атрибутах сведений о вызывающем коде (§17.4.4).
Директивы строк обычно используются в средствах метапрограммирования, создающих исходный код на C# из другого текстового ввода.
pp-line:
whitespaceopt # whitespaceopt line whitespace line-indicator pp-new-line
line-indicator:
decimal-digits whitespace file-name
decimal-digits
default
hidden
file-name:
" file-name-characters "
file-name-characters:
file-name-character
file-name-characters file-name-character
file-name-character:
Any input-character except "
При отсутствии директив #line компилятор сообщает в своем выводе настоящие номера строк и имена исходных файлов. При обработке директивы #line, содержащей индикатор строки, не являющийся default, компилятор обрабатывает строку после этой директивы как строку с указанным номером (и именем файла, если оно указано).
Директива #line default отменяет действие всех предшествующих директив #line. Компилятор сообщает настоящие сведения о строках для последующих строк, как если бы директивы #line не обрабатывались.
Директива #line hidden не влияет на файл и номера строк, передаваемые в сообщениях об ошибках, но влияет на отладку на исходном уровне. При отладке все строки между директивой #line hidden и последующей директивой #line (отличной от директивы #line hidden) не имеют сведений о номерах строк. При пошаговом выполнении кода в отладчике эти строки полностью пропускаются.
Обратите внимание, что имя файла отличается от правильных строковых литералов тем, что escape-символы не обрабатываются; символ "\" просто означает обычный символ обратной косой черты в имени файла.
Директивы pragma
Препроцессорная директива #pragma используется для указания компилятору необязательных сведений о контексте. Сведения, предоставленные в директиве #pragma, ни в каком случае не изменяют семантику программы.
pp-pragma:
whitespaceopt # whitespaceopt pragma whitespace pragma-body pp-new-line
pragma-body:
pragma-warning-body
C# предоставляет директивы #pragma для управления предупреждениями компилятора. В будущие версии языка могут быть включены дополнительные директивы #pragma. Чтобы обеспечить взаимодействие с другими компиляторами C#, компилятор C# корпорации Майкрософт не выдает ошибки компиляции для неизвестных директив #pragma; тем не менее, такие директивы создают предупреждения.
Директива pragma warning
Директива #pragma warning используется для отключения или восстановления всех или отдельного набора предупреждающих сообщений во время компиляции последующего текста программы.
pragma-warning-body:
warning whitespace warning-action
warning whitespace warning-action whitespace warning-list
warning-action:
disable
restore
warning-list:
decimal-digits
warning-list whitespaceopt , whitespaceopt decimal-digits
Директива #pragma warning, в которой опущен список предупреждений, влияет на все предупреждения. Директива #pragma warning, в которую включен список предупреждений, влияет только на предупреждения, указанные в этом списке.
Директива #pragma warning disable отключает все предупреждения или заданный набор предупреждений.
Директива #pragma warning restore восстанавливает все предупреждения или заданный набор до состояния, бывшего в начале блока компиляции. Обратите внимание, что если отдельное предупреждение было отключено извне, директива #pragma warning restore (для всех или для конкретного предупреждения) не включит это предупреждение.
В следующем примере показано использование директивы #pragma warning для временного отключения предупреждений, выдаваемых при ссылке на устаревшие члены, с помощью номера предупреждения компилятора Microsoft C#.
using System;
class Program
{
[Obsolete]
static void Foo() {}
static void Main() {
#pragma warning disable 612
Foo();
#pragma warning restore 612
}
}
Основные понятия
Запуск приложения
Сборка с точкой входа называется приложением. При выполнении приложения создается новый домен приложения. Несколько различных экземпляров приложения могут существовать на одном компьютере одновременно, при этом у каждого будет свой домен приложения.
Домен приложения обеспечивает изоляцию приложения путем функционирования в качестве контейнера состояния приложения. Домен приложения функционирует в качестве контейнера и границы типов, заданных в приложении, и используемых библиотек классов. Типы, загружаемые в один домен приложения, отличаются от аналогичных типов, загруженных в другой домен приложения, а экземпляры объектов не используются совместно непосредственным образом между доменами приложений. Например, каждый домен приложения имеет свою копию статических переменных для данных типов, а статический конструктор для типа выполняется не более одного раза на домен приложения. Можно обеспечивать характерные реализации политики или механизмы для создания или удаления доменов приложений.
Запуск приложений происходит, когда среда выполнения вызывает назначенный метод, известный как точка входа приложения. Этот метод точки входа всегда носит имя Main и может иметь одну из следующих сигнатур:
static void Main() {...}
static void Main(string[] args) {...}
static int Main() {...}
static int Main(string[] args) {...}
Как указано выше, точка входа может дополнительно возвратить значение int. Данное возвращаемое значение используется в завершении приложения (§3.2).
Точка входа может дополнительно иметь один формальный параметр. Параметр может иметь любое имя, но тип параметра должен быть string[]. При наличии формального параметра среда выполнения создает и передает аргумент string[], содержащий аргументы командной строки, заданные при запуске приложения. Аргумент string[] никогда не принимает значение NULL, но может иметь нулевую длину, если не были заданы аргументы командной строки.
Так как C# поддерживает перегрузку методов, класс или структура могут содержать несколько определений какого-либо метода при условии того, что их сигнатуры отличаются. Однако в рамках одной программы класс или структуры не могут содержать более одного метода Main, определение которого позволяет использовать этот метод в качестве точки входа приложения. Другие перегруженные версии Main разрешены с условием наличия более одного параметра или с условием наличия только параметра, тип которого не является типом string[].
Приложение может содержать несколько классов и структур. При этом более одного класса или более одной структуры могут содержать метод Main, определение которого позволяет использовать его в качестве точки входа приложения. В таких случаях для выбора одного из методов Main в качестве точки входа требуется использовать внешний механизм (например, компилятор командной строки).
В C# каждый метод должен быть задан в качестве члена класса или структуры. Как правило, объявленная доступность (§3.5.1) метода определяется модификаторами доступа (§10.3.5), указанными в объявлении, и, аналогичным образом, объявленная доступность типа определяется модификаторами доступа, указанными в объявлении. Чтобы заданный метод заданного типа можно было вызвать, и тип, и член должны быть доступны. Однако точка входа приложения является особым случаем. В частности, среда выполнения может вызвать точку доступа приложения независимо от объявленной доступности в объявлениях вмещающих типов.
Метод точки входа приложения может не находиться в объявлении универсального класса.
Во всех других отношениях методы точки входа функционируют аналогично другим методам.
Завершение приложения
При завершении приложения управление возвращается среде выполнения.
Если возвращаемое значение метода точки входа приложения имеет тип int, то оно служит кодом состояния завершения приложения. Целью данного кода является передача среде выполнения сведений о том, успешно ли прошло выполнение, или имел место сбой.
Если возвращаемое значение метода точки входа имеет тип void, то достижение правой фигурной скобки (}), завершающей данный метод, или выполнение оператора return без выражения приведет к коду статуса завершения со значением 0.
Перед завершением приложения для всех его объектов, которые еще не были собраны сборщиком мусора, вызываются деструкторы, если такая очистка не была отключена (например, путем вызова метода библиотеки GC.SuppressFinalize).
Объявления
Объявления в программе на C# определяют составные элементы программы. Программы на C# организованы посредством использования пространств имен (§9), которые могут содержать объявления типов и объявления вложенных пространств имен. Объявления типов (§9.6) используются для задания классов (§10), структур (§10.14), интерфейсов (§13), перечислений (§14) и делегатов (§15). Типы членов, разрешенных в объявлении типов, зависят от формы объявления типа. Например, объявления классов могут содержать объявления для констант (§10.4), полей (§10.5), методов (§10.6), свойств (§10.7), событий (§10.8), индексаторов (§10.9), операторов (§10.10), конструкторов экземпляров (§10.11), статических конструкторов (§10.12), деструкторов (§10.13) и вложенных типов (§10.3.8).
Объявление определяет имя в области объявления, которому принадлежит объявление. За исключением перегруженных членов (§3.6), наличие двух или более объявлений, представляющих члены с одним именем в области объявления, является ошибкой времени компиляции. Недопустимо, чтобы область объявления содержала различные типы членов с одинаковыми именами. Например, область объявления не может содержать поля и метода с одинаковыми именами.
Существует несколько типов областей объявлений, которые представлены далее.
· В рамках всех исходных файлов программы объявления членов пространства имен без вмещающего объявления пространства имен являются членами единой комбинированной области объявления, которая называется глобальной областью объявления.
· В рамках всех исходных файлов программы объявления членов пространства имен в объявлении пространства имен с аналогичным полным именем пространства имен являются членами одной комбинированной области объявления.
· Каждый класс, структура или интерфейс объявления создает новую область объявления. Имена размещаются в данной области объявления посредством объявлений членов классов, объявлений членов структур, объявлений членов интерфейсов или параметров типов. Исключая объявления перегруженных конструкторов экземпляров и объявления статических конструкторов, классы или структуры не могут содержать объявления членов с именем, совпадающим с именем класса или структуры. Класс, структура или интерфейс допускает объявление перегруженных методов и индексаторов. Более того, класс или структура допускают объявление перегруженных конструкторов экземпляров и операторов. Например, класс, структура или интерфейс могут содержать несколько объявлений методов с одним именем с условием, что данные объявления методов отличаются по сигнатуре (§3.6). Обратите внимание, что базовые классы не размещаются в области объявления класса, а также базовые интерфейсы не размещаются в области объявления интерфейса. Таким образом производный класс или интерфейс могут содержать объявление члена с одинаковым именем в качестве унаследованного члена. Такой член скрывает унаследованный член.
· Каждое объявление делегата создает новую область объявления. Имена размещаются в данной области объявления посредством формальных параметров (фиксированных параметров и массивов параметров) и параметров типа.
· Каждое объявление перечисляемого типа создает новую область объявления. Имена размещаются в данной области объявления посредством объявлений членов перечисляемых типов.
· Каждое объявление метода, объявление индексатора, объявление оператора, объявление конструктора экземпляров и анонимная функция создает новую область объявления, которая называется областью объявлений локальных переменных. Имена размещаются в данной области объявления посредством формальных параметров (фиксированных параметров и массивов параметров) и параметров типов. Предполагается, что тело члена функции или анонимной функции (при наличии) вложено в область объявлений локальных переменных. Наличие в области объявления локальных переменных и вложенной области объявления локальных переменных элементов с одинаковым именем является ошибкой. Таким образом, в рамках вложенной области объявления невозможно объявить локальную переменную или константу с одинаковым именем в родительской области объявления. Две области объявления могут содержать элементы с одинаковыми именами, если ни одна из областей не содержит другую.
· Каждый блокили блок switch, а также операторы for, foreach и using создают область объявлений для локальных переменных и локальных констант . Имена размещаются в данной области объявления посредством объявлений локальных переменных и объявлений локальных констант. Обратите внимание, что блоки, используемые в качестве тела члена функции или анонимной функции (или внутри такого тела), вложены в область объявлений локальных переменных, объявленной этими функциями для своих параметров. Следовательно, наличие, к примеру, метода с одинаковыми именами локальной переменной и параметра является ошибкой.
· Каждый блок или блок switch создает отдельную область объявления для меток. Имена размещаются в данной области объявления посредством помеченных операторов, а ссылки на имена осуществляются посредством операторов goto. Область объявления меток блока содержит все вложенные блоки. Таким образом в рамках вложенного блока невозможно объявить метку с одинаковым именем метки в родительском блоке.
Текстовый порядок, в котором имена объявляются, в общем случае не имеет значения. В частности, текстовый порядок не имеет значения для объявления и использования пространств имен, констант, методов, свойств, событий, индексаторов, операторов, конструкторов экземпляров, деструкторов, статических конструкторов и типов. Порядок объявления имеет значение в следующих случаях.
· Порядок объявления объявлений полей и локальных переменных определяет порядок выполнения их инициализаторов (при их наличии).
· Локальные переменные должны быть заданы до их использования (§3.7).
· Порядок объявлений членов перечислений (§14.3) важен, если опущены значения константных выражений.
Область объявления пространства имен является «открыто завершенной», поэтому два объявления пространств имен с одинаковыми полными именами размещаются в одной области объявления. Пример
namespace Megacorp.Data
{
class Customer
{
...
}
}
namespace Megacorp.Data
{
class Order
{
...
}
}
два объявления пространств имен вверху размещаются в одной и той же области объявлений, объявляя в этом случае два класса с полными именами Megacorp.Data.Customer и Megacorp.Data.Order. Поскольку два объявления размещаются в одной области объявления, если в каждом объявлении содержится объявление класса с одинаковым именем, происходит ошибка времени компиляции.
Согласно указанному выше, область объявления блока содержит любые вложенные блоки. Поэтому в следующем примере методы F и G приводят к ошибке времени выполнения, так как имя i объявлено во внешнем блоке и не может быть повторно объявлено во внутреннем блоке. Однако методы H и I верны, так как два имени i объявлены в отдельных невложенных блоках.
class A
{
void F() {
int i = 0;
if (true) {
int i = 1;
}
}
void G() {
if (true) {
int i = 0;
}
int i = 1;
}
void H() {
if (true) {
int i = 0;
}
if (true) {
int i = 1;
}
}
void I() {
for (int i = 0; i < 10; i++)
H();
for (int i = 0; i < 10; i++)
H();
}
}
Члены
Пространства имен и типы содержат члены. Члены сущности обычно доступны посредством использования полного имени, начинающегося со ссылки на сущность, затем следует маркер ".", а затем имя члена.
Члены типа либо объявляются в объявлении типа, либо наследуются от базового класса типа. Когда тип наследуется из базового класса, все члены базового класса, исключая конструкторы экземпляров, деструкторы и статические конструкторы, становятся членами производного типа. Объявленная доступность члена базового класса не контролирует наследование члена, наследование распространяется на каждый член, не являющийся конструктором экземпляров, статическим конструктором или деструктором. Однако наследованный член может быть недоступен в производном типе, либо по причине объявленной доступности (§3.5.1), либо по причине того, что он скрыт объявлением в самом типе (§3.7.1.2).
Члены пространства имен
Пространства имен и типы, имеющие родительское пространство имен, являются членами глобального пространства имен. Это непосредственно соответствует именам, объявленным в глобальной области объявления.
Пространства имен и типы, объявленные в рамках пространства имен, являются членами данного пространства имен. Это непосредственно соответствует именам, объявленным в области объявления пространства имен.
Пространства имен не имеют ограничений. Невозможно объявить частные, защищенные или внутренние пространства имен, пространства имен всегда открыто доступны.
Члены структуры
Членами структуры являются члены, объявленные в структуре, и члены, унаследованные от непосредственного базового класса структуры System.ValueType и косвенного базового класса object.
Члены простого типа непосредственно соответствуют членам типа структуры с псевдонимами по простому типу.
· Члены sbyte являются членами структуры System.SByte.
· Члены byte являются членами структуры System.Byte.
· Члены short являются членами структуры System.Int16.
· Члены ushort являются членами структуры System.UInt16.
· Члены int являются членами структуры System.Int32.
· Члены uint являются членами структуры System.UInt32.
· Члены long являются членами структуры System.Int64.
· Члены ulong являются членами структуры System.UInt64.
· Члены char являются членами структуры System.Char.
· Члены float являются членами структуры System.Single.
· Члены double являются членами структуры System.Double.
· Члены decimal являются членами структуры System.Decimal.
· Члены bool являются членами структуры System.Boolean.
Члены перечисления
Членами перечисления являются константы, объявленные в перечислении, и члены, унаследованные от непосредственного базового класса перечисления System.Enum и косвенных базовых классов System.ValueType и object.
Члены класса
Членами класса являются члены, объявленные в классе, и члены, унаследованные от базового класса (исключая класс object, не имеющего базовый класс). Члены, унаследованные из базового класса, включают константы, поля, методы, свойства, события, индексаторы, операторы и типы базового класса, но не включают конструкторы экземпляров, деструкторы и статические конструкторы базового класса. Члены базового класса наследуются вне зависимости от их доступности.
Объявление класса может содержать объявления констант, полей, методов, свойств, событий, индексаторов, операторов, конструкторов экземпляров, деструкторов, статических конструкторов и типов.
Члены object и string непосредственно соответствуют членам типов класса, псевдонимами которых они являются.
· Члены object являются членами класса System.Object.
· Члены string являются членами класса System.String.
Члены интерфейса
Членами интерфейса являются члены, объявленные в интерфейсе и во всех базовых интерфейсах данного интерфейса. Члены в классе object, строго говоря, не являются членами какого-либо интерфейса (§13.2). Тем не менее, члены в классе object доступны при поиске членов интерфейса любого типа (§7.4).
Члены массива
Членами массива являются члены, унаследованные из класса System.Array.
Члены делегата
Членами делегата являются члены, унаследованные из класса System.Delegate.
Доступ к члену
Объявления членов позволяют осуществлять контроль доступа членов. Доступность члена устанавливается посредством объявленной доступности (§3.5.1) члена в сочетании с доступностью непосредственно содержащем типе, при его наличии.
Когда доступ к определенному члену разрешен, член является доступным. И наоборот, когда доступ к определенному члену запрещен, член является недоступным. Доступ к члену разрешен, когда текстовое положение, в котором происходит доступ, включено в домен доступности (§3.5.2) члена.
Объявленная доступность
Возможные варианты объявленной доступности члена представлены ниже.
· Открытая, выбираемая путем включения модификатора public в объявление члена. Интуитивное понимание модификатора public — "доступ не ограничен".
· Защищенная, выбираемая путем включения модификатора protected в объявление члена. Интуитивное понимание модификатора protected — "доступ ограничен классом-контейнером или типами, производными от класса-контейнера".
· Внутренняя, выбираемая путем включения модификатора internal в объявление члена. Интуитивное понимание модификатора internal — "доступ ограничен данной программой".
· Внутренняя защищенная (обозначает защищенную или внутреннюю доступность), выбираемая путем включения модификаторов protected и internal в объявление члена. Интуитивное понимание модификатор protected internal — "доступ ограничен данной программой или типами, производными от класса-контейнера".
· Частная, выбираемая путем включения модификатора private в объявление члена. Интуитивное понимание модификатора private — "доступ ограничен типом-контейнером".
В зависимости от контекста, в котором происходит объявление члена, разрешены только определенные типы объявленной доступности. Кроме того, когда объявление члена не включает модификаторы доступа, контекст, в котором происходит объявление, определяет объявленную доступность по умолчанию.
· Пространства имен неявно имеют объявленную доступность public. Модификаторы доступа запрещены для объявлений пространств имен.
· Типы, объявленные в блоках компиляции, или пространства имен могут иметь объявленную доступность public или internal, а по умолчанию используется объявленная доступность internal.
· Члены класса могут иметь любую из пяти типов объявленной доступности, а по умолчанию используется объявленная доступность private. (Обратите внимание, что тип, объявленный в качестве члена класса, может иметь любую из пяти объявленных доступностей, тогда как тип, объявленный в качестве члена пространства имен, может иметь только объявленную доступность public или internal.)
· Члены структуры могут иметь объявленную доступность public, internal или private, по умолчанию используется объявленная доступность private, так как структуры неявно запечатаны. Члены структуры, размещенные в структуре (т. е. не унаследованные данной структурой), не могут иметь статусы объявленной доступности protected или protected internal. (Обратите внимание, что тип, объявленный в качестве члена структуры, может иметь объявленную доступность public, internal или private, тогда как тип, объявленный в качестве члена пространства имен, может иметь только объявленную доступность public или internal.)
· Члены интерфейса неявно имеют объявленную доступность public. Модификаторы доступа запрещены для объявлений члена интерфейса.
· Члены перечисления неявно имеют объявленную доступность public. Модификаторы доступа запрещены для объявлений члена перечисления.
Домены доступности
Домен доступности члена состоит из (возможно, не связанных) разделов текста программы, в которых разрешен доступ к члену. С целью определения домена доступности члена он считается верхнего уровня, если он не объявлен в рамках типа, и считается вложенным, если он объявлен в рамках другого типа. Дополнительно определяется текст программы как весь текст программы, содержащийся во всех исходных файлах программы, а текст программы типа определяется как весь текст программы, содержащийся в объявлениях типа данного типа (включая, возможно, типы, вложенные в этот тип).
Домен доступности предопределенного типа (например, object, int или double) неограничен.
Домен доступности непривязанного типа верхнего уровня T (§4.4.3), объявленного в программе P, определяется следующим образом.
· Если для T объявлена доступность public, доменом доступности T является текст программы P и любая программа, ссылающаяся на P.
· Если объявленный уровень доступности T — internal, то домен доступности T совпадает с текстом программы P.
Из этих определений следует, что домен доступности свободного типа верхнего уровня всегда является по крайней мере текстом программы, в которой данный тип объявлен.
Доменом доступности для сформированного типа T<A1, ...,AN> является пересечение домена доступности обобщенного свободного типа T и доменов доступности аргументов типа A1, ...,AN.
Домен доступности вложенного члена M, объявленного в типе T в программе P, определяется следующим образом (учитывая, что M может являться типом).
· Если объявленный уровень доступности M — public, то домен доступности M совпадает с доменом доступности T.
· Если для M объявлена доступность protected internal, пусть D будет объединением текста программы P и текста программы любого типа, произведенного из T, объявленного вне P. Доменом доступности M является пересечение домена доступности T с доменом доступности D.
· Если для M объявлена доступность protected, пусть D будет объединением текста программы T и текста программы любого типа, произведенного из T. Доменом доступности M является пересечение домена доступности T с доменом доступности D.
· Если объявленный уровень доступности M — internal, то домен доступности M представляет собой пересечение домена доступности T с текстом программы P.
· Если объявленный уровень доступности M — private, то домен доступности M совпадает с текстом программы T.
Из этих определений следует, что домен доступности вложенного члена всегда является по крайней мере текстом программы, в которой член объявлен. Дополнительно следует, что домен доступности члена никогда не включает больше объектов, чем домен доступности типа, в котором он объявлен.
Интуитивно это значит, что при доступе к типу или члену M для обеспечения разрешения доступа вычисляются следующие шаги.
· Сначала, если M объявлен в типе (в противоположность блоку компиляции или пространству имен), при недоступности данного типа возникает ошибка времени компиляции.
· Затем, если для M объявлена доступность public, доступ разрешается.
· В противном случае, если M объявлен как protected internal, доступ разрешается в том случае, когда он происходит в рамках программы, в которой объявлен M, или когда он происходит в рамках класса, произведенного из класса, в котором объявлен M, и доступ выполняется посредством производного типа класса (§3.5.3).
· В противном случае, если M объявлен как protected, доступ разрешается в том случае, когда он происходит в рамках класса, в котором объявлен M, или когда он возникает в рамках класса, произведенного из класса, в котором объявлен M, и доступ выполняется посредством производного типа класса (§3.5.3).
· В противном случае, если для M объявлена доступность internal, доступ разрешается при условии, что он осуществляется в рамках программы, в которой объявлен M.
· В противном случае, если для M объявлена доступность private, доступ разрешается при условии, что он осуществляется в типе, в котором объявлен M.
· В противном случае тип или член недоступны, и возникает ошибка времени компиляции.
В этом примере
public class A
{
public static int X;
internal static int Y;
private static int Z;
}
internal class B
{
public static int X;
internal static int Y;
private static int Z;
public class C
{
public static int X;
internal static int Y;
private static int Z;
}
private class D
{
public static int X;
internal static int Y;
private static int Z;
}
}
классы и члены имеют следующие области доступности.
· Домен доступности A и A.X неограничен.
· Доменом доступности A.Y, B, B.X, B.Y, B.C, B.C.X и B.C.Y является текст содержащей их программы.
· Доменом доступности A.Z является текст программы A.
· Доменом доступности B.Z и B.D является текст программы B, включая текст программ B.C и B.D.
· Доменом доступности B.C.Z является текст программы B.C.
· Доменом доступности B.D.X и B.D.Y является текст программы B, включая текст программы B.C и B.D.
· Доменом доступности B.D.Z является текст программы B.D.
Как видно из примера, область доступности члена никогда не превышает области доступности содержащего типа. Например, несмотря на то что все члены X имеют открытую объявленную доступность, все члены, кроме A.X, имеют домены доступности, ограниченные тип-контейнером.
Согласно описанию в разделе §3.4 все члены базового класса, кроме конструкторов экземпляров, деструкторов и статических конструкторов, наследуются производными типами. Это касается даже закрытых членов базового класса. Однако домен доступности закрытого класса включает только текст программы типа, в котором член объявлен. В этом примере
class A
{
int x;
static void F(B b) {
b.x = 1; // Ok
}
}
class B: A
{
static void F(B b) {
b.x = 1; // Error, x not accessible
}
}
класс B наследует закрытый член x от класса A. Так как член является закрытым, он доступен только в рамках тела класса A. Поэтому доступ к b.x успешно осуществляется в методе A.F, но завершается ошибкой в методе B.F.