355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Герберт Шилдт » Полное руководство. С# 4.0 » Текст книги (страница 31)
Полное руководство. С# 4.0
  • Текст добавлен: 7 октября 2016, 10:48

Текст книги "Полное руководство. С# 4.0"


Автор книги: Герберт Шилдт



сообщить о нарушении

Текущая страница: 31 (всего у книги 58 страниц)

Как правило, отправитель – это параметр, передаваемый вызывающим кодом с помощью ключевого слова this. А параметр е типа EventArgs содержит дополни тельную информацию о событии и может быть проигнорирован, если он не нужен. Сам класс EventArgs не содержит поля, которые могут быть использованы для передачи дополнительных данных обработчику. Напротив, EventArgs служит в ка честве базового класса, от которого получается производньгй класс, содержащий все необходимые поля. Тем не менее в классе EventArgs имеется одно поле Empty типа static, которое представляет собой объект типа EventArgs без данных. Ниже приведен пример программы, в которой формируется .NET-совместимое событие. // Пример формирования .NET-совместимого события. using System; // Объявить класс, производный от класса EventArgs. class MyEventArgs : EventArgs { public int EventNum; } // Объявить тип делегата для события. delegate void MyEventHandler(object source, MyEventArgs arg); // Объявить класс, содержащий событие. class MyEvent { static int count = 0; public event MyEventHandler SomeEvent; // Этот метод запускает событие SomeEvent. public void OnSomeEvent() { MyEventArgs arg = new MyEventArgs(); if(SomeEvent != null) { arg.EventNum = count++; SomeEvent(this, arg); } } } class X { public void Handler(object source, MyEventArgs arg) { Console.WriteLine("Событие " + arg.EventNum + " получено объектом класса X."); Console.WriteLine("Источник: " + source); Console.WriteLine(); } } class Y { public void Handler(object source, MyEventArgs arg) { Console.WriteLine("Событие " + arg.EventNum + " получено объектом класса Y."); Console.WriteLine("Источник: " + source); Console.WriteLine(); } } class EventDemo6 { static void Main() { X ob1 = new X(); Y ob2 = new Y(); MyEvent evt = new MyEvent(); // Добавить обработчик Handler() в цепочку событий. evt.SomeEvent += ob1.Handler; evt.SomeEvent += ob2.Handler; // Запустить событие. evt.OnSomeEvent(); evt.OnSomeEvent(); } }

Ниже приведен результат выполнения этой программы. Событие 0 получено объектом класса X Источник: MyEvent Событие 0 получено объектом класса Y Источник: MyEvent Событие 1 получено объектом класса X Источник: MyEvent Событие 1 получено объектом класса Y Источник: MyEvent

В данном примере создается класс MyEventArgs, производный от класса EventArgs. В классе MyEventArgs добавляется лишь одно его собственное поле: EventNum. Затем объявляется делегат MyEventHandler, принимающий два параметра, требующиеся для среды .NET Framework. Как пояснялось выше, первый параметр содержит ссыл ку на объект, формирующий событие, а второй параметр – ссылку на объект класса EventArgs или производного от него класса. Обработчики событий Handler(), опре деляемые в классах X и Y, принимают параметры тех же самых типов.

В классе MyEvent объявляется событие SomeEvent типа MyEventHandler. Это событие запускается в методе OnSomeEvent() с помощью делегата SomeEvent, ко торому в качестве первого аргумента передается ссылка this, а вторым аргумен том служит экземпляр объекта типа MyEventArgs. Таким образом, делегату типа MyEventHandler передаются надлежащие аргументы в соответствии с требованиями совместимости со средой .NET. Применение делегатов EventHandler и EventHandler

В приведенном выше примере программы объявлялся собственный делегат со бытия. Но как правило, в этом не никакой необходимости, поскольку в среде .NET Framework предоставляется встроенный обобщенный делегат под названием EventHandler. (Более подробно обобщенные типы рассматриваются в главе 18.) В данном случае тип TEventArgs обозначает тип аргумента, передаваемого параметру EventArgs события. Например, в приведенной выше программе событие SomeEvent может быть объявлено в классе MyEvent следующим образом. public event EventHandler SomeEvent;

В общем, рекомендуется пользоваться именно таким способом, а не определять собственный делегат.

Для обработки многих событий параметр типа EventArgs оказывается ненуж ным. Поэтому с целью упростить создание кода в подобных ситуациях в среду .NET Framework внедрен необобщенный делегат типа EventHandler. Он может быть ис пользован для объявления обработчиков событий, которым не требуется дополни тельная информация о событиях. Ниже приведен пример использования делегата EventHandler. // Использовать встроенный делегат EventHandler. using System; // Объявить класс, содержащий событие, class MyEvent { public event EventHandler SomeEvent; // использовать делегат EventHandler // Этот метод вызывается для запуска события. public void OnSomeEvent() { if(SomeEvent != null) SomeEvent(this, EventArgs.Empty); } } class EventDemo7 { static void Handler(object source, EventArgs arg) { Console.WriteLine("Произошло событие"); Console.WriteLine("Источник: " + source); } static void Main() { MyEvent evt = new MyEvent(); // Добавить обработчик Handler() в цепочку событий. evt.SomeEvent += Handler; // Запустить событие. evt.OnSomeEvent(); } }

В данном примере параметр типа EventArgs не используется, поэтому в качестве этого параметра передается объект-заполнитель EventArgs.Empty. Результат выпол нения кода из данного примера следующий. Произошло событие Источник: MyEvent Практический пример обработки событий

События нередко применяются в таких ориентированных на обмен сообщениями средах, как Windows. В подобной среде программа просто ожидает до тех пор, пока не будет получено конкретное сообщение, а затем она предпринимает соответствую щее действие. Такая архитектура вполне пригодна для обработки событий средствами С#, поскольку дает возможность создавать обработчики событий для реагирования на различные сообщения и затем просто вызывать обработчик при получении кон кретного сообщения. Так, щелчок левой кнопкой мыши может быть связан с событием LButtonClick. При получении сообщения о щелчке левой кнопкой мыши вызывает ся метод OnLButtonClick(), и об этом событии уведомляются все зарегистрирован ные обработчики.

Разработка программ для Windows, демонстрирующих такой подход, выходит за рамки этой главы, тем не менее, рассмотрим пример, дающий представление о принципе, по которому действует данный подход. В приведенной ниже програм ме создается обработчик событий, связанных с нажатием клавиш. Всякий раз, когда на клавиатуре нажимается клавиша, запускается событие KeyPress при вызове ме тода OnKeyPress(). Следует заметить, что в этой программе формируются .NET– совместимые события и что их обработчики предоставляются в лямбда-выражениях. // Пример обработки событий, связанных с нажатием клавиш на клавиатуре. using System; // Создать класс, производный от класса EventArgs и // хранящий символ нажатой клавиши. class KeyEventArgs : EventArgs { public char ch; } // Объявить класс события, связанного с нажатием клавиш на клавиатуре. class KeyEvent { public event EventHandler KeyPress; // Этот метод вызывается при нажатии клавиши. public void OnKeyPress(char key) { KeyEventArgs k = new KeyEventArgs(); if(KeyPress != null) { k.ch = key; KeyPress (this, k); } } } // Продемонстрировать обработку события типа KeyEvent. class KeyEventDemo { static void Main() { KeyEvent kevt = new KeyEvent(); ConsoleKeyInfo key; int count = 0; // Использовать лямбда-выражение для отображения факта нажатия клавиши. kevt.KeyPress += (sender, е) => Console.WriteLine(" Получено сообщение о нажатии клавиши: " + e.ch); // Использовать лямбда-выражение для подсчета нажатых клавиш. kevt.KeyPress += (sender, е) => count++; // count – это внешняя переменная Console.WriteLine("Введите несколько символов. " + "По завершении введите точку."); do { key = Console.ReadKey(); kevt.OnKeyPress(key.KeyChar); } while(key.KeyChar != '.'); Console.WriteLine("Было нажато " + count + " клавиш."); } }

Вот, например, к какому результату приводит выполнение этой программы. Введите несколько символов. По завершении введите точку. t Получено сообщение о нажатии клавиши: t е Получено сообщение о нажатии клавиши: е s Получено сообщение о нажатии клавиши: s t Получено сообщение о нажатии клавиши: t . Получено сообщение о нажатии клавиши: . Было нажато 5 клавиш.

В самом начале этой программы объявляется класс KeyEventArgs, производный от класса EventArgs и служащий для передачи сообщения о нажатии клавиши об работчику событий. Затем объявляется обобщенный делегат EventHandler, опреде ляющий обработчик событий, связанных с нажатием клавиш. Эти события инкапсу лируются в классе KeyEvent, где определяется событие KeyPress.

В методе Main() сначала создается объект kevt класса KeyEvent. Затем в це почку событий kevt.KeyPress добавляется обработчик, предоставляемый лямбда– выражением. В этом обработчике отображается факт каждого нажатия клавиши, как показано ниже. kevt.KeyPress += (sender, е) => Console.WriteLine(" Получено сообщение о нажатии клавиши: " + e.ch);

Далее в цепочку событий kevt.KeyPress добавляется еще один обработчик, пре доставляемый лямбда-выражением. В этом обработчике подсчитывается количество нажатых клавиш, как показано ниже. kevt.KeyPress += (sender, е) => count++; // count – это внешняя переменная

Обратите внимание на то, что count является локальной переменной, объявленной в методе Main() и инициализированной нулевым значением.

Далее начинает выполняться цикл, в котором метод kevt.OnKeyPress() вызыва ется при нажатии клавиши. Об этом событии уведомляются все зарегистрированные обработчики событий. По окончании цикла отображается количество нажатых кла виш. Несмотря на всю свою простоту, данный пример наглядно демонстрирует саму суть обработки событий средствами С#. Аналогичный подход может быть использован и для обработки других событий. Безусловно, в некоторых случаях анонимные обра ботчики событий могут оказаться непригодными, и тогда придется внедрить имено ванные методы.

ГЛАВА 16. Пространства имен, препроцессор и сборки

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

О пространстве имен уже вкратце упоминалось в главе 2 в связи с тем, что это основополагающее понятие для С#. В действительности пространство имен в той или иной сте пени используется в каждой программе на С#. Потребность в подробном рассмотрении пространств имен не возникала до сих пор потому, что для каждой программы на C# авто матически предоставляется используемое по умолчанию глобальное пространство имен. Следовательно, в приме рах программ, представленных в предыдущих главах, ис пользовалось глобальное пространство имен. Но во многих реальных программах приходится создавать собственные пространства имен или же организовать взаимодействие с другими пространствами имен. Подобные пространства будут представлены далее во всех подробностях.

Пространство имен определяет область объявлений, в которой допускается хранить одно множество имен от дельно от другого. По существу, имена, объявленные в одном пространстве имен, не будут вступать в конфликт с аналогичными именами, объявленными в другой обла сти. Так, в библиотеке классов для среды .NET Framework, которая одновременно является библиотекой классов С#, используется пространство имен System. Именно поэтому строка кода using System;

обычно вводится в самом начале любой программы на С#. Как пояснялось в главе 14, классы ввода-вывода определены в пространстве имен System.IO, подчиненном про странству имен System. Ему подчинены и многие другие пространства имен, относя щиеся к разным частям библиотеки классов С#.

Пространства имен важны потому, что за последние годы в программировании "расплодились" в огромном количестве имена переменных, методов, свойств и клас сов, применяемых в библиотечных программах, стороннем и собственном коде. Поэто му без отдельных пространств все эти имена будут соперничать за место в глобальном пространстве имен, порождая конфликтные ситуации. Так, если в программе опреде лен класс Finder, то этот класс может вступить в конфликт с другим классом Finder, доступным в сторонней библиотеке, используемой в этой программе. К счастью, по добного конфликта можно избежать, используя отдельные пространства имен, огра ничивающие область видимости объявленных в них имен. Объявление пространства имен

Пространство имен объявляется с помощью ключевого слова namespace. Ниже приведена общая форма объявления пространства имен: namespace имя { // члены }

где имя обозначает конкретное имя объявляемого пространства имен. При объявлении пространства имен определяется область его действия. Все, что объявляется непосред ственно в этом пространстве, оказывается в пределах его области действия. В простран стве имен можно объявить классы, структуры, делегаты, перечисления, интерфейсы или другие пространства имен.

Ниже приведен пример объявления namespace для создания пространства имен Counter. В этом пространстве локализуется имя, используемое для реализации про стого класса вычитающего счетчика CountDown. // Объявить пространство имен для счетчиков. 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.

Обратите внимание на то, что класс CountDown объявляется в пределах области действия пространства имен Counter. Для того чтобы проработать этот пример на практике, поместите приведенный выше код в файл Counter.cs.

Ниже приведен пример программы, демонстрирующий применение пространства имен Counter. // Продемонстрировать применение пространства имен Counter. using System; class NSDemo { static void Main() { // Обратите внимание на то, как класс CountDown // определяется с помощью пространства имен Counter. Counter.CountDown cd1 = new Counter.CountDown(10); int i; do { i = cd1.Count(); Console.Write(i + " "); } while(i > 0); Console.WriteLine(); // Еще раз обратите внимание на то, как класс CountDown // определяется с помощью пространства имен Counter. Counter.CountDown cd2 = new Counter.CountDown(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(); } }

При выполнении этой программы получается следующий результат. 10 9 8 7 6 5 4 3 2 1 0 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 4 3 2 1 0

Для того чтобы скомпилировать эту программу, вы должны включить приве денный выше код в отдельный файл и указать его вместе с упоминавшимся выше файлом, содержащим код объявления пространства имен Counter. Если этот код находится в файле NSDemo.cs, а код объявления пространства имен Counter – в фай ле Counter.cs, то для компиляции программы используется следующая командная строка. csc NSDemo.cs counter.cs

Некоторые важные аспекты данной программы заслуживают более пристального внимания. Во-первых, при создании объекта класса CountDown необходимо допол нительно определить его имя с помощью пространства имен Counter, как показано ниже. Ведь класс CountDown объявлен в пространстве имен Counter. Counter.CountDown cd1 = new Counter.CountDown(10);

Это правило можно обобщить: всякий раз, когда используется член пространства имен, его имя необходимо дополнительно определить с помощью этого пространства имен. В противном случае член пространства имен не будет обнаружен компилятором.

Во-вторых, как только объект типа Counter будет создан, дополнительно опреде лять его члены с помощью пространства имен уже не придется. Следовательно, метод cd1.Count() может быть вызван непосредственно без дополнительного указания про странства имен, как в приведенной ниже строке кода. i = cd1.Count();

И в-третьих, ради наглядности примера рассматриваемая здесь программа была разделена на два отдельных файла. В одном файле содержится код объявления про странства имен Counter, а в другом – код самой программы NSDemo. Но оба фраг мента кода можно было бы объединить в единый файл. Более того, в одном файле ис ходного кода может содержаться два или более пространства имен со своими собствен ными областями объявлений. Когда оканчивается действие внутреннего пространства имен, возобновляется действие внешнего пространства имен – в примере с Counter это глобальное пространство имен. Ради большей ясности в последующих примерах все пространства имен, требующиеся в программе, будут представлены в одном и том же файле. Следует, однако, иметь в виду, что их допускается распределять по отдель ным файлам, что практикуется чаще в выходном коде. Предотвращение конфликтов имен с помощью пространств имен

Главное преимущество пространств имен заключается в том, что объявленные в них имена не вступают в конфликт с именами, объявленными за их пределами. Например, в приведенной ниже программе определяются два пространства имен. Первым из них является представленное ранее пространство имен Counter, а вторым – Counter2. Оба пространства имен содержат классы с одинаковым именем CountDown, но по скольку это разные пространства, то оба класса CountDown не вступают в конфликт друг с другом. Кроме того, оба пространства имен определены в одном и том же фай ле. Как пояснялось выше, это вполне допустимо. Безусловно, каждое из этих про странств имен можно было бы выделить в отдельный файл, если бы в этом возникла потребность. // Пространства имен предотвращают конфликты имен. using System; // Объявить пространство имен 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; } } } // Объявить пространство имен Counter2. namespace Counter2 { /* Этот класс CountDown относится к пространству имен Counter2 и поэтому не вступает в конфликт с аналогичным классом из пространства имен Counter. */ class CountDown { public void Count() { Console.WriteLine("Это метод Count() из " + "пространства имен Counter2."); } } } class NSDemo2 { static void Main() { // Это класс CountDown из пространства имен Counter. Counter.CountDown cd1 = new Counter.CountDown(10); // Это класс CountDown из пространства имен Counter2. Counter2.CountDown cd2 = new Counter2.CountDown(); int i; do { i = cd1.Count(); Console.Write(i + " "); } while(i > 0); Console.WriteLine(); cd2.Count(); } }

Вот к какому результату приводит выполнение этой программы. 10 9 8 7 6 5 4 3 2 1 0 Это метод Count() из пространства имен Counter2.

Как следует из приведенного выше результата, класс CountDown из пространства имен Counter существует отдельно от класса того же названия из пространства имен Counter2, и поэтому конфликт имен не возникает. Несмотря на всю простоту данного примера, он наглядно показывает, как удается избежать конфликта имен в собствен ном коде и коде, написанном другими разработчиками, поместив классы с одинаковы ми именами в разные пространства имен. Директива using

Если в программе присутствуют частые ссылки на члены конкретного пространства имен, то указывать это пространство всякий раз, когда требуется ссылка на него, не очень удобно. Преодолеть это затруднение помогает директива using. В подавляю щем большинстве приводившихся ранее примеров программ с помощью этой ди рективы делалось видимым глобальное для C# пространство имен System, поэтому она отчасти вам уже знакома. Как и следовало ожидать, с помощью директивы using можно сделать видимыми вновь создаваемые пространства имен.

Существуют две формы директивы using. Ниже приведена первая из них: using имя;

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

Приведенная ниже программа является вариантом предыдущего примера, пере работанным с целью продемонстрировать применение директивы using, делающей видимым создаваемое пространство имен. // Продемонстрировать применение директивы using. using System; // Сделать видимым пространство имен Counter. using 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; } } } class NSDemo3 { static void Main() { // Теперь класс CountDown может быть использован непосредственно. CountDown cd1 = new CountDown(10); int i; do { i = cd1.Count(); Console.Write(i + " "); } while(i > 0); Console.WriteLine(); CountDown cd2 = new CountDown(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(); } }

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

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

Теперь пространство имен Counter становится видимым, и поэтому класс CountDown может быть использован непосредственно.

Рассматриваемая здесь программа иллюстрирует еще одно важное обстоятельство: применение одного пространства имен не отменяет действие другого. Когда простран ство имен делается видимым, это просто дает возможность использовать его содержи мое без дополнительного определения имен. Следовательно, в данном примере оба пространства имен, System и Counter, становятся видимыми. Вторая форма директивы using

Вторая форма директивы using позволяет определить еще одно имя (так называе мый псевдоним) типа данных или пространства имен. Эта форма приведена ниже: using псевдоним = имя;

где псевдоним становится еще одним именем типа (например, типа класса) или про странства имен, обозначаемого как имя. После того как псевдоним будет создан, он может быть использован вместо первоначального имени.

Ниже приведен вариант программы из предыдущего примера, измененный с це лью показать создание и применение псевдонима MyCounter вместо составного имени Counter.CountDown. // Продемонстрировать применение псевдонима. using System; // Создать псевдоним для составного имени Counter.CountDown. using MyCounter = Counter.CountDown; // Объявить пространство имен для счетчиков. 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; } } } class NSDemo4 { static void Main() { // Здесь и далее псевдоним MyCounter используется // вместо составного имени Counter.CountDown. MyCounter cd1 = new MyCounter(10); int i; do { i = cd1.Count(); Console.Write(i + " "); } while(i > 0); Console.WriteLine(); MyCounter cd2 = new MyCounter(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(); } }

Псевдоним MyCounter создается с помощью следующего оператора. using MyCounter = Counter.CountDown;

После того как псевдоним будет определен в качестве другого имени класса Counter.CountDown, его можно использовать для объявления объектов без дополни тельного определения имени данного класса. Например, в следующей строке кода из рассматриваемой здесь программы создается объект класса CountDown. MyCounter cd1 = new MyCounter(10); Аддитивный характер пространств имен

Под одним именем можно объявить несколько пространств имен. Это дает воз можность распределить пространство имен по нескольким файлам или даже разде лить его в пределах одного и того же файла исходного кода. Например, в приведенной ниже программе два пространства имен определяются под одним и тем же именем 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 0 0 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); // Ошибка! ! ! // ... } }


    Ваша оценка произведения:

Популярные книги за неделю