1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 80
Текст из файла (страница 80)
При объявлении действующим некоторогопространства имен его имя просто добавляется к именам других, которые действуют вданный момент. Следовательно, в этой программе действуют пространства имен System иCounter.Вторая форма использования директивы usingДиректива using обладает еще одной формой применения:using псевдоимя = имя;Здесь элемент псевдоимя задает еще одно имя для класса или пространства имен,заданного элементом имя. Теперь программу счета в обратном порядке переделаем еще раз,чтобы показать, как создается псевдоимя Count для составного имениCounter.CountDown.// Демонстрация использования псевдоимени.using System;// Создаем псевдоимя для класса Counter.CountDown.using Count = Counter.CountDown;// Объявляем пространство имен для счетчиков.namespace Counter {// Простой счетчик для счета в обратном направлении.class CountDown {int val;public CountDown(int n) {val = n;}public void reset(int n) {Глава 16.
Пространства имен, препроцессор и компоновочные файлы437}}val = n;}public int count() {if(val > 0) return val--;else return 0;}class NSDemo {public static void Main() {// Здесь Count используется в качестве имени// вместо Counter.CountDown.Count cd1 = new Count(10);int i;do {i = cd1.count();Console.Write(i + " ");} while(i > 0);Console.WriteLine();Count cd2 = new Count(20);do {i = cd2.count();Console.Write(i + " ");} while(i > 0);Console.WriteLine();}}cd2.reset(4);do {i = cd2.count();Console.Write(i + " ");} while(i > 0);Console.WriteLine();После того как имя Count было определено в качестве еще одного имени длясоставного имени Counter.CountDown, его можно использовать для объявленияобъектов класса CountDown без уточняющего указания пространства имен. Например, внашей программе при выполнении строкиCount cd1 = new Count(10);создается объект класса CountDown.Аддитивность пространств именВ одной программе одно и то же пространство имен можно объявить больше одногораза.
Это позволяет распределить его по нескольким файлам или даже разделить его внутриодного файла. Например, в следующей программе определяется два пространства именCounter. Одно содержит класс CountDown, второе — класс CountUp. При компиляциисодержимое двух пространств имен Counter объединяется в одно.// Демонстрация аддитивности пространств имен.using System;438Часть I. Язык C#// Делаем "видимым" пространство имен Counter.using Counter;// Теперь действующим является первое пространство// имен Counter.namespace Counter {// Простой счетчик для счета в обратном направлении.class CountDown {int val;public CountDown(int n) {val = n;}public void reset(int n) {val = n;}public int count() {if(val > 0) return val--;else return 0;}}}// Теперь действующим является второе пространство// имен Counter.namespace Counter {// Простой счетчик для счета в прямом направлении.class CountUp {int val;int target;public int Target {get{return target;}}public CountUp(int n) {target = n;val = 0;}public void reset(int n) {target = n;val = 0;}}}public int count() {if(val < target) return val++;else return target;}class NSDemo {public static void Main() {Глава 16.
Пространства имен, препроцессор и компоновочные файлы439}}CountDown cd = new CountDown(10);CountUp cu = new CountUp(8);int i;do {i = cd.count();Console.Write(i + " ");} while(i > 0);Console.WriteLine();do {i = cu.count();Console.Write(i + " ");} while(i < cu.Target);При выполнении этой программы получаем следующие результаты:10 9 8 7 6 5 4 3 2 1 00 1 2 3 4 5 6 7 8Хотелось бы обратить ваше внимание вот на что.
Инструкцияusing Counter;делает “видимым” содержимое обоих пространств имен. Поэтому к методам CountDown иCountUp можно обращаться напрямую, без уточняющей информации о пространстве имен.И тот факт, что пространство имен Counter было разделено на две части, не имеетникакого значения.Пространства имен могут быть вложеннымиОдно пространство имен можно вложить в другое.
Рассмотрим следующуюпрограмму:// Демонстрация вложенных пространств имен.using System;namespace NS1 {class ClassA {}public ClassA() {Console.WriteLine("Создание класса ClassA.");}namespace NS2 { // Вложенное пространство имен.class ClassB {}}}public ClassB() {Console. WriteLine("Создание класса ClassB.");}class NestedNSDemo {public static void Main() {NS1.ClassA a = new NS1.ClassA();// NS2.ClassB b = new NS2.ClassB(); // Ошибка!!!440Часть I.
Язык C#// Пространство имен NS2 не находится в зоне видимости.NS1.NS2.ClassB b = new NS1.NS2.ClassB();}}// Здесь все// правильно.Вот результаты выполнения этой программы:Создание класса ClassA.Создание класса ClassB.В этой программе пространство имен NS2 вложено в пространство имен NS1.Следовательно, при обращении к классу ClassB его имя необходимо уточнять, указываяоба пространства имен: как NS1, так и NS2. Одного лишь имени NS2 недостаточно.
Каквидно в программе, имена пространств имен разделяются точкой,Вложенные пространства имен можно задавать с помощью одной инструкции, норазделив их точками. Например, задание вложенных пространств именnamespace OuterNS {namespace InnerNS {// ...}}можно записать в таком виде:namespace OuterNS.InnerNS {// ....}Пространство имен по умолчаниюЕсли для программы не объявлено пространство имен, используется пространствоимен, действующее по умолчанию.
Вот почему необязательно было указывать его дляпрограмм, приведенных в предыдущих главах. Но если для коротких простых программ(подобных тем, что приведены в этой книге) такой стандартный подход вполне приемлем (идаже удобен), большинство реальных программ содержится внутри некоторогопространства имен. Главная причина инкапсуляции программного кода внутрипространства имен состоит в предотвращении конфликтов при совпадении имен.Пространства имен — это еще один инструмент, позволяющий программисту такорганизовать свои программы, чтобы они не теряли жизнеспособности в сложной среде ссетевой структурой.ПрепроцессорВ C# определен ряд директив препроцессора, которые влияют на способинтерпретации исходного кода компилятором.
Эти директивы обрабатывают текстисходного файла, в котором они находятся, еще до трансляции программы в объектный код.Директивы препроцессора — в основном “выходцы” из C++, поскольку препроцессор C#очень похож на тот, который определен в языке C++. Термин директива препроцессорасвоим происхождением обязан тому факту, что эти инструкции традиционнообрабатывались на отдельном этапе компиляции, именуемой процессором предварительнойобработки, или препроцессором (preprocessor).
Современная технология компиляторовбольше не требует отдельного этапа для обработки директив препроцессором, но названиеосталось.Глава 16. Пространства имен, препроцессор и компоновочные файлы441В C# определены следующие директивы препроцессора:#define#endregion#region#elif#error#undef#else#if#warning#endif#lineВсе директивы препроцессора начинаются со знака “#”, Кроме того, каждаядиректива препроцессора должна занимать отдельную строку.Откровенно говоря, поскольку в C# использована современная объектноориентированная архитектура, в директивах препроцессора программисты не испытываютострой необходимости, как это было в языках программирования более ранних поколений.Тем не менее время от времени они могут быть полезными, особенно для условнойкомпиляции.
Рассмотрим все перечисленные выше директивы.#defineДиректива #define определяет последовательность символов, именуемуюидентификатором. С помощью директив #if или #elif можно определить наличие илиотсутствие в программе идентификатора, а результат такой проверки используется дляуправления компиляцией. Общая форма записи директивы #define такова:#define идентификаторОбратите внимание на то, что в инструкции нет завершающей точки с запятой.Между самой директивой #define и идентификатором может стоять любое количествопробелов, но завершить идентификатор можно только символом новой строки.
Например,чтобы определить идентификатор EXPERIMENTAL, используйте следующую директиву:#define EXPERIMENTALНа заметкуВ C/C++ директиву #define можно использовать для выполнения текстовыхподстановок, определяя для заданного значения осмысленное имя, а также длясоздания макросов, действующих подобно функциям. Таков использованиедирективы #define C# не поддерживает. В C# директива #defineиспользуется только для определения идентификатора.#if и #endifДирективы #if и #endif позволяют выполнить условную компиляциюпоследовательности инструкций программного кода в зависимости от того, истинно ливыражение, включающее одно или несколько идентификаторов. Истинным считаетсяидентификатор, определенный в программе. В противном случае он считается ложным.Следовательно, если символ определен с помощью директивы #define, он оцениваетсякак истинный.Общая форма использования директивы #if такова:#if символьное_выражениепоследовательность_инструкций#endifЕсли выражение, стоящее после директивы #if(символьное_выражение),истинно,код,расположенныймеждунеюидирективой#endif(последовательность_инструкций), компилируется.
В противном случае онопускается. Директива #endif означает конец #if-блока.442Часть I. Язык C#Символьное выражение может состоять из одного идентификатора. Более сложноевыражение можно образовать с помощью следующих операторов: !, ==, !=, && и ||.Разрешено также использовать круглые скобки.Рассмотрим пример использования директив #if, #endif и #define.// Демонстрация использования директив #if, #endif// и #define.#define EXPERIMENTALusing System;class Test {public static void Main() {#if EXPERIMENTALConsole.WriteLine("Компилируется для экспериментальной версии.");#endif}}Console.WriteLine("Эта информация отображается во всех версиях.");При выполнении программы отображаются следующие результаты:Компилируется для экспериментальной версии.Эта информация отображается во всех версиях.В этой программе с помощью директивы #define определяется идентификаторEXPERIMENTAL.