C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
// Программа, в которой используется класс 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("Дом имеет:п " +
house.Floors + " этажап " + house.Occupants + " жильцап " + house.Area +
" кв. футов общей площади, из нихп " + агеаРР + " приходится на одного человека");
}
}
Эта программа состоит из двух классов: 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.es
Если вы пользуетесь интегрированной средой разработки Visual Studio, то вам нужно ввести оба упомянутых выше файла в свой проект и затем скомпоновать их.
Прежде чем двигаться дальше, рассмотрим следующий основополагающий принцип: у каждого объекта имеются свои копии переменных экземпляра, определенных в его классе. Следовательно, содержимое переменных в одном объекте может отличаться от их содержимого в другом объекте. Между обоими объектами не существует никакой связи, за исключением того факта, что они являются объектами одного и того же типа. Так, если имеются два объекта типа Building, то у каждого из них своя копия переменных Floors, Area и Occupants, а их содержимое в обоих объектах может отличаться. Этот факт демонстрируется в следующей программе.
// В этой программе создаются два объекта типа 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 BuildingO;
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 + " этажап " + house.Occupants + " жильцап " + house.Area +
" кв. футов общей площади, из нихп " + areaPP + " приходится на одного человека");
// Вычислить площадь на одного человека в учреждении. areaPP = office.Area / office.Occupants;
Console.WriteLine("Учреждение имеет:n " +
office.Floors + " этажап " +
office.Occupants + " работниковп " +
office.Area +
" кв. футов общей площади, из нихп " + 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 содержит не сам объект, а лишь ссылку на него.
Переменные ссылочного типа и присваивание
В операции присваивания переменные ссылочного типа действуют иначе, чем переменные типа значения, например типа int. Когда одна переменная типа значения присваивается другой, ситуация оказывается довольно простой. Переменная, находящаяся в левой части оператора присваивания, получает копию значения переменной, находящейся в правой части этого оператора. Когда же одна переменная ссылки на объект присваивается другой, то ситуация несколько усложняется, поскольку такое присваивание приводит к тому, что переменная, находящаяся в левой части оператора присваивания, ссылается на тот же самый объект, на который ссылается переменная, находящаяся в правой части этого оператора. Сам же объект не копируется. В силу этого отличия присваивание переменных ссылочного типа может привести к несколько неожиданным результатам. В качестве примера рассмотрим следующий фрагмент кода.