Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Основные положения о классах

Поиск

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

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

Физическое представление класса появится в оперативной памяти лишь после того, как будет создан объект этого класса.

6.1.1 Общая форма определения класса

При определении класса объявляются данные, которые он содержит, а также код, оперирующий этими данными. Если самые простые классы могут содержать только код или только данные, то большинство настоящих классов содержит и то и другое.

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

Класс создается с помощью ключевого слова class. Ниже приведена общая форма определения простого класса, содержащая только переменные экземпляра и методы.

classимя_класса

{

// Объявление переменных экземпляра.

доступ тип переменная1;

доступ тип переменная2;

// ...

доступ тип переменнаяN;

 

// Объявление методов.

доступ возращаемый_тип метод1(параметры)

{

// тело метода

}

доступ возращаемый_тип метод2 (параметры)

{

// тело метода

}

//...

 

доступ возращаемый_тип методN(параметры)

{

// тело метода

}

}

Обратите внимание на то, что перед каждым объявлением переменной и метода указывается доступ. Это спецификатор доступа, например public, определяющий порядок доступа к данному члену класса. Как упоминалось ранее, члены класса мо­гут быть как закрытыми (private) в пределах класса, так открытыми (public), т.е. бо­лее доступными. Спецификатор доступа определяет тип разрешенного доступа. Ука­зывать спецификатор доступа не обязательно, но если он отсутствует, то объявляемый член считается закрытым в пределах класса. Члены с закрытым доступом могут ис­пользоваться только другими членами их класса. В примерах программ, приведенных в этой главе, все члены, за исключением метода Main(), обозначаются как открытые (public). Это означает, что их можно использовать во всех остальных фрагментах кода - даже в тех, что определены за пределами класса. Мы еще вернемся к обсужде­нию спецификаторов доступа далее.

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

Несмотря на отсутствие соответствующего правила в синтаксисе С#, правильно сконструированный класс должен определять одну и только одну логическую сущ­ность. Например, класс, в котором хранятся Ф.И.О. и номера телефонов, обычно не со­держит сведения о фондовом рынке, среднем уровне осадков, циклах солнечных пятен или другую информацию, не связанную с перечисляемыми фамилиями. Таким об­разом, в правильно сконструированном классе должна быть сгруппирована логически связанная информация. Если же в один и тот же класс помещается логически несвя­занная информация, то структурированность кода быстро нарушается.

Классы, использовавшиеся в приведенных ранее примерах программ, содержали только один метод: Main(). Но в представленной выше общей форме определения класса метод Main() не указывается. Этот метод требуется указывать в классе лишь в том случае, если программа начинается с данного класса.

Определение класса

Для того чтобы продемонстрировать классы на конкретных примерах, разработаем постепенно класс, инкапсулирующий информацию о зданиях, в том числе о домах, складских помещениях, учреждениях и т.д. В этом классе (назовем его Building) бу­дут храниться три элемента информации о зданиях: количество этажей, общая пло­щадь и количество жильцов.

Ниже приведен первый вариант класса Building. В нем определены три перемен­ные экземпляра: Floors, Area и Occupants. Как видите, в классе Building вообще отсутствуют методы. Это означает, что в настоящий момент этот класс состоит только из данных. (Впоследствии в него будут также введены методы.)

class Building

{

public int Floors; // количество этажей

public int Area; // общая площадь здания

public int Occupants; // количество жильцов

}

Переменные экземпляра, определенные в классе Building, демонстрируют общий порядок объявления переменных экземпляра. Ниже приведена общая форма для объ­явления переменных экземпляра:

доступ тип имя_переменной;

где доступ обозначает вид доступа; тип - конкретный тип переменной, а имя_переменной - имя, присваиваемое переменной. Следовательно, за исключением специфи­катора доступа, переменная экземпляра объявляется таким же образом, как и локаль­ная переменная. Все переменные объявлены в классе Building с предваряющим их модификатором доступа public. Как пояснялось выше, благодаря этому они стано­вятся доступными за пределами класса Building.

Определение class обозначает создание нового типа данных. В данном случае но­вый тип данных называется Building. С помощью этого имени могут быть объявлены объекты типа Building. Не следует, однако, забывать, что объявление class лишь описывает тип, но не создает конкретный объект. Следовательно, в приведенном выше фрагменте кода объекты типа Building не создаются.

Для того чтобы создать конкретный объект типа Building, придется воспользо­ваться следующим оператором.

Building house = new Building(); // создать объект типа Building

После выполнения этого оператора объект house станет экземпляром класса Building, т.е. обретет "физическую" реальность. Не обращайте пока что внимание на отдельные составляющие данного оператора.

Всякий раз, когда получается экземпляр класса, создается также объект, содержа­щий собственную копию каждой переменной экземпляра, определенной в данном классе. Таким образом, каждый объект типа Building будет содержать свои копии переменных экземпляра Floors, Area и Occupants. Для доступа к этим перемен­ным служит оператор доступа к члену класса, который принято называть оператором-точкой. Оператор-точка связывает имя объекта с именем члена класса. Ниже приведе­на общая форма оператора-точки.

объект.член

В этой форме объект указывается слева, а член - справа. Например, присваива­ние значения 2 переменной Floors объекта house осуществляется с помощью сле­дующего оператора.

house.Floors = 2;

В целом, оператор-точка служит для доступа к переменным экземпляра и мето­дам. Ниже приведен полноценный пример программы, в которой используется класс Building.

Листинг 6.1

// Программа, в которой используется класс Building.

 

using System;

 

class Building

{

public int Floors; // количество этажей

public int Area; // общая площадь здания

public int Occupants; // количество жильцов

}

 

// В этом классе объявляется объект типа Building.

class BuildingDemo

{

static void Main()

{

Building house = new Building(); // создать объект типа Building

int areaPP; // площадь на одного человека

 

// Присвоить значения полям в объекте house.

house.Occupants = 4;

house.Area = 2500;

house.Floors = 2;

 

// Вычислить площадь на одного человека.

areaPP = house.Area / house.Occupants;

 

Console.WriteLine("Дом имеет:\n " +

house.Floors + " этажа\n " +

house.Occupants + " жильца\n " +

house.Area +

" кв. футов общей площади, из них\n " +

areaPP + " приходится на одного человека");

}

}

Эта программа состоит из двух классов: Building и BuildingDemo. В классе BuildingDemo сначала создается экземпляр house класса Building с помощью ме­тода Main(), а затем в коде метода Main() осуществляется доступ к переменным эк­земпляра house для присваивания им значений и последующего использования этих значений. Следует особо подчеркнуть, что Building и BuildingDemo - это два со­вершенно отдельных класса. Единственная взаимосвязь между ними состоит в том, что в одном из них создается экземпляр другого. Но, несмотря на то, что это раздельные классы, у кода из класса BuildingDemo имеется доступ к членам класса Building, поскольку они объявлены как открытые (public). Если бы при их объявлении не был указан спецификатор доступа public, то доступ к ним ограничивался бы пределами Building, а следовательно, их нельзя было бы использовать в классе BuildingDemo.

Допустим, что исходный текст приведенной выше программы сохранен в файле UseBuilding.cs. В результате ее компиляции создается файл UseBuilding.exe. При этом оба класса, Building и BuildingDemo, автоматически включаются в состав исполняемого файла. При выполнении данной программы выводится следующий ре­зультат.

Дом имеет:

2 этажа

4 жильца

2500 кв. футов общей площади, из них

625 приходится на одного человека

Но классам Building и BuildingDemo совсем не обязательно находиться в одном и том же исходном файле. Каждый из них можно поместить в отдельный файл, на­пример Building.cs и BuildingDemo.cs, а компилятору C# достаточно сообщить, что оба файла должны быть скомпилированы вместе. Так, если разделить рассматри­ваемую здесь программу на два таких файла, для ее компилирования можно восполь­зоваться следующей командной строкой.

csc Building.cs BuildingDemo.cs

Если вы пользуетесь интегрированной средой разработки Visual Studio, то вам нуж­но ввести оба упомянутых выше файла в свой проект и затем скомпоновать их.

Прежде чем двигаться дальше, рассмотрим следующий основополагающий прин­цип: у каждого объекта имеются свои копии переменных экземпляра, определенных в его классе. Следовательно, содержимое переменных в одном объекте может отли­чаться от их содержимого в другом объекте. Между обоими объектами не существует никакой связи, за исключением того факта, что они являются объектами одного и того же типа. Так, если имеются два объекта типа Building, то у каждого из них своя ко­пия переменных Floors, Area и Occupants, а их содержимое в обоих объектах может отличаться. Этот факт демонстрируется в следующей программе.

Листинг 6.2

// В этой программе создаются два объекта типа Building.

 

using System;

 

class Building

{

public int Floors; // количество этажей

public int Area; // общая площадь здания

public int Occupants; // количество жильцов

}

 

// В этом классе объявляется два объекта типа Building.

class BuildingDemo

{

static void Main()

{

Building house = new Building();

Building office = new Building();

 

int areaPP; // площадь на одного человека

 

// Присвоить значения полям в объекте house.

house.Occupants = 4;

house.Area = 2500;

house.Floors = 2;

 

// Присвоить значения полям в объекте office.

office.Occupants = 25;

office.Area = 4200;

office.Floors = 3;

 

// Вычислить площадь на одного человека в жилом доме.

areaPP = house.Area/house.Occupants;

 

Console.WriteLine("Дом имеет:\n " +

house.Floors + " этажа\n " +

house.Occupants + " жильца\n " +

house.Area +

" кв. футов общей площади, из них\n " +

areaPP + " приходится на одного человека");

 

 

Console.WriteLine();

 

// Вычислить площадь на одного человека в учреждении.

areaPP = office.Area/office.Occupants;

 

Console.WriteLine("Учреждение имеет:\n " +

office.Floors + " этажа\n " +

office.Occupants + " работников\n " +

office.Area +

" кв. футов общей площади, из них\n " +

areaPP + " приходится на одного человека");

}

}

Ниже приведен результат выполнения этой программы.

Дом имеет:

2 этажа

4 жильца

2500 кв. футов общей площади, из них

625 приходится на одного человека

Учреждение имеет:

3 этажа 25 работников

4200 кв. футов общей площади, из них

168 приходится на одного человека

Как видно, данные из объекта house полностью отделены от данных, содержащих­ся в объекте office. Эта ситуация наглядно показана на рис. 6.1.

 

Рисунок 6.1 - Переменные экземпляра одного объекта полностью отделены

от переменных экземпляра другого объекта

Создание объектов

В предыдущих примерах программ для объявления объекта типа Building ис­пользовалась следующая строка кода.

Building house = new Building();

Эта строка объявления выполняет три функции. Во-первых, объявляется перемен­ная house, относящаяся к типу класса Building. Сама эта переменная не является объектом, а лишь переменной, которая может ссылаться на объект. Во-вторых, соз­дается конкретная, физическая, копия объекта. Это делается с помощью оператора new. И наконец, переменной house присваивается ссылка на данный объект. Таким образом, после выполнения анализируемой строки объявленная переменная house ссылается на объект типа Building.

Оператор new динамически (т.е. во время выполнения) распределяет память для объ­екта и возвращает ссылку на него, которая затем сохраняется в переменной. Следова­тельно, в C# для объектов всех классов должна быть динамически распределена память.

Как и следовало ожидать, объявление переменной house можно отделить от созда­ния объекта, на который она ссылается, следующим образом.

Building house; // объявить ссылку на объект

house = new Building(); // распределить память для объекта типа Building

В первой строке объявляется переменная house в виде ссылки на объект типа Building. Следовательно, house - это переменная, которая может ссылаться на объ­ект, хотя сама она не является объектом. А во второй строке создается новый объект типа Building, и ссылка на него присваивается переменной house. В итоге перемен­ная house оказывается связанной с данным объектом.

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

int х;

х = 10;

переменная х содержит значение 10, поскольку она относится к типу int, который является типом значения. Но в строке

Building house = new Building();

переменная house содержит не сам объект, а лишь ссылку на него.



Поделиться:


Последнее изменение этой страницы: 2016-12-27; просмотров: 294; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.224.38.170 (0.007 с.)