Чтение онлайн

ЖАНРЫ

Полное руководство. С# 4.0
Шрифт:

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

Вложенные пространства имен можно указать в одном операторе namespace, раз делив их точкой. Например, вложенные пространства имен namespace OuterNS { namespace InnerNS { // ... } }

могут быть указаны следующим образом. namespace OuterNS.InnerNS { // ... } Глобальное пространство имен

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

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

Пространства имен помогают предотвратить конфликты имен, но не устранить их полностью. Такой конфликт может, в частности, произойти, когда одно и то же имя объявляется в двух разных пространствах имен и затем предпринимается попытка сделать видимыми оба пространства. Допустим, что два пространства имен содержат класс MyClass. Если попытаться сделать видимыми оба пространства имен с помо щью директив using, то имя MyClass из первого пространства вступит в конфликт с именем MyClass из второго пространства, обусловив появление ошибки неоднознач ности. В таком случае для указания предполагаемого пространства имен явным обра зом можно воспользоваться описателем псевдонима пространства имен ::.

Ниже приведена общая форма оператора ::. псевдоним_пространства_имен::идентификатор

Здесь псевдонимпространстваимен обозначает конкретное имя псевдонима про странства имен, а идентификатор — имя члена этого пространства.

Для того чтобы стало понятнее назначение описателя псевдонима пространства имен, рассмотрим следующий пример программы, в которой создаются два простран ства имен, Counter и AnotherCounter, и в обоих пространствах объявляется класс CountDown. Затем оба пространства имен становятся видимыми с помощью директив using. И наконец, в методе Main предпринимается попытка получить экземпляр объекта типа CountDown. // Продемонстрировать необходимость описателя ::. using System; // Использовать оба пространства имен Counter и AnotherCounter. using Counter; using AnotherCounter; // Объявить пространство имен для счетчиков. namespace Counter { // Простой вычитающий счетчик. class CountDown { int val; public CountDown(int n) { val = n; } // ... } } // Объявить еще одно пространство имен для счетчиков. namespace AnotherCounter { // Объявить еще один класс CountDown, принадлежащий // пространству имен AnotherCounter. class CountDown { int val; public CountDown(int n) { val = n; } // ... } } class WhyAliasQualifier { static void Main { int i; // Следующая строка, по существу, неоднозначна! // Неясно, делается ли в ней ссылка на класс CountDown // из пространства имен Counter или AnotherCounter? CountDown cd1 = new CountDown(10); // Ошибка! ! ! // ... } }

Если попытаться скомпилировать эту программу, то будет получено сообщение об ошибке, уведомляющее о неоднозначности в следующей строке кода из метода Main. CountDown cd1 = new CountDown(10); // Ошибка!!!

Причина подобной неоднозначности заключается в том, что в обоих пространствах имен, Counter и AnotherCounter, объявлен класс CountDown и оба пространства сделаны видимыми. Поэтому неясно, к какому именно варианту класса CountDown следует отнести приведенное выше объявление. Для устранения подобного рода недо разумений и предназначен описатель ::.

Для того чтобы воспользоваться описателем ::, необходимо сначала определить псевдоним для пространства имен, которое требуется описать, а затем дополнить опи сание неоднозначного элемента этим псевдонимом. Ниже приведен вариант преды дущего примера программы, в котором устраняется упомянутая выше неоднознач ность. // Продемонстрировать применение описателя ::. using System; using Counter; using AnotherCounter; // Присвоить классу Counter псевдоним Ctr. using Ctr = Counter; //

Объявить пространство имен для счетчиков. namespace Counter { // Простой вычитающий счетчик. class CountDown { int val; public CountDown(int n) { val = n; } // ... } } // Объявить еще одно пространство имен для счетчиков. namespace AnotherCounter { // Объявить еще один класс CountDown, принадлежащий // пространству имен AnotherCounter. class CountDown { int val; public CountDown(int n) { val = n; } // ... } } class AliasQualifierDemo { static void Main { // Здесь оператор :: разрешает конфликт, предписывая компилятору // использовать класс CountDown из пространства имен Counter. Ctr::CountDown cd1 = new Ctr::CountDown(10); // ... } }

В этом варианте программы для класса Counter сначала указывается псевдоним Ctr в следующей строке кода. using Ctr = Counter;

А затем этот псевдоним используется в методе Main для дополнительного опи сания класса CountDown, как показано ниже. Ctr::CountDown cd1 = new Ctr::CountDown(10);

Описатель :: устраняет неоднозначность, поскольку он явно указывает на то, что следует обратиться к классу CountDown из пространства Ctr, а фактически — Counter. Именно это и делает теперь программу пригодной для компиляции.

Описатель :: можно также использовать вместе с предопределенным идентифика тором global для ссылки на глобальное пространство имен. Например, в приведен ной ниже программе класс CountDown объявляется как в пространстве имен Counter, так и в глобальном пространстве имен. А для доступа к варианту класса CountDown в глобальном пространстве имен служит предопределенный псевдоним global. // Использовать псевдоним глобального пространства имен. using System; // Присвоить классу Counter псевдоним Ctr. using Ctr = Counter; // Объявить пространство имен для счетчиков. namespace Counter { // Простой вычитающий счетчик. class CountDown { int val; public CountDown(int n) { val = n; } // ... } } // Объявить еще один класс CountDown, принадлежащий // глобальному пространству имен. class CountDown { int val; public CountDown(int n) { val = n; } // ... } class GlobalAliasQualifierDemo { static void Main { // Здесь описатель :: предписывает компилятору использовать // класс CountDown из пространства имен Counter. Ctr::CountDown cd1 = new Ctr::CountDown(10); // Далее создать объект класса CountDown из // глобального пространства имен. global::CountDown cd2 = new global::CountDown(10); // ... } }

Обратите внимание на то, что идентификатор global служит для доступа к классу CountDown из используемого по умолчанию пространства имен. global::CountDown cd2 = new global::CountDown(10);

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

И последнее: описатель псевдонима пространства имен можно применять вместе с псевдонимами типа extern, как будет показано в главе 20. Препроцессор

В С# определен ряд директив препроцессора, оказывающих влияние на интерпре тацию исходного кода программы компилятором. Эти директивы определяют поря док интерпретации текста программы перед ее трансляцией в объектный код в том исходном файле, где они появляются. Термин директива препроцессора появился в связи с тем, что подобные инструкции по традиции обрабатывались на отдельной стадии компиляции, называемой препроцессором. Обрабатывать директивы на отдельной ста дии препроцессора в современных компиляторах уже не нужно, но само ее название закрепилось.

Ниже приведены директивы препроцессора, определенные в С#. #define #elif #else #endif #endregion #error #if #line #pragma #region #undef #warning

Все директивы препроцессора начинаются со знака #. Кроме того, каждая директи ва препроцессора должна быть выделена в отдельную строку кода.

Принимая во внимание современную объектно-ориентированную архитектуру языка С#, потребность в директивах препроцессора в нем не столь велика, как в языках программирования предыдущих поколений. Тем не менее они могут быть иногда по лезными, особенно для условной компиляции. В этом разделе все директивы препро цессора рассматриваются по очереди. Директива #define

Поделиться с друзьями: