class MyClass {} class UrClass {}
}
В этом примере классы MyClass и UrClass являются частью пространства имен MyStuff.
Кроме классов, пространства имен могут содержать другие типы, такие как структуры и интерфейсы. Одно пространство имен может также содержать вложенные пространства имен с любой глубиной вложенности. У вас может быть пространство имен Namespace2, вло женное в Namespacel, как показано в следующем фрагменте исходного текста: namespace Namespacel
{
//Классы в Namespacel namespace Namespace2
{
//Классы в Namespace2 public class Class2
public void AMethod() { }
}
}
}
Для вызова метода из Class2 в Namespace2 откуда-то извне пространства имен Namespacel применяется следующая запись: Namespacel.Namespace2.Class2.AMethod();
Пространства имен неявно открыты, так что для них нельзя использовать специфика торы доступа, даже спецификатор public.
Удобно добавлять к пространствам имен в ваших программах название вашей фирмы: MyCompany .MathRoutines. (Конечно, если вы работаете на фирме. Вы можете также использовать свое собственное имя. Я бы мог применять для своих программ что-то наподобие CMSCo.MathRoutines или Sphar.MathRoutines.) Добавление названия фирмы предупреждает коллизии имен в вашем коде при использовании двух библиотек сторонних производителей, у которых ока зывается одно и то же базовое имя пространства имен, например, MathRoutines.
Такие "имена с точками" выглядят как вложенные пространства имен, но на самом деле это одно пространство имен, так что System. Data — это полное имя Пространст ва имен, а не имя пространства имен Data, вложенного в пространство имен System. Такое соглашение позволяет проще создавать несколько связанных пространств имен, таких как System. 10, System. Data и System. Text.
Visual Studio Application Wizard помещает каждый формируемый им класс в пространство имен, имеющее такое же имя, как и создаваемый им каталог. Взгляните на любую программу в этой книге, созданную Application Wizard. Например, программа AlignOutput размещается в папке AlignOutput. Имя исходного файла— Program.cs, соответствующее имени класса по умолчанию. Имя пространства имен в Program.cs то же, что и имя папки: AlignOutput. (Можно изменить любое из этих имен, только делайте это ос торожно и аккуратно. Изменение имени общего пространства имен проекта выполняется в окне Properties проекта.)
Глава 19. Работа с файлами и библиотеками |
423 |
Если вы не помещаете ваши классы в пространство имен, С# сам поместит их в глобаль ное пространство имен. Однако лучше использовать собственные пространства имен.
Важность пространств имен
Самое важное в пространствах имен с практической точки зрения то, что от расширяют описание управления доступом, о котором говорилось в главе 11, "Классы" (где были введены такие ключевые слова, как public, private protected, internal и protected internal). Пространства имен рас ширяют управление доступом с помощью дальнейшего ограничения на доступ к членам класса.
Реально пространства имен влияют не на доступность, а на видимость. По умолчанию. классы и методы в пространстве имен NamespaceA невидимы классам в пространстве имен NamespaceB, независимо от их спецификаторов доступа. Но можно сделать мас сы и методы из пространства имен NamespaceB видимыми для пространства имен NamespaceA. Обращаться вы можете только к тому, что видимо для вас.
Видимы ли вам необходимые классы и методы?
Для того чтобы определить, может ли класс Classl в пространстве имен NamespaceA вызывать NamespaceB. Class2 . AMethod () , рассмотрим следующие два элемента.
1.Видим ли класс Class2 из пространства имен NamespaceB вызывающему классу Classl? Это вопрос видимости пространства имен, который будет вскоре рассмотрен.
2.Если ответ на первый вопрос — "да", то "достаточно ли открыты" Class2 и его метод AMethod () классу Classl для доступа? "Достаточная открытость" опре деляется как наличие спецификаторов доступа нужной степени строгости с точки зрения вызывающего класса Classl. Это вопрос управления доступом, рассмат ривающийся в главе 11, "Классы".
Если Class2 находится в сборке, отличной от сборки Classl, он должен быть открыт (publ ic) для Classl для доступа к его членам. Если же это одна и та же сборка, Class2 должен быть объявлен как минимум как internal. Классы мо гут быть только public, protected, internal или private.
Аналогично, метод класса Class2 должен иметь как минимум определенный уровень доступа в каждой из этих ситуаций. Методы добавляют protected internal в список спецификаторов доступа класса. Детальнее об этом расска зывается в главе 11, "Классы".
Вы должны получить ответ "да" на оба поставленных вопроса, чтобы класс Classl мог вызывать методы Class2.
Остальной приведенный здесь материал посвящен вопросам использования и види мости пространств имен.
Как сделать видимыми классы и методы в другом пространстве имен
С# предоставляет два пути сделать элементы в пространстве имен NamespaceB ви димыми в пространстве имен NamespaceA.
424 |
Насть VII. Дополнительные главы |
Применяя полностью квалифицированные имена из пространства имен Name spaceB при использовании их в пространстве имен NamespaceA. Это приводит к коду наподобие приведенного, начинающемуся с имени пространства имен, к которому добавляется имя класса и имя метода:
System.Console.WriteLine("my string");
Устраняя необходимость в полностью квалифицированных именах в пространстве имен NamespaceA посредством директивы using для пространства имен
NamespaceB:
using NamespaceB;
Программы в этой книге используют последний способ — директиву using. Полно стью квалифицированные имена и директивы using будут рассмотрены в двух следую щих разделах.
Доступ к классам с использованием полностью квалифицированных имен
Пространство имен класса является составной частью его расширенного имени, что приводит к первому способу обеспечения видимости класса из одного пространства имен в другом. Рассмотрим следующий пример, в котором не имеется ни одной директи вы using для упрощения обращения к классам в других пространствах имен: namespace MathRoutines
{
class Sort
{
public void SomeFunction(){}
namespace Paint
{
public class PaintColor
{
public |
PaintColor(int nRed, int nGreen, int nBlue) {} |
public |
void Paint() {} |
public |
staticvoid StaticPaint () {} |
}
namespace MathRoutines
{
public class Test
{
static public void Main(string[] args)
//Создание объекта типа Sort из того же пространства
//имен, в котором мы находимся, и вызов некоторой
//функции
Sort obj = new Sort(); obj.SomeFunction();
//Создание объекта в другом пространстве имен —
//обратите внимание, что пространство имен должно
//быть явно включено в каждую ссылку на класс
Глава 19. Работа с файлами и библиотеками |
425 |
Paint.PaintColor black = new Paint.PaintColor(0, 0, 0); black.Paint();
Paint.PaintColor.StaticPaint();
}
}
В этом случае классы Sort и Test содержатся внутри одного и того же пространст ва имен MathRoutines, хотя и объявляются в разных местах файла. Это пространство имен разбито на две части (в данном случае в одном и том же файле).
В обычной ситуации Sort и Test оказались бы в различных исходных файлах С#, которые вы бы собрали в одну программу.
Функция Test. Main () может обращаться к классу Sort без указания его про странства имен, так как оба эти класса находятся в одном и том же пространстве имен, Однако Main () должна указывать пространство имен Paint при обращении к
PaintColor, как это сделано в вызове Paint, PaintColor. StaticPaint (). Здесь использовано полностью квалифицированное имя.
Обратите внимание, что вам не требуется принимать специальных мер при обраще нии к black. Paint ( ) , поскольку класс и пространство имен объекта black известны
Директива using
Обращение к классу с использованием полностью квалифицированного имени быст ро становится раздражающим. С# позволяет избежать излишнего раздражения с помо щью ключевого слова using. Директива using добавляет указанное пространство имен в список пространств имен по умолчанию, в которых С# выполняет поиск при разреше нии имени класса. Следующий пример компилируется без каких-либо замечаний: namespace Paint
{
public class PaintColor
{
public PaintColor(int nRed, int nGreen, int nBlue) {} public void Paint() {}
public static void StaticPaint() {}
namespace MathRoutines
{
//Добавляем Paint к пространствам имен, в которых
//выполняется автоматический поиск
using Paint; public class Test
{
static public void Main(string[] args)
{
//Создаем объект в другом пространстве имен —
//название пространства имен не требуется включать в
//имя, поскольку само пространство имен было включено
//полностью с использованием директивы "using" PaintColor black = new PaintColor(0, 0, 0 ) ;
426 |
Часть VII. Дополнительные главы |
black.Paint(); PaintColor.StaticPaint();
Директива using говорит компилятору: "Если ты не в состоянии найти определен ный класс в текущем пространстве имен, посмотри еще и в этом пространстве имен, мо жет, ты найдешь его там". Можно указать любое количество пространств имен, но все они должны быть указаны в самом начале программы (либо внутри, либо снаружи блока пространства имен), как описано ранее в разделе "Объявление пространств имен".
Все программы включают директиву using System,-. Эта команда дает программе автоматический доступ к функциям, включенным в системную библиотеку, таким как WriteLine ().
Использование полностью квалифицированных имен
Приведенная далее демонстрационная программа NamespaceUse иллюст рирует влияние пространств имен на видимость и использование директивы using и полностью квалифицированных имен, чтобы обеспечить види мость элемента.
//NamespaceUse - демонстрирует доступ к объектам с одним и
//тем же именем в разных пространствах имен
using System; // Все пространства имен нуждаются в этой
//директиве для доступа к классам типа String
//и Console
namespace MainCode
{
using LibraryCodel; // Эта директива упрощает MainCode public class Classl
{
public void AMethod()
{
Console.WriteLine("MainCode.Classl.AMethod() ") ;
}
// Функция Main():
static void Main(string[] args)
//Создание экземпляра класса, содержащего функцию
//Main, в данном пространстве имен
Classl cl = new C l a s s l О ; |
// MainCode.Classl |
cl.AMethod(); |
// |
Никогда |
не вызывайте |
|
// |
Main() |
самостоятельно! |
//Создание экземпляра LibraryCodel.Classl
//Приведенный далее код создает объект
// |
MainCode.Classl, |
а не тот, |
что вы хотели, так как |
// |
нет ни директивы |
using, ни |
полностью |
//квалифицированного имени Classl с2 = new Classl(),• с2.AMethod();
//Однако полностью квалифицированное имя создает
//объект требуемого класса. Имя следует
Глава 19. Работа с файлами и библиотеками |
427 |
//квалифицировать даже при использовании директивы
//using, поскольку оба пространства имен содержат
//класс Classl
LibraryCodel.Classl сЗ = new LibraryCodel.Classl(); c3 .AMethod () ,-
//В то же время создание LibraryCodel.Class2 не
//требует полностью квалифицированного имени,
//поскольку имеется директива using при отсутствии
//коллизии имен; С# может без труда найти Class2 Class2 с4 = new Class2();
с4.AMethod();
//Создание экземпляра LibraryCode2.Classl требует
//полностью квалифицированного имени, как из-за
//отсутствия директивы using для LibraryCode2, так и
//потому, что оба пространства имен имеют Classl
//Примечание: этот способ работает даже несмотря на
//то, что LibraryCode2.Classl объявлен как internal,
// |
а не public, |
поскольку оба класса |
находятся |
в одной |
// |
компилируемой |
сборке |
|
|
|
LibraryCode2.Classl с5 = new LibraryCode2.Classl(); |
с5.AMethod(); |
|
|
|
|
// |
Ожидаем подтверждения пользователя |
|
Console.WriteLine("Нажмите <Enter> для " + |
|
|
|
"завершения программы..."); |
|
Console.Read(); |
|
|
|
|
} |
|
|
|
|
|
namespace |
LibraryCodel |
|
|
|
|
{ |
|
|
|
|
|
public |
class Classl |
// |
Имя дублирует |
Classl в |
другом |
{ |
|
// |
пространстве |
имен |
|
public void AMethod() // |
Имя дублировано в другом |
{ |
|
// |
пространстве |
имен |
|
Console.WriteLine("LibraryCodel.Classl.AMethod()");
} } |
|
|
public class Class2 |
// |
Имя уникально, его нет в другом |
{ |
// |
пространстве имен |
public void AMethod()
{
Console.WriteLine("LibraryCodel.Class2.AMethod()");
namespace |
LibraryCode2 |
|
|
{ |
|
|
|
class Classl |
// |
Нет ключевых слов, описывающих |
{ |
|
// доступ: по умолчанию доступ — |
public |
void AMethod() |
// |
internal |
{
Console.WriteLine("LibraryCode2.Classl.AMethod()");
}
428 |
Часть VII. Дополнительные главы |
Данная демонстрационная программа включает три пространства имен: MainCode, в которое входит один класс Classl, содержащий функцию Main() и один дополни тельный метод AMethod ( ) . Пространство имен LibraryCodel содержит два класса: Classl дублирует имя Classl из пространства имен MainCode, класс Class2 уни кален. Пространство имен LibraryCode2 имеет один класс, также названный Classl, имя которого создавало бы коллизию с именем Classl в другом пространстве имен, ес ли бы эти имена не были разделены и размещены каждое в своем пространстве имен. Каждый их этих классов имеет метод AMethod ().
Функция Main () из MainCode . Classl пытается создать и использовать MainCode. Classl (класс-владелец MainO), LibraryCodel. Classl, Library Codel .Class2 и LibraryCode2 .Classl. После создания объектов функция вызы вает метод AMethod () каждого из них. Каждый метод идентифицирует свое местопо ложение. Вывод демонстрационной программы на экран выглядит следующим образом:
MainCode.Classl.AMethod()
MainCode.Classl.AMethod()
LibraryCodel.Classl.AMethod()
LibraryCodel.Class2.AMethod()
LibraryCode2.Classl.AMethod()
Нажмите <Enter> для завершения программы...
Без разделения на различные пространства имен компилятор не может позволить дублирования имен классов в MainO . При применении пространств имен исходный текст компилируется, но вы должны использовать либо директиву using, либо полно стью квалифицированные имена для обращения к объектам в разных пространствах имен. Пространство имен MainCode содержит директиву using для пространства имен
LibraryCodel, так что функция Main O из MainCode. Classl может обратиться к LibraryCodel. Class2 без полностью квалифицированного имени благодаря нали чию упомянутой директивы.
Попытка создать LibraryCodel. Classl без использования полностью квалифи цированного имени приводит ко второй строке в выводе на экран в рассмотренном при мере. Как видите, оказался создан объект класса MainCode. Classl, а не класса из пространства имен LibraryCodel. Без применения полностью квалифицированного имени компилятор находит Classl в пространстве имен MainCode. Остальные вызовы работают так, как от них и ожидалось.
Однако использование директивы using для пространства имен LibraryCodel сослужит функции Main () плохую службу при ее желании обратиться к Library Codel . Classl, так как имя этого класса дублировано в двух пространствах имен. Не смотря на наличие директивы using, единственным решением является применение полностью квалифицированного имени для доступа к LibraryCodel. Classl.
Последний вызов в Main () для создания и использования объекта Library2 . Classl был бы неверен, если бы пространство имен Library2 находилось в сборке, отличной от сборки MainCode. Причина заключается в том, что спе цификаторы доступа у класса Library2 .Classl не указаны, так что вместо того чтобы быть public, он является internal. Это поведение по умолча нию для классов без спецификаторов доступа (для методов по умолчанию ис пользуется private). Повторяясь еще р а з — всегда явно указывайте уровень доступа к каждому классу и методу.
Глава 19. Работа с файлами и библиотеками |
429 |
После того как вы используете полностью квалифицированные имена или директивы using, ваш класс сможет обратиться к чему угодно в другом пространстве имен, если конечно, это не запрещено ему правилами доступа — как, например, к закрытым членам.
В главе 21, "Использование интерфейса Visual Studio", вы познакомитесь с тем, как создавать проект с несколькими . СS-файлами. Даже при разбиении проекта на несколь ко файлов (обычно по одному классу в файле) один проект равен одной скомпилированной сборке. Библиотеки классов располагаются в файлах с расширением . DLL и не яв ляются выполнимыми программами сами по себе. Они служат для поддержки других программ, предоставляя им полезные классы и методы.
Простейшее определение проекта библиотеки классов — это классы, не содер жащие функции Main ( ) , что отличает библиотеку классов от выполнимой программы.
Visual Studio 2005 позволяет построить либо . ЕХЕ, либо . DLL (либо решение, со держащее их оба). В следующем разделе поясняются основы создания ваших собствен ных библиотек классов. Не беспокойтесь: рабочая лошадка С# вытянет за вас и этот груз.
Менее дорогие версии Visual Studio 2005, такие как Visual С# Express, не позволяют собирать библиотеки классов. Однако вам будет показан один трюк, позволяющий обой ти данное ограничение.
Создание проекта библиотеки классов
Для формирования нового проекта библиотеки в полной версии Visual Studio 2005 выберите тип проекта Class Library в диалоговом окне New Project. Затем пропустите следующий раздел главы и переходите к созданию классов, которые будут составлять библиотеку.
Если вы используете Visual С# Express, процесс создания нового проекта биб лиотеки будет немного более сложным. Несмотря на снижение функциональ ности, которому Microsoft подвергла Visual С# Express, если выполнить опи санные далее действия, все равно можно сформировать библиотеку классов.
1.При создании нового проекта создавайте Console Application (или Win dows Application).
Если вы не уверены в том, как это делается, вернитесь к главам 1, "Создание вашей первой Windows-программы на С#", и 2, "Создание консольного приложения на C#", Обычно библиотека классов формируется под видом консольного приложения.
2.В Solution Explorer удалите файл Program.cs, закройте и сохраните ре шение.
Поскольку библиотека классов не может содержать функцию Main ( ) , вы просто удаляете файл с ней.
3.Запустите Блокнот Windows или другой обычный текстовый редактор и от кройте в нем файл ProjectName.csproj из вновь созданного вами проекта.
430 |
Часть VII. Дополнительные главы |
Он выглядит страшновато, но это всего лишь масса информации о программе, за писанная с использованием языка XML.
4.Воспользуйтесь командой меню Edit"=>Find для того, чтобы найти строку:
<OutputType>Exe</OutputType>
Если вы создали проект приложения Windows, а не консольного приложения, найдите строку
<OutputType>WinExe</OutputТуре>
Это примерно восьмая строка в файле.
5.Замените в найденной строке Ехе (или WinExe) на Library:
<OutputType>Library</OutputType >
Вот и все!
6.Сохраните файл и заново откройте проект в Visual С# Express.
7.Добавляйте в проект новые классы и работайте. Только убедитесь, что вы не раз местили где-то в проекте функцию Main ().
Когда вы соберете новый проект, то получите . DLL-файл, а не . ЕХЕ-файл.
Создание классов для библиотеки
После того как вы сформировали проект библиотеки классов, вы создаете классы, составляющие эту библиотеку. Приведенный далее пример ClassLibrary демонстрирует простую библиотеку классов, которую вы сможете увидеть в действии (в примере показан как исходный текст библио теки, так и описываемого далее драйвера).
//ClassLibrary - простая библиотека классов и ее
//программа-драйвер
//Файл ClassLibrary.cs
using System;
namespace ClassLibrary
{
public class MyLibrary
{
public void LibraryFunctionl()
{
Console.WriteLine("Это LibraryFunctionl()");
}
public int LibraryFunction2(int input)
{
Console.Write("Это LibraryFunction2(), " + "возвращает { о } , input);
return input; // Возвращает аргумент
}
}
//Драйвер — в отдельном проекте
//Файл Program.cs
using System;
Глава 19. Работа с файлами и библиотеками |
431 |
using ClassLibrary; // Вам надо использовать эту библиотеку / / в программе
namespace ClassLibraryDriver
{
class Program
{
static void Main(string[] args)
{
//Создание объекта библиотеки и использование его
//методов
MyLibrary ml = new MyLibrary(); ml.LibraryFunctionl();
//Вызов статической функции посредством класса int nResult = MyLibrary.LibraryFunction2(27); Console . WriteLine (nResu-lt. ToString () ) ;
//Ожидаем подтверждения пользователя Console.WriteLine("Нажмите <Enter> для " +
"завершения программы.. . ") ;
Console.Read();
}
}
}
Вывод тестовой программы-драйвера, описанной в следующем разделе, выглядит следующим образом:
Это LibraryFunctionl()
Это LibraryFunction2(), возвращает 27 27
Нажмите <Enter> для завершения программы...
Библиотеки зачастую предоставляют только статические функции. В этом случае не нужно инстанцировать библиотечный объект — можно просто вызвать функцию по средством класса.
Создание проекта драйвера
Сама по себе библиотека классов не делает ничего, так что вам нужна программадрайвер, небольшая выполнимая программа, которая тестирует библиотеку в процессе разработки путем вызова ее методов. Для создания драйвера для проекта ClassLi brary выполните следующие действия.
1.Щелкните правой кнопкой мыши на имени решения в окне Solution Ex plorer проекта ClassLibrary и выберите Add : New Project.
Тем самым вы добавите проект в то же решение, в котором находится тестируе мая библиотека классов.
2.В диалоговом окне New Project выберите Console Application и назовите его ClassLibraryDriver или как вам заблагорассудится.
3.Находясь в диалоговом окне New Project, укажите местоположение про екта, щелкнув на кнопке Browse рядом с полем Location. Перейдите в папку, в которой вы хотите хранить проект драйвера, и щелкните на кнопке Open.
432 |
Часть VII. Дополнительные главы |