
Объектно-ориентированное программирование.-6
.pdf
{
int i = 0; #pragma warning disable 0429
if (false && (i++) > 100) i = 0; #pragma warning restore
return 0;
}
}
Данные опции для всего проекта можно изменить также из командной строки компилятора:
csc.exe /warn:<уровень> ...
csc.exe /nowarn:<номер1>[,<номер2>...] ...
Пример: Samples\3.6\3_6_5_warn.
241

4. КЛАССЫ И ИНТЕРФЕЙСЫ
Классы – сердце каждого объектно-ориентированного языка. Класс представляет собой инкапсуляцию данных и методов их обработки. В том, что касается классов и многих функций языка, C# кое-что заимствует из C++, но также привносит и кое-что новое.
Также мы рассмотрим делегаты и интерфейсы. Но сначала поговорим о пространствах имен, включающих в себя описания других объектов.
§ 4.1. Пространства имен
Программы на языке C# организованы с помощью пространств имен. Пространства имен используются как в качестве «внутренней» системы организации для программы, так и в качестве «внешней» системы организации
–способа представления программных элементов другим программам.
Вцелом, структура исходного файла .CS может быть следующей:
<исходный файл> :: [<директивы объявлений>] [<внешние псевдонимы>]
[<директивы использования>] [<глобальные атрибуты>] [<члены пространства имен>]
<члены пространства имен> :: <член пространства имен> [...]
<член пространства имен> :: <пространство имен> <член пространства имен> :: [<атрибуты>] [<модификатор>] <перечисление>
<член пространства имен> :: [<атрибуты>] [<модификатор>] <структура> <член пространства имен> :: [<атрибуты>] [<модификатор>] <класс> <член пространства имен> :: [<атрибуты>] [<модификатор>] <делегат> <член пространства имен> :: [<атрибуты>] [<модификатор>] <интерфейс>
<модификатор> :: internal <модификатор> :: public
Итак, в исходном файле могут быть описаны:
1.Директивы объявлений (§ 3.6);
2.Внешние псевдонимы (п. 4.1.3);
3.Директивы использования (п. 4.1.2);
4.Глобальные атрибуты (§ 5.4);
5.Члены пространства имен – вложенные пространства имен, перечисления и структуры (§ 3.1), классы (§ 4.2), делегаты (§ 4.8), а также интерфей-
сы (§ 4.9).
Для членов пространства имен доступны два модификатора – internal
242

(член доступен только в пределах текущей программы) и public (неограниченный доступ). По умолчанию используется модификатор internal. Модификаторы доступа, разрешенные в других конструкциях программы, рассмотрены в § 4.2. Атрибуты рассмотрим в § 5.4.
4.1.1. Описание пространства имен
Синтаксис описания пространства имен в языке C#:
<пространство имен> :: namespace <имя пространства имен>
"{"
[<члены пространства имен>]
"}"
<имя пространства имен> :: <идентификатор> <имя пространства имен> :: <идентификатор>.<имя пространства имен>
Как мы видим, он соответствует синтаксису языка C++, только имя пространства может состоять из нескольких идентификаторов, разделенных точкой. Запись
namespace <имя1>.<имя2>
{
// члены
}
соответствует записи
namespace <имя1>
{
namespace <имя2>
{
// члены
}
}
Полным именем члена пространства имен будет
<имя пространства имен>.<имя члена>
Пространство с одинаковым именем может объявляться несколько раз. В этом случае все члены, описанные во всех объявлениях пространства, считаются членом одного объединенного пространства имен. Элементы вложенного пространства имен нельзя использовать как элементы вмещающего пространства. Имя вложенного пространства при этом должно быть обязательно указано. А пространства с одинаковыми именами, но находящиеся в разных ветвях иерархии, не считаются одним целым. Пример:
namespace Test1
{
243

struct S1 { } namespace Test2
{
struct S2 { }
}
}
namespace Test2.Inner
{
struct S3 { }
}
namespace Test2.Outer
{
struct S4 { }
}
namespace Test1
{
struct S5 { }
}
namespace Test2
{
struct S6 { }
public struct S7 { } internal struct S8 { }
private struct S9 { } // Ошибка
}
namespace NamespaceSample
{
class Program
{
static int Main()
{
Test1.S1 s1; Test1.Test2.S2 s2; Test2.Inner.S3 s3; Test2.Outer.S4 s4; Test1.S5 s5; Test2.S6 s6;
Test2.S2 ss2; // Ошибка Test2.S3 ss3; // Ошибка return 0;
}
}
}
Первая ошибка связана с тем, что используется неразрешенный модификатор доступа. Вторая – с тем, что структура S2 объявлена в пространстве имен Test1.Test2, а не Test2. Третья – структура S3 объявлена в пространстве имен Test2.Inner, а не Test2.
Пространства имен следует использовать для улучшения структуры программы. Все классы, близкие по функциональности, помещаются в от-
244

дельное пространство имен. В свою очередь, пространства имен образуют иерархию, отражающую классификацию содержащихся в них элементов.
Пример: Samples\4.1\4_1_1_namespace.
4.1.2. Директивы использования
Для работы с элементами пространства имен предназначена директива использования using. Причем в языке C++ мы можем использовать как пространство имен целиком, так и отдельные его элементы, например:
namespace X
{
typedef int Type1; namespace Y
{
typedef int Type2;
}
}
void main(void)
{
{
Type1 z1; // Ошибка – Type2 не описан X::Type1 z2; // ОК
X::Y::Type2 z3; // ОК
}
{
using namespace Y; // Ошибка – неизвестное пространство Y Type2 z; // Ошибка – Type2 не описан
}
{
using namespace X; // Используем все элементы X
Type1 z1; // ОК
Type2 z2; // Ошибка – вложенные пространства не используются
Y::Type2 z3; // ОК
}
{
using namespace X::Y; // Используем все элементы X::Y Type1 z1; // Ошибка – мы использовали только Y, но не X Type2 z2; // ОК
X::Type1 z3; // ОК
}
{
using namespace X; // Используем все элементы X using namespace Y; // Используем все элементы Y Type1 z1; // ОК
Type2 z2; // ОК
}
{
using X::Type1; // Используем только X::Type1
using Y::Type2; // Ошибка – неизвестное пространство Y using; // Используем только X::Y::Type2
Type1 z1; // ОК Type2 z2; // ОК
}
245

}
Примерно те же правила используются и в языке C#. Использовать можно как пространство имен целиком, так и отдельные его элементы (задавая псевдонимы). Использование вложенных пространств имен нужно указывать явно, т.к. рекурсивное включение не поддерживается.
Пример: Samples\4.1\4_1_2_using.
4.1.2.1. Директива импортирования
Синтаксис использования директивы импортирования:
using <имя пространства имен>;
В отличие от языка C++, ключевое слово namespace не указывается, а все директивы импортирования должны быть описаны до членов пространства имен. Такой синтаксис позволяет подключать только пространства имен целиком. После этого можно использовать имена членов пространства, не указывая предварительно имя пространства.
Рассмотрим варианты использования двух интерфейсов – IList из про-
странства имен System.Collections и IList<T> из System.Collections.Generic (см. п. 4.9.4). Пример 1:
using System;
namespace UsingSample
{
class Program
{
static int Main()
{
IList<int> list1 = null; // Ошибка Collections.IList list2 = null; // Ошибка System.Collections.Generic.IList<int> list3 = null; System.Collections.IList list4 = null;
return 0;
}
}
}
Итак, директивы импортирования не являются рекурсивными. Хотя мы используем пространство имен System, это не означает использование вло-
женных пространств System.Collections и System.Collections.Generic. С этим связана первая ошибка. Вторая ошибка не проявилась бы в языке C++, т.к. в нем можно использовать относительные имена пространств (см. в примере
246

выше – указав директиву использования X, мы можем использовать элементы пространства Y, указывая Y::<имя элемента>). В языке C# имя пространства либо не указывается вообще, либо указывается полностью.
Пример 2:
namespace UsingSample2
{
using System.Collections;
class MyClass2
{
static void F2()
{
IList<int> list1 = null; // Ошибка IList list2 = null; // ОК
System.Collections.Generic.IList<int> list3 = null; System.Collections.IList list4 = null;
}
}
}
Теперь компилятор считает, что IList нельзя использовать как универсальный тип. В используемом пространстве имен System.Collections интерфейс IList описан, но его универсальная версия описана в пространстве System.Collections.Generic, для которого директива использования не приведена. Полные имена интерфейсов можно использовать в обоих случаях.
4.1.2.2. Внутренние псевдонимы
Синтаксис описания псевдонима:
<внутренний псевдоним> ::
using <идентификатор> = <имя пространства имен>;
using <идентификатор> = <имя пространства имен>.<имя члена>;
После чего вместо имени пространства или члена пространства можно использовать указанный идентификатор. Пример:
namespace UsingSample3
{
using Coll = System.Collections;
using List = System.Collections.Generic.IList<int>;
class MyClass3
{
static void F3()
{
List list1 = null; Coll.IList list2 = null;
Coll.Generic.IList<int> list3 = null;
}
}
247

}
Теперь для обозначения интерфейса IList можно использовать запись
Coll.IList, а для IList<int> – псевдоним List или запись Coll.Generic.IList<int>.
Для доступа к элементам псевдонима можно также использовать оператор
«::»:
Coll::IList list4 = null;
4.1.3. Ссылки на сборки
Сборка – это основной строительный блок приложения .NET Framework. Все управляемые типы и ресурсы содержатся в сборках и помечаются либо как доступные только в пределах сборки (internal), либо как доступные из кода в других сборках (public). Сборки также играют ключевую роль в системе безопасности. Управление доступом для кода использует сведения о сборке для определения набора разрешений, предоставляемых коду в сборке.
4.1.3.1. Просмотр информации о сборках
Некоторые приемы работы со сборками мы рассмотрим в § 5.3. Посмотреть список сборок, подключенных к проекту, можно в обозревателе решений среды MS Visual Studio 2008 (рис. 4.1).
Рис. 4.1 – Ссылки на сборки
Не следует путать понятия сборки и пространства имен. Например, ссылка System – это не синоним пространства имен System, а пространства
248

имен System.Core вообще не существует. Сборки и пространства имен имеют отношение «многие ко многим». В одной сборке могут быть описаны объекты различных пространств, а объекты одного пространства могут находиться в разных сборках.
Чтобы посмотреть, какие объекты включены в сборку, выберите ее в списке ссылок, нажмите правую кнопку мыши и выберите пункт контекстного меню «Просмотр в обозревателе сборок» (рис. 4.2).
249

Рис. 4.2 – Обозреватель сборок
Попытка использовать объекты, не описанные в одной из подключенных сборок, приведет к ошибке компиляции «Не удалось найти имя типа или пространства имен "…" (пропущена директива using или ссылка на сборку?)». Пример:
System.Security.Cryptography.ProtectedData data = null; // Ошибка
Класс ProtectedData включен в сборку System.Security, которая по умолчанию к проекту не подключается. Если это сделать вручную (выбрав пункт меню «Проект» → «Добавить ссылку…» или посредством контекстного меню обозревателя решения), то ошибка исчезнет (рис. 4.3).
Рис. 4.3 – Добавление ссылки на сборку
Интересно, что класс Console не входит в сборку System. Поиск показывает, что он определен в сборке «mscorlib». Это базовая сборка, в которой определены классы базовых типов данных и некоторые другие основные классы.
Также можно добавлять ссылки на сборки, используя командную стро-
250