Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Под одним именем можно объявить несколько пространств имен. Это дает возможность распределить пространство имен по нескольким файлам или даже разделить его в пределах одного и того же файла исходного кода. Например, в приведеннойниже программе два пространства имен определяются под одним и тем же именемCounter. Одно из них содержит класс CountDown, а другое — класс CountUp. Во время компиляции содержимое обоих пространств имен Counter складывается.// Аддитивный характер пространств имен.using System;// Сделать видимым пространство имен 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; va1 = 0; } public void Reset(int n) { target = n; val = 0; } public int Count() { if(val < target) return val++; else return target; } }}class NSDemo5 { static void Main() { 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(d < cu.Target); }}
Вот к какому результату приводит выполнение этой программы.10 9 8 7 6 5 4 3 2 1 00 1 2 3 4 5 6 7 8
Обратите также внимание на то, что директиваusing Counter;
делает видимым все содержимое пространства имен 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 { static void Main() { NS1.ClassA a = new NS1.ClassA(); // NS2.ClassB b = new NS2.ClassB(); // Неверно!!! Пространство NS2 невидимо NS1.NS2.ClassB b = new NS1.NS2.ClassB(); // Верно! }}
Выполнение этой программы дает следующий результат.Конструирование класса ClassAКонструирование класса ClassB
В этой программе пространство имен NS2 вложено в пространство имен NS1. Поэтому для обращения к классу ClassB необходимо дополнительно указать пространства имен NS1 и NS2. Указания одного лишь пространства имен NS2 для этого недостаточно. Как следует из приведенного выше примера, пространства имен дополнительно указываются через точку. Следовательно, для обращения к классу ClassB в методеMain() необходимо указать его полное имя — NS1.NS2.ClassB.
Пространства имен могут быть вложенными больше, чем на два уровня. В этом случае член вложенного пространства имен должен быть дополнительно определен с помощью всех охватывающих пространств имен.
Вложенные пространства имен можно указать в одном операторе 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); // ... }}