Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Books / 3_C#_2005_для_чайников_(Дэвис-2008)

.pdf
Скачиваний:
86
Добавлен:
24.03.2015
Размер:
15.46 Mб
Скачать

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. Дополнительные главы