Вторник, 23.04.2024, 12:06
Приветствую Вас Гость | RSS

Лекции

Меню сайта
Форма входа
Категории раздела
ТАУ (Теория автоматического управления) [31]
лекции по ТАУ
Экология [151]
учебник
Бухгалтерский учет и налогообложение в строительстве [56]
Дементьев А.Ю. - Практическое пособие
Психология [104]
Пип
информатика [80]
с# Карли Ватсон
современные стулья [0]
новинки
Поиск

Главная » 2010 » Февраль » 11 » Определение классов
00:22
Определение классов
Определение классов
В предыдущей главе вы познакомились с возможностями объектно-ориентиро-
ванного программирования (ООП). В этой главе мы перейдем от теории к практи-
ке и рассмотрим определение классов в С#.
Для начала мы не будем заходить столь далеко, чтобы определять члены клас-
сов, а сконцентрируем свое внимание на определении самих классов. Может пока-
заться, что мы чересчур ограничиваем тему, но не волнуйтесь: в этой главе
содержится достаточное количество материала, который вам необходимо усвоить.
В первую очередь мы рассмотрим основной синтаксис, применяемый при опи-
сании классов, ключевые слова, которые могут использоваться для определения
режима доступа к классу и для других целей, а также способы задания наследова-
ния. Мы также разберем определение интерфейсов, поскольку оно во многих ас-
пектах сходно с описанием классов.
Остальная часть главы будет посвящена изучению различных тем, могущих
оказаться полезными при определении классов в С#, среди которых:
• Класс System.Object
• Полезные инструменты, предоставляемые Visual Studio.NET (VS)
• Библиотеки классов
„ • Сравнение интерфейсов и абстрактных классов '
• Структурные типы
• Копирование объектов
Итак, мы начнем с того, что подробно ознакомимся с основами.
Определение классов в С#
В языке С# для описания классов используется ключевое слово class. При
этом необходимо задействовать следующую основную конструкцию:
class myClass
' • Ш-^ - • • • -: • :' >• •'':; ' • й Ш й й Ш Ш Ш ";:^- & Ш
Ш:Ш • •• ' • :' • • •• • ' ••••''••••• • ' • ' • ' < - у .
Этот код определяет класс с именем myClass. После того как мы определили
класс, мы вольны создавать экземпляры этого класса в нашем проекте везде, где
имеется доступ к этому определению. По умолчанию классы определяются как
Определение классов
internal (внутренние), что означает, что доступ к ним будет иметь только код теку-
щего проекта. Мы можем указать это явно, воспользовавшись ключевым словом
определения доступа internal (хотя и не обязаны этого делать):
internal class MyClass
// члены класса
При желании можно указать, что класс является общим и должен быть доступен
из других проектов. Для этого используется ключевое слово public:
public class MyClass
{ ' :- ' ••"••••••••••••-••••••• ' ••••••-• *
// члены класса
} i
Обратите внимание, что классы, которые объявляются
самостоятельно, не могут быть частными или защищенными.
Соответствующие модификаторы — private и protected —
можно использовать только для описания классов, являющихся
членами других классов. Их мы рассмотрим в следующей главе.
Кроме ключевых слов этих двух модификаторов доступа, для описания класса
можно использовать ключевое слово abstract (абстрактный; создавать экземпля-
ры такого класса запрещено, он может только наследоваться, в нем допускается
наличие абстрактных членов) или sealed (изолированный; такой класс не может
наследоваться). Эти ключевые слова являются взаимоисключающими. Таким об-
разом, абстрактный класс должен определяться следующим образом:
public abstract s&ass MyClass
// члены класса, среди которых могут быть абстрактные
В данном случае MyClass является общим абстрактным классом, хотя также
возможны и внутренние абстрактные классы.
Изолированные классы определяются следующим образом:
public sealed class MyClass
// члены класса
Так же, как абстрактные классы, изолированные классы могут быть общими
или внутренними.
В определении класса может также задаваться и наследование. Для этого сле-
дует после имени класса поместить двоеточие, за которым должно следовать имя
базового класса. Например:
public class MyClass : MyBase
// члены класса
Заметьте, что в определении класса в С# допускается наличие только одного
базового класса, и если данный класс наследуется от некоторого абстрактного
класса, то в нем должны быть реализованы все наследуемые абстрактные члены
(если, конечно, этот класс сам не является абстрактным).
182 Глава 9
Компилятор не допустит ситуации, при которой производный класс будет более
доступным, чем его базовый класс. Отсюда следует, что внутренний класс может
наследоваться от общего базового класса, но общий класс не может наследоваться
от внутреннего базового класса. Следующий код является допустимым:
public class MyBase
{
// члены класса
}
internal class MyClass : MyBase
{
// члены класса
. }
А такой код не пройдет компиляцию:
internal class MyBase
{
// члены класса
}
public class MyClass : MyBase
{
// члены класса
}
Если базовый класс не задан, то класс наследуется только от базового класса
System.Object (ДЛЯ КОТОРОГО В С # ИСПОЛЬЗуется СИНОНИМ object). System.Object
безусловно является корневым в иерархии наследования для всех классов. Мы
будем изучать этот фундаментальный класс ниже.
Помимо указания базовых классов, мы можем таким же способом — через
двоеточие — указать и поддерживаемые интерфейсы. Если одновременно требует-
ся задавать базовый класс, то он должен располагаться первым после двоеточия,
а интерфейсы — за ним. Если базовый класс не задается, то интерфейсы указыва-
ются непосредственно за двоеточием. Для отделения имени базового класса (если
таковое присутствует) и для отделения одних имен интерфейсов от других следует
использовать запятую.
Например, мы можем добавить в класс MyClass интерфейс:
public class MyClass : IMylnterface
{
// члены класса
}
Все члены, являющиеся интерфейсами, должны иметь реализацию в любом
классе, поддерживающем данный интерфейс, хотя, конечно, всегда существует воз-
можность реализовать "пустой" интерфейс (без какого бы то ни было функциональ-
ного кода), если от данного члена не требуется выполнения каких-либо действий.
Следующее объявление неверно, поскольку базовый класс MyBase не является
первым вхождением списка наследования:
public class MyClass : IMylnterface, MyBase
{
// члены класса
}
Правильный способ задать и базовый класс, и интерфейс следующий:
public class MyClass : MyBase, IMylnterface
{
// члены класса
Определение классов 183
Не забывайте, что допускается наличие нескольких интерфейсов, поэтому сле-
дующий код также является допустимым:
public class MyClass : MyBase, IMylnterface, IMySecondlnterface
{
// члены класса
}
Для того чтобы вы смогли быстро освежить свою память, ниже приводится
таблица допустимых в определениях классов комбинаций модификаторов доступа:
Модификатор Значение
Отсутствует либо internal Класс доступен только в рамках текущего проекта
public Класс доступен отовсюду
abstract или internal abstract Класс доступен только в рамках текущего проекта, не допускает
создания экземпляров, может только наследоваться
public abstract Класс доступен отовсюду, не допускает создания экземпляров,
может только наследоваться
sealed или internal sealed Класс доступен только в рамках текущего проекта, не может
наследоваться, допускает только создание экземпляров
public sealed Класс доступен отовсюду, не может наследоваться, допускает
только создание экземпляров
Определение интерфейсов
Интерфейсы объявляются тем же способом, что и классы, только вместо клю-
чевого слова class используется ключевое слово interface. Например:
interface IMylnterface
{
// члены интерфейса
•; • •.-..•у::::-:•':::. •: ' ; . - ' : : i : / } • ' • . ' • < • • . ^ Щ : - ^ Ш : ' : ' ' Ш : : . . . Щ ' Ш Ш Ш Щ - ' - : • ' • • > • ' • • :^'--.-.--.''--:- ' •'''•"• • . : , х ; : : \ / . . - - • • • : ; " :: . ; • • • • • • т ^ Ш Ш
Ключевые слова для модификации доступа public и internal используются
точно так же, поэтому для того, чтобы сделать интерфейс общедоступным, следует
использовать ключевое слово public:
public interface IMylnterface
{
// члены интерфейса
}
Для интерфейсов ключевые слова abstract и sealed использовать нельзя, так
как ни один модификатор для интерфейсов не имеет смысла (у них отсутствует
реализация, следовательно, для них не могут создаваться экземпляры в явном
виде).
Наследование для интерфейсов определяется аналогично наследованию для
классов. Основное отличие здесь в том, что мы можем использовать интерфейсы
с множественными базами, например:
public interface IMylnterface : IMyBaselnterface, IMyBaseInterface2
{
// члены интерфейса
184 Глава 9
Интерфейсы, как и классы, наследуются от System.object. Этот механизм до-
пускает полиморфизм интерфейсов. Однако, как отмечалось ранее, нельзя создать
экземпляр интерфейса таким же способом, как и экземпляр класса.
Давайте обратимся к некоторым примерам определения классов, которые при-
водятся вместе с кодом, в котором они используются.
Практикум: определение классов
1. Создайте новое консольное приложение с именем ch09Ex0i
В Директории C:\BegCSharp\Chapter9.
2. Измените код в ciassi.cs следующим образом:
namespace Ch09Ex01
{ ' «W
public abstract class MyBase
{
} : • • "i Щ •
internal class MyClass : MyBase
{
public interface IMyBaselnterface
internal interface IMyBaseInterface2
} : .'••••/ :.
internal interface IMyInterface : IMyBaselnterface, IMyBaseInterface2
{ :'
internal sealed class MyComplexClass : MyClass, IMylnterface
{
illill ШШШШШ-Ш..
II/ <summary>
/// Summary description for Glass 1.
/// </summary>
class Classl
{
static void Main(string [] args)
{
MyComplexCiass myObj = new MyComplexClass();
Console.WriteLine(myObj.ToString());
3. Выполните проект:
j
Ch09Ex@l.NyComplexClass
Ppess any hey to continue
J n j x;
Определение классов 185
System.Object
«interface»
IMyBaselnterface
A
«interface»
IMyBaselnterface2
«interface»
IMylnterface4-
Classi
-Main(in args: stringQ)
MyBase
MyClass
Как это работает
В данном проекте описаны
классы и интерфейсы, облада-
ющие иерархией наследования,
показанной на рисунке слева.
Мы включили сюда classi,
так как его описание аналогич-
но описанию остальных наших
классов, хотя он и не входит
в основную иерархию классов.
Метод Main (), находящийся в
этом классе, является точкой
входа в приложение; этот мо-
мент обсуждался раньше.
MyBase И IMyBaselnterface
определены как общие, поэ-
тому они доступны из других
проектов. Остальные классы
и интерфейсы являются внут-
ренними и доступны только
в рамках настоящего проекта.
В программе вызывается метод TostringO объекта myobj, который является
Экземпляром Класса MyComplexClass:
MyComplexClass myObj = new MyComplexClass();
Console.WriteLine(myObj .ToStringO ) ;
ЭТО ОДИН ИЗ Наследуемых ОТ System.Object методов (класс System.Object не
изображен на диаграмме просто для краткости). TostringO возвращает имя клас-
са данного объекта в виде строки.
MyComplexClass IMylnterface
System.Object
Поскольку все классы наследуются от System.Object, то они обладают досту-
пом ко всем его защищенным и общим членам. Поэтому имеет смысл ознакомить-
ся с тем, что нам доступно. System.Object содержит следующие методы:
Метод
Возвращае-
мый тип
Вир- Ста-
туаль- тиче-
ный ский Описание
Object()
-Object()
(также известен под
именем Finalize () —
см. следующий раздел)
Equals(object)
Отсутствует Нет Нет
Отсутствует Нет Нет
bool Да Нет
Конструктор типа System.Object.
Автоматически вызывается конструкторами
производных типов
Деструктор для типа System.Object.
Автоматически вызывается деструкторами
производных типов, сам по себе вызван
быть не может
Сравнивает объект, для которого
вызывается, с другим объектом
и возвращает значение true, если они
равны. Реализация, выполняющаяся
по умолчанию, проверяет, ссылается ли
186
Метод
Возвращае-
мый тип
Вир-
туаль-
ный
Ста-
тиче-
ский Описание
Глава 9
Продолжение таблицы
Equals(object)
(продолжение)
Equals(object, object) bool
ReferenceEquals(obj ect, bool
obj ect)
ToString() string
MemberwiseClone()
GetType()
GetHashCode()
переданный в качестве параметра объект
на тот же самый объект (поскольку объекты
представляют собой ссылочные типы).
Если необходимо сравнивать объекты
каким-либо иным образом, например,
на предмет одинакового значения,
этот метод может быть переопределен
Нет Да Сравнивает два объекта, передаваемых ему
в качестве параметров, на предмет того,
равны ли они. Эта проверка выполняется
с помощью метода Equals (object).
Заметьте, что, если оба объекта обладают
нулевыми ссылками, возвращается
значение true
Нет Да Сравнивает два переданных ему объекта,
определяя, являются ли они ссылками
на один и тот же экземпляр
Да Нет Возвращает строку, соответствующую
экземпляру объекта. По умолчанию
это квалифицированное имя класса
(см. предыдущий пример), однако метод
можно переопределить для выполнения
действий, подходящих для типа данного
класса
Нет Нет Копирует объект посредством создания
нового экземпляра объекта и копирования
всех членов. Обратите внимание, что
такое копирование членов не приводит
к созданию новых экземпляров этих членов.
Любые члены ссылочного типа в новом
объекте будут ссылаться на те же объекты,
на которые они ссылаются в исходном
классе. Рассматриваемый метод является
защищенным, поэтому его можно
использовать внутри класса или внутри
производных классов
System.туре Нет Нет Возвращает тип объекта в виде объекта
System.Type
int Да Нет Используется как функция хеширования
для объектов. Хеш-функция —
это функция, возвращающая значение,
которое позволяет идентифицировать
объект в некоторой сжатой форме
obj ect
Эти методы являются основными, они должны поддерживаться всеми типами
объектов в .NET Framework, хотя, возможно, некоторые из них вам никогда не
придется использовать (или только при определенных обстоятельствах, как, на-
пример, GetHashCode () ).
Метод GetType о полезен при использовании полиморфизма, поскольку он по-
зволяет выполнять разные коды для объектов разных типов, а не один и тот же код
для всех объектов, как это часто бывает. Например, если у нас имеется функция,
Определение классов 187
которой передается параметр типа object (это означает, что мы можем передавать
ей практически все, что угодно), то можно предусмотреть выполнение в ней спе-
циальных работ в случае поступления объектов конкретного типа. Воспользовав-
шись сочетанием GetiypeO с typeof о (оператор С#, который преобразовывает
имя класса в объект system.туре), мы получаем возможность выполнять сравне-
ния примерно следующим образом:
if (typeof(myObj) == typeof(MyComplexClass))
{
. ••//• Объект myObj является экземпляром класса MyComplexClass
Возвращаемый объект System.туре обладает гораздо более широкими возмож-
ностями, но здесь мы не будем на нем останавливаться. Отражение подробно опи-
сывается в главе 22.
Очень часто оказывается весьма полезным переопределить метод Tostring (),
особенно в тех ситуациях, когда содержимое объекта может быть с легкостью
представлено в виде одной удобочитаемой строки.
Эти методы System.object будут постоянно встречаться в последующих главах,
поэтому сейчас мы завершаем обсуждение; дополнительные детали будут объяс-
няться по мере необходимости.
Конструкторы и деструкторы
Когда мы описываем какой-либо класс в С#, то в большинстве случаев нет не-
обходимости описывать соответствующие ему конструкторы и деструкторы, посколь-
ку объект базового класса System.object обеспечивает их реализацию по умолчанию.
Однако иногда стоит описать их самостоятельно, что позволит инициализировать
и уничтожать объекты.
Для того чтобы добавить в класс простой конструктор, необходимо воспользо-
ваться следующим несложным синтаксисом:
class MyClass
{ ....... ............ , .. .... ^ ^ . _ ^ :. . .: ......
public MyClass ()
// Код конструктора
1В1
Такой конструктор обладает тем же именем, что и класс, в ко юром он содер-
жится, не имеет параметров (что превращает его в конструктор, использующийся
по умолчанию) и является общим, что позволяет создавать экземпляры объектов
данного класса (более подробная информация по этому вопросу содержится в пре-
дыдущей главе).
Существует также возможность использовать частный конструктор по умолча-
нию, что означает, что экземпляры объектов данного класса не могут создаваться
с помощью этого конструктора (еще раз отсылаем читателей к предыдущей главе):
class MyClass
. {
private MyClass ()
{ ^ • .
.// Код конструктора
188 Глава 9
Кроме того, у нас имеется возможность аналогичным образом определить кон-
структоры данного класса, использующиеся не по умолчанию; для этого достаточно
просто задать параметры. Например:
class MyClass
{ ... ..... ........ .......................... . ,.:. . . : ..:.. ...... .......................v.v
public MyClass ()
" •. • . -• ^"•:;;• v.-V:S.--:::-:;^;:-:• ••"^:'"=;::••• - : - ' - й ^ : " • ••:--:^• ; / J ^ X :':••''•. ::: ,'•.:• v j ¥ \ : ' : > ' . ! : ' • • X ^ X ' ' - - . . r X X i
XX 'Л,. • •:;.• •'•;'••[ .; v ^ •::.••;;' ХЁМ ХЩШ§.
public MyClass(int myInt)
: ; v - . ••;• {
// Код конструктора, использующегося не по
// умолчанию (используется параметр my Int}
•'•••••••••••• ) Ч : '. - ' ' \£'Ш
}
Количество задаваемых конструкторов не ограничено.
Деструкторы определяются с помощью несколько иного синтаксиса. Деструктор,
используемый в .NET (и предоставляемый классом system.object), называется
FinaiizeO, однако для объявления деструктора используется другое имя. Вместо
того чтобы переопределять FinaiizeO, мы используем следующий код:
class MyClass
{
-MyClass () •• , ;: • .
(
: . // тело деструктора :
Код, заключенный в деструкторе, будет выполняться при сборке мусора, позво-
ляя освобождать удерживаемые ресурсы. После вызова деструктора происходят
явные вызовы деструкторов базовых классов, включая вызов FinaiizeO в корне-
вом классе system.object. Такой способ позволяет .NET Framework гарантировать
выполнение, поскольку переопределение FinaiizeO означало бы, что необходимы
явные вызовы базовых классов, а это таит в себе потенциальную опасность (мы
познакомимся с вызовом методов базовых классов в следующей главе).
Последовательность выполнения конструкторов
Если нам требуется выполнить несколько действий в конструкторах класса, то
удобнее поместить весь этот код в одном месте, что дает те же преимущества, что
и разбиение кода на функции. Для этого можно использовать отдельный метод
(см. следующую главу), однако в С# имеется замечательная альтернативная воз-
можность. Каждый конструктор может быть настроен таким образом, что перед
выполнением собственного кода он будет вызывать некоторый другой конструктор.
Однако прежде чем перейти к рассмотрению этого вопроса, давайте поближе
познакомимся с тем, что по умолчанию происходит при создании нового экземпля-
ра класса.
Для того чтобы создать экземпляр производного класса, необходимо создать
экземпляр его базового класса. В свою очередь, чтобы создать экземпляр этого
базового класса, требуется создать экземпляр базового класса этого базового
класса — и так далее до System.object. В результате, какой бы конструктор ни
использовался для создания класса, system.object.object о всегда будет вызы-
ваться первым.
Определение классов 189
Если используется конструктор класса не по умолчанию, то в таком случае по
умолчанию будет использоваться конструктор базового класса, сигнатура которого
совпадает с сигнатурой данного конструктора. Если таковой обнаружить не удает-
ся, то используется конструктор базового класса по умолчанию (это происходит
всегда, за исключением корневого класса System.object, поскольку у него отсутст-
вуют конструкторы, использующиеся не по умолчанию). Давайте рассмотрим неко-
торый пример, который поможет проиллюстрировать последовательность событий.
Рассмотрим следующую иерархию объектов:
public class MyBaseClass
{ .
public MyBaseClass()
public MyBaseClass(int i)
public class MyDerivedClass : MyBaseClass
{ •
public MyDerivedClass{)
public MyDerivedClass(int i)
public MyDerivedClass(int i, int j)
{
}
} '/•• ' '
ЕСЛИ МЫ попытаемся создать экземпляр класса MyDerivedClass следующим об-
разом:
MyDerivedClass myObj = new MyDerivedClass() ;
то это приведет к такой последовательности событий:
• ВЫПОЛНИТСЯ КОНСТруКТОр System.Object.Object().
• ВЫПОЛНИТСЯ КОНСТруКТОр MyBaseClass.MyBaseClass().
• ВЫПОЛНИТСЯ КОНСТруКТОр MyDerivedClass.MyDerivedClass().
Если же мы попытаемся создать экземпляр класса таким образом:
MyDerivedClass myObj = new MyDerivedClass(4);
то соответствующая последовательность будет иметь следующий вид:
• ВЫПОЛНИТСЯ КОНСТруКТОр System.Object.Object().
• ВЫПОЛНИТСЯ КОНСТруКТОр MyBaseClass.MyBaseClass(int i).
• ВЫПОЛНИТСЯ КОНСТруКТОр MyDerivedClass.MyDerivedClass(int i).
Наконец, если мы воспользуемся следующим вариантом:
MyDerivedClass myObj = new MyDerivedClass(4, 8) ;
1Q0 ; - Глава 9
то произойдет вот что:
• ВЫПОЛНИТСЯ КОНСТруКТОр S y s t e m . O b j e c t . O b j e c t ( ) .
• ВЫПОЛНИТСЯ, КОНСТруКТОр M y B a s e C l a s s . M y B a s e C l a s s () .
Q ВЫПОЛНИТСЯ КОНСТруКТОр M y D e r i v e d C l a s s . M y D e r i v e d C l a s s ( i n t i, i n t j ) .
При таком подходе мы получаем возможность поместить код, ответственный за
обработку параметра int i, в MyBaseClass (int i ) , подразумевая, что конструктору
MyDerivedciass(int i, int j) достанется меньше работы — ему придется обраба-
тывать только параметр int j . (Подобные рассуждения строятся на основе предпо-
ложения, что параметр int i в обоих случаях имеет один и тот же смысл, что
может и не выполняться, хотя на практике при таком способе оформления обычно
выполняется.) С# при необходимости позволяет нам задать именно такой тип по-
ведения.
Для этого требуется просто указать конструктор базового класса в определении
конструктора нашего производного класса следующим образом:
public class MyDerivedClass : MyBaseClass
public MyDerivedClass (int i, int j) : base(i)
Ключевое слово base указывает .NET, что в процессе создания экземпляра сле-
дует использовать конструктор базового класса с сигнатурой, совпадающей с за-
данной. В данном случае мы задействуем единственный параметр int, поэтому
будет ВЫЗВаН MyBaseClass ( i n t i ) . КОНСТруКТОр MyBaseClass () не будет ВЫЗЫВать-
ся, т. е. последовательность событий будет такой же, как и в последнем приме-
ре,— что, собственно, в данном случае и требовалось.
Существует возможность с помощью этого же ключевого слова задавать лите-
ральные значения для конструкторов базового класса, допустим, применяя конст-
руктор класса MyDerivedClass, ИСПОЛЬЗУЮЩИЙСЯ ПО умолчанию, ДЛЯ ВЫЗОВа
конструктора класса MyBaseClass, использующегося не по умолчанию:
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass() : base(5)
В этом случае последовательность событий будет иметь такой вид:
• ВЫПОЛНИТСЯ КОНСТруКТОр System.Object.Object().
• ВЫПОЛНИТСЯ КОНСТруКТОр MyBaseClass.MyBaseClass (int i).
• ВЫПОЛНИТСЯ КОНСТруКТОр MyDerivedClass.MyDerivedClass() .
Кроме ключевого слова base, в этом контексте может использоваться еще одно
ключевое слово: this. Оно указывает процессу создания экземпляра в .NET на не-
обходимость использовать конструктор не по умолчанию для текущего класса,
прежде чем будет вызван указанный конструктор. Например:
Определение классов 191
public class MyDerivedClass : MyBaseClass
public MyDerivedClass () : this(5, 6)
public MyDerivedClass (int i, int j) : base(i)
Это приведет к такой последовательности событий:
• ВЫПОЛНИТСЯ КОНСТруКТОр System.Obj ect.Object().
• ВЫПОЛНИТСЯ КОНСТруКТОр MyBaseClass.MyBaseClass (int i).
• ВЫПОЛНИТСЯ КОНСТруКТОр MyDerivedClass. MyDerivedClass (int i, int j).
• ВЫПОЛНИТСЯ КОНСТруКТОр MyDerivedClass .MyDerivedClass ().
Единственным ограничением в данном случае является задание только одного
конструктора, использующего ключевые слова base или this. На самом деле, как
было продемонстрировано в предыдущем примере, это не такое уж серьезное
ограничение, поскольку все равно остается возможность конструирования чрезвы-
чайно изощренных последовательностей выполняемых действий.
Мы увидим этот способ в действии немного позже.
Инструменты ООП в Visual Studio.NET
Поскольку ООП является основополагающим понятием .NET Framework, то в VS
предусмотрены определенные инструменты, облегчающие разработку приложений
с использованием ООП. В этом разделе будут рассмотрены некоторые из них.
Окно Class View
Class View - СИ09Ек01
myBaselnterface
й myBaselnterfасе2
bg° mylnterface
Solution Explorer • .&$ Class View.
В главе 2 мы видели, что окно
Solution Explorer делит экранное
пространство с окном Class View
(окно просмотра классов, в котором
отображается иерархия классов
приложения. Кроме того, названное
окно позволяет моментально уз-
нать характеристики используемых
классов. Существует несколько ре-
жимов просмотра этой информации,
причем по умолчанию используется
режим Sort By Type (упорядочива-
ние по типу). Для проекта, кото-
рый был взят в качестве примера
в предшествующем разделе, это
окно будет иметь вид, подобный
представленному на рисунке слева.
192 Глава 9
Class View - €К0§ЕкО1
В окне Class View может использоваться большое количество разнообразных
символов, среди которых:
• |$1 — Проект
• {}• — Пространство имен
• Ш — Класс
• *о — Интерфейс
my8ase
^ j Bases end Me*faces
Ш *(f Object
В •*£ 8ases and Interfaces
Й-^Jmy
^ " Bases andMerfaces
Ш Object
8 ^ j f Bases and Interfaces
B-|j£ myClass
• I 8 ^ J Bases and Interfaces :
В ••^[f fftyB^ase
| В ^t 8^se« and Interfaces
S-^f Object
Й-^myInterface
E l ^ Bases and Interfaces
: : :.. . i i - ^ f Object :;
Щ
Bases and Interfaces
*$ Object
S"
8 - ^ вййий^ and Interfaces
; ш-^ object
B- *O myBaselncerface
j B-^j Bases and Interfaces ;
Ш ' ^ Object
i Й-^jJ Ш$е$ :and Interfaces, .-: •',:,:
: ft-^t Object
В gP mylnterface : ; ;-.:-'' ,-'
Й-^f Bases and Interfaces
Щ-^t Object
Й • *O myBaselnterface
| Й - 4 ^ Bases and Interfaces
I ^J Object
Bases and Interfaces
- ^ Object
Й Solution Explorer ^ class Vtew |
Q: /;ёф — Метод
• Иг* — Свойство
• # — Поле
• *Ш — Структура
• Ц| — Перечислимый тип
Q: ,JSJI — Элемент перечислимого типа
• 9 — Событие
Обратите внимание, что часть символов используется для
определения типов, не являющихся классами, например, для
перечислений или структур.
Некоторые из вхождений могут иметь по дополнитель-
ному символу, расположенному ниже и обозначающему
режим доступа к ним (у вхождений общего типа символ
отсутствует):
• Ш — Частный
• Ф — Защищенный
• ш — Внутренний
Для обозначения абстрактных, изолированных и виртуаль-
ных вхождений никаких символов не используется.
Все режимы работают одинаково, позволяя разворачи-
вать определения классов с использованием стандартных
средств управления просмотром дерева. Классы и интер-
фейсы раЗВОраЧИВаЮТСЯ ВПЛОТЬ ДО урОВНЯ System.Object
(см. рис. слева). Отсюда можно почерпнуть любую инфор-
мацию о классах проекта.
Помимо просмотра этой информации, в данном окне можно получить доступ
к коду большинства элементов. Двойной щелчок мышью по элементу или двойное
нажатие правой кнопки мыши и выбор пункта меню Go To Definition (переход
к определению) позволяет перейти непосредственно к тому коду, который описыва-
ет данный элемент, если этот код является доступным. В ином случае — например,
если он находится в каком-либо базовом типе, к которому нет доступа,— мы пе-
рейдем в окно Object Browser (браузер объектов).
• г {
$£&& ""^ ч- ft „ * Г"
'••*}• «2»««drib • ': . ••/.'••' ••:• •• ;
• . . . •' • ••••:,••• . . ' . . • .• • -I & X
. : •:•:•.-. : • : • :
;
Г :' • •. •:• : ' ' .• •' ' • ' -у у-'- у :-:'-y * |
Определение классов 193
Окно Object Browser
Окно Object Browser — это расширенная версия окна Class View, которая по-
зволяет просматривать другие классы, доступные нашему проекту, и даже совер-
шенно посторонние классы. Переход в это окно осуществляется либо автоматически
(например, в ситуациях, подобных описанной в предшествующем разделе), либо
вручную через View | Other Windows | Object
Browser. Это окно появляется в основном
окне и позволяет осуществлять просмотр
так же, как и в окне Class View.
В окне Object Browser (см. рис. слева),
в отличие от окна Class View, классы и
члены классов изображаются в различных
местах, при этом в их число включаются
все модули .NET, на которые ссылается
данный проект. Здесь можно просматривать,
например, вхождения пространства имен
System.
После того как некоторый элемент выбран, в поле внизу отображается инфор-
мация о нем. В данном случае в ней содержится уровень доступа, базовый класс
и пространство имен для класса Classl. Эту информацию также можно использо-
вать для поиска: щелчок мышью, например, на System.object приведет к выводу
информации, относящейся к данному классу. Кроме того, здесь можно просмот-
реть некоторую обобщенную информацию. Она генерируется с использованием
документирующих XML-комментариев, включенных в код:
•••.•• ••:...:. • /' • • / / / < S U m i T i a r y > . •. .•.•,•.• • . • . . : • , . . • , . , : . . • . . . . , ; . . • , . ; • : . • . . • ., \ , • ,'•
/// Краткое описание класса Classl.
/// </summary>
class Classl
С
static void Main(string[] args)
MyComplexClass myObj = new MyComplexClass() ;
Console.WriteLine(myObj.ToString());
}
}
Более подробно документирующие XML-комментарии будут рассматриваться
в главе 18.
Добавление классов
В состав VS входят инструменты, которые позволяют ускорять выполнение не-
которых наиболее распространенных задач, и некоторые из них применимы для
ООП. Один из таких инструментов позволяет быстро добавлять в проект новые
классы при минимуме информации, которую необходимо набирать.
Доступ к этому инструменту можно получить либо через пункт меню File | Add
New Item..., либо с помощью двойного щелчка мышью на имени проекта в окне
Solution Explorer и выбора подходящего элемента. В обоих вариантах появляется
диалоговое окно, которое позволяет выбрать тип элемента, который требуется
добавить. Для того чтобы добавить класс, необходимо выбрать вхождение Class
в окне, расположенном справа, ввести имя файла, в котором будет содержаться
194 Глава 9
этот класс, и затем нажать Open. В результа-
те создаваемому классу будет присвоено имя
в соответствии с именем, выбранным для
файла (см. рис. слева).
В примере, приводившемся в этой главе,
мы добавляли описания классов в файл
ciassi.cs вручную. Однако часто хранение
различных классов в разных файлах позво-
ляет упростить контроль за ними.
Когда мы откроем проект ch09Ex0i, мы
УВИДИМ, ЧТО В файле MyNewClass.cs ПОЯВИЛСЯ
следующий код:
using System;
namespace Ch09Ex01
{
/// <summary>
/// </summary>
public class MyNewClass
{
public MyNewClass{)
{
//
// Следует сделать: вставить сюда логику конструктора
Данный класс — MyNewClass — описывается в том же пространстве имен, что
и наш класс — ciassi, являющийся точкой входа, поэтому мы можем использовать
его из программы так же, как если бы он был определен в том же самом файле.
Как можно увидеть из кода (или, точнее, из находящихся в коде комментариев),
созданный класс содержит конструктор по умолчанию.
Проекты, представляющие собой библиотеки классов
Классы можно размещать не только в отдельных файлах в рамках одного про-
екта, но и в совершенно независимых проектах. Проект, в котором не содержится
ничего, кроме классов (вместе с описаниями других относящихся к делу типов, но
без точки входа), называется библиотекой классов.
Проекты, представляющие собой библиотеки классов, компилируются в модули
.dll, а доступ к их содержимому можно получить, включая в другие проекты ссыл-
ки на них (которые могут быть, хотя и не обязательно, частью того же самого ре-
шения). Такой подход позволяет увеличить степень инкапсуляции, обеспечиваемой
объектами, поскольку процесс изменения и совершенствования библиотек классов
может происходить, абсолютно не затрагивая те проекты, которые используют
данные библиотеки. Это позволяет с легкостью совершенствовать услуги, предо-
ставляемые различными классами (что может влиять сразу на несколько различ-
ных приложений, использующих эти классы).
Определение классов 195
Давайте рассмотрим пример проектов, один из которых представляет собой
библиотеку классов, а другой использует классы, которые содержатся в этой биб-
лиотеке.
Практикум: использование библиотеки классов
•'JT-' -:ts
Cj Vj*u4 С * Projects
LJ Setu? 4fttf Oof^yment Projects
':•} L i Other Pt&^sto
Cl Visual Studio Solutions
JA project for с
Class Ub?u*y
:
ASP.&ET Web A5P.NET Web
en Service
Zl
f Kdd to SdtJuon <• СШ ЭФЛю»
<ж и
1. Создайте новый проект с именем
ChO9ClassLib В Директории
С:\BegCSharp\Chapter9,
воспользовавшись диалоговым
окном New Project (см. рис. слева).
2. Переименуйте файл ciassi.cs
В MyExternalClass.cs (ЭТО МОЖНО
сделать, щелкнув правой кнопкой
мыши по имени файла в окне
Solution Explorer и выбрав опцию
Rename (переименовать)).
3. Измените КОД В MyExternalClass.cs
так, чтобы он отражал данное
изменение имени класса.
Для этого запишем код так:
public class MyExternalClass
public MyExternalClass()
// Следует сделать: вставить сюда логику конструктора
4. Добавьте в проект новый класс, использовав в качестве имени файла
MylnternalClass.cs.
5. Модифицируйте КОД таким Образом, ЧТОбы КЛаСС MylnternalClass
стал внутренним:
internal class MylnternalClass
public MylnternalClass()
// Следует сделать: вставить сюда логику конструктора
6. Откомпилируйте проект (обратите внимание, что в этом проекте
не имеется точки входа, поэтому его невозможно запустить как
обычный проект — вместо этого вы можете просто построить его,
воспользовавшись опцией меню Build | Build).
7. Создайте новое консольное приложение с именем chO9ExO2
В директории C:\BegCSharp\Chapter9.
196 Глава 9
Solution Explorer
\ В -
Solution 'ChO9ExG2' (I project)
j- «~ •© System
'{•— *Ш System,Data L- *© System^Mt
@ Assemblylnfo.es
gj Classi.es :
i Class View
12. Откройте окно Object
Browser и посмотрите,
какие объекты
содержатся
в новой ссылке
(см. рис. справа).
13. Внесите следующие
изменения в ciassi.es:
using System;
using ChO9ClassLib;
namespace ChO9ExO2
8. Выберите пункт меню Project | Add Reference... либо
выберите ту же самую опцию, дважды щелкнув
правой кнопкой мыши по пункту References
в окне Solution Explorer.
9. Щелкните по кнопке Browse..., перейдите в директорию
C:\BegCSharp\Chapter9\ChO9ClassLib\bin\Debug\
И ДВаЖДЫ ЩеЛКНИТе МЫШЬЮ ПО ChO9ClassLib.dll.
10. Нажмите ОК.
11. После того как выполнение операции будет завершено,
убедитесь, что в окне Solution Explorer добавилась
новая ссылка (см. рис. слева).
.*• &*л - - . - -'-. - ;>-.> ~.> , -
1 •
.•
/// <summary>
/// Краткое описание класса Classl.
/// </summary>
class Classl
static void Main(string[] args)
MyExternalClass myObj = new MyExternalClass();
Console.WriteLine(myObj.ToString());
14. Запустите приложение:
Chf39ClassLib.l1i|ExteraalClasi
Press an51 key to continue
И
Как это работает
В этом практикуме мы создали два проекта, один из которых является библиоте-
кой классов, а второй — консольным приложением. Проект, представляющий собой
библиотеку КЛаССОВ,— ChO9ClassLib — ВКЛЮЧаеТ ДВа класса: MyExternalClass,
Определение классов 197
ЯВЛЯЮЩИЙСЯ общеДОСТуПНЫМ, И MylnternalClass, К КОТОрОМу ИМееТСЯ ТОЛЬКО ВНуТ-
ренний доступ. Консольное приложение — chO9ExO2 — содержит простой код, ис-
пользующий библиотеку классов.
ДЛЯ ТОГО ЧТОбы ИСПОЛЬЗОВаТЬ КЛаССЫ ИЗ ChO9ClassLib, МЫ ДОбаВИЛИ В Приложе-
ние ChO9ExO2 ссылку на DLL, которая была создана проектом библиотеки классов.
Для этого мы просто указали соответствующую DLL. Для задач, решаемых в дан-
ном примере, оказалось достаточно указать на выходной файл библиотеки классов,
однако с тем же успехом мы могли бы скопировать этот файл в такое место, где
он был бы локальным по отношению к chO9ExO2. Это позволило бы продолжать
разработку библиотеки классов, не затрагивая консольное приложение. Для того
чтобы заменить старую версию модуля на новую, потребуется записать вновь со-
зданную DLL на место старой.
После добавления ссылки мы получили возможность просмотреть перечень
доступных классов с помощью Object Browser. Поскольку один из двух классов —
MylnternalClass — является внутренним, то он не виден: этот класс недоступен
внешним проектам. Напротив, класс MyExtemaiciass доступен, и мы используем
его в консольном приложении.
Мы можем попробовать заменить код в консольном приложении на код, кото-
рый попытается использовать внутренний класс следующим образом:
static void Main (string [] args)
{
MylnternalClass myObj = new MylnternalClass();
Console.WriteLine(myObj .ToStringO );
}
При попытке откомпилировать такой код будет получено следующее сообщение
об ошибке:
C:\BegCSharp\Chapter9\ChO9ExO2\Class1.cs(13): <Ch09ClassLib.MylnternalClass>
(C:\BegCSharp\Chapter9\ChO9ExO2\Classl.cs( 13): класс 'ChO9ClassLib.MyInternalClass'
is inaccessible due to its protection level
недоступен из-за уровня его защиты)
Этот способ использования классов из внешних модулей является ключевым
при программировании на С# в .NET Framework. Фактически так мы поступаем со
всеми классами в .NET Framework, поскольку работа с ними организована анало-
гичным образом.
Сравнение интерфейсов и абстрактных классов
В этой главе мы познакомились с тем, каким образом можно создавать и ин-
терфейсы, и абстрактные классы (пока не содержащие членов — к рассмотрению
этого вопроса мы перейдем в следующей главе). Эти два типа в некотором смысле
аналогичны, и имеет смысл рассмотреть те ситуации, в которых может потребо-
ваться использование одних или других. Итак, сначала о сходстве.
Как в абстрактных классах, так и в интерфейсах допускается наличие членов,
которые могут наследоваться производными классами. Ни абстрактные классы, ни
интерфейсы не допускают непосредственного создания экземпляров, хотя мы мо-
жем объявлять переменные соответствующих типов. В этом случае можно исполь-
зовать полиморфизм при присваивании объектов, которые наследуются от этих
типов, переменным таких типов. В обоих случаях мы получаем возможность ис-
пользовать члены этих типов с помощью этих переменных, хотя и не имеем непо-
средственного доступа к другим членам производного класса.
198 Глава 9
i
PassengerTrain
Теперь о различиях.
Производные классы могут наследоваться только от одного базового класса,
что означает, что непосредственно наследоваться может только один абстрактный
класс (хотя вся цепочка наследования может включать в себя несколько абстракт-
ных классов). Напротив, классы могут использовать произвольное количество ин-
терфейсов. Отличие, однако, не столь уж существенно: результаты в обоих случаях
приблизительно одинаковы. При использовании интерфейсов все оказывается не-
сколько иначе.
У абстрактных классов могут быть как абстрактные члены (у которых отсу
Категория: информатика | Просмотров: 1537 | Добавил: basic | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *:
Календарь
«  Февраль 2010  »
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

krutoto.ucoz.ru
Бесплатный конструктор сайтов - uCoz