Ниже приведен простой пример применения конструктора. // Простой конструктор. using System; class MyClass { public int x; public MyClass() { x = 10; } } class ConsDemo { static void Main() { MyClass t1 = new MyClass(); MyClass t2 = new MyClass(); Console.WriteLine(t1.x + " " + t2.x); } }
В данном примере конструктор класса MyClass имеет следующий вид. public MyClass() { x = 10; }
Обратите внимание на то, что этот конструктор обозначается как public. Дело в том, что он должен вызываться из кода, определенного за пределами его класса. В этом конструкторе переменной экземпляра класса MyClass присваивается значе ние 10. Он вызывается в операторе new при создании объекта. Например, в следующей строке: MyClass t1 = new MyClass();
конструктор MyClass() вызывается для объекта t1, присваивая переменной его эк земпляра t1.х значение 10. То же самое происходит и для объекта t2. После констру ирования переменная t2.х будет содержать то же самое значение 10. Таким образом, выполнение приведенного выше кода приведет к следующему результату. 10 10 Параметризированные конструкторы
В предыдущем примере использовался конструктор без параметров. В некоторых случаях этого оказывается достаточно, но зачастую конструктор, должен принимать один или несколько параметров. В конструктор параметры вводятся таким же образом, как и в метод. Для этого достаточно объявить их в скобках после имени конструктора. Ниже приведен пример применения параметризированного конструктора MyClass. // Параметризированный конструктор. using System; class MyClass { public int x; public MyClass(int i) { x = i; } } class ParmConsDemo { static void Main() { MyClass t1 = new MyClass(10); MyClass t2 = new MyClass(88); Console.WriteLine(t1.x + " " + t2.x); } }
При выполнении этого кода получается следующий результат. 10 88
В данном варианте конструктора MyClass() определен параметр i, с помощью которого инициализируется переменная экземпляра х. Поэтому при выполнении следующей строки кода: MyClass t1 = new MyClass(10);
параметру i передается значение, которое затем присваивается переменной х. Добавление конструктора в класс Building
Класс Building можно усовершенствовать, добавив в него конструктор, автомати чески инициализирующий поля Floors, Area и Occupants при создании объекта. Обратите особое внимание на то, как создаются объекты класса Building. // Добавить конструктор в класс Building. using System; class Building { public int Floors; // количество этажей public int Area; // общая площадь здания public int Occupants; // количество жильцов // Параметризированный конструктор для класса Building. public Building(int f, int a, int o) { Floors = f; Area = a; Occupants = o; } // Возвратить площадь на одного человека. public int AreaPerPerson() { return Area / Occupants; } // Возвратить максимальное количество человек, занимающих здание, // исходя из заданной минимальной площади на одного человека. public int MaxOccupant(int minArea) { return Area / minArea; } } // Использовать параметризированный конструктор класса Building. class BuildingDemo { static void Main() { Building house = new Building(2, 2500, 4); Building office = new Building(3, 4200, 25); Console.WriteLine("Максимальное количество человек в доме, \n" + "если на каждого должно приходиться " + 300 ,+ " кв. футов: " + house.MaxOccupant(300)); Console.WriteLine("Максимальное количество человек " + "в учреждении, \n" + "если на каждого должно приходиться " + 300 + " кв. футов: " + office.MaxOccupant (300)); } }
Результат выполнения этой программы оказывается таким же, как и в предыдущей ее версии.
Оба объекта, house и office, были инициализированы конструктором Building() при их создании в соответствии с параметрами, указанными в этом конструкторе. Например, в строке Building house = new Building(2, 2500, 4);
конструктору Building() передаются значения 2, 2500 и 4 при создании нового объек та. Следовательно, в копиях переменных экземпляра Floors, Area и Occupants объекта house будут храниться значения 2, 2500 и 4 соответственно. Еще раз об операторе new
Теперь, когда вы ближе ознакомились с классами и их конструкторами, вернемся к оператору new, чтобы рассмотреть его более подробно. В отношении классов общая форма оператора new такова: new имя_класса(список_аргументов)
где имякласса обозначает имя класса, реализуемого в виде экземпляра его объекта. А имякласса с последующими скобками обозначает конструктор этого класса. Если в классе не определен его собственный конструктор, то в операторе new будет исполь зован конструктор, предоставляемый в C# по умолчанию. Следовательно, оператор new может быть использован для создания объекта, относящегося к классу любого типа.
Оперативная память не бесконечна, и поэтому вполне возможно, что оператору new не удастся распределить память для объекта из-за нехватки имеющейся оператив ной памяти. В этом случае возникает исключительная ситуация во время выполнения (подробнее об обработке исключительных ситуаций речь пойдет в главе 13). В приме рах программ, приведенных в этой книге, ситуация, связанная с исчерпанием опера тивной памяти, не учитывается, но при написании реальных программ такую возмож ность, вероятно, придется принимать во внимание. Применение оператора new вместе с типами значений
В связи с изложенным выше возникает резонный вопрос: почему оператор new не целесообразно применять к переменным таких типов значений, как int или float? В C# переменная типа значения содержит свое собственное значение. Память для хра нения этого значения выделяется автоматически во время прогона программы. Следо вательно, распределять память явным образом с помощью оператора new нет никакой необходимости. С другой стороны, в переменной ссылочного типа хранится ссылка на объект, и поэтому память для хранения этого объекта должна распределяться динами чески во время выполнения программы.
Благодаря тому что основные типы данных, например int или char, не преобра зуются в ссылочные типы, существенно повышается производительность программы. Ведь при использовании ссылочного типа существует уровень косвенности, повышаю щий издержки на доступ к каждому объекту. Такой уровень косвенности исключается при использовании типа значения.
Но ради интереса следует все же отметить, что оператор new разрешается исполь зовать вместе с типами значений, как показывает следующий пример. int i = new int();
При этом для типа int вызывается конструктор, инициализирующий по умол чанию переменную i нулевым значением. В качестве примера рассмотрим такую программу. // Использовать оператор new вместе с типом значения. using System; class newValue { static void Main() { int i = new int(); // инициализировать переменную i нулевым значением Console.WriteLine("Значение переменной i равно: " + i); } }
Выполнение этой программы дает следующий результат. Значение переменной i равно: 0
Как показывает результат выполнения данной программы, переменная i инициа лизируется нулевым значением. Напомним, что если не применить оператор new, то переменная i окажется неинициализированной. Это может привести к ошибке при попытке воспользоваться ею в операторе, содержащем вызов метода WriteLine(), если предварительно не задать ее значение явным образом.
В общем, обращение к оператору new для любого типа значения приводит к вызову конструктора, используемого по умолчанию для данного типа. Но в этом случае па мять динамически не распределяется. Откровенно говоря, в программировании обыч но не принято пользоваться оператором new вместе с типами значений. “Сборка мусора” и применение деструкторов
Как было показано выше, при использовании оператора new свободная память для создаваемых объектов динамически распределяется из доступной буферной об ласти оперативной памяти. Разумеется, оперативная память не бесконечна, и поэто му свободно доступная память рано или поздно исчерпывается. Это может привести к неудачному выполнению оператора new из-за нехватки свободной памяти для соз дания требуемого объекта. Именно по этой причине одной из главных функций лю бой схемы динамического распределения памяти является освобождение свободной памяти от неиспользуемых объектов, чтобы сделать ее доступной для последующего перераспределения. Во многих языках программирования освобождение распреде ленной ранее памяти осуществляется вручную. Например, в C++ для этой цели слу жит оператор delete. Но в C# применяется другой, более надежный подход: "сборка мусора".
Система "сборки мусора" в C# освобождает память от лишних объектов автома тически, действуя незаметно и без всякого вмешательства со стороны программиста. "Сборка мусора" происходит следующим образом. Если ссылки на объект отсутству ют, то такой объект считается ненужным, и занимаемая им память в итоге освобож дается и накапливается. Эта утилизированная память может быть затем распределена для других объектов.
"Сборка мусора" происходит лишь время от времени по ходу выполнения про граммы. Она не состоится только потому, что существует один или более объектов, которые больше не используются. Следовательно, нельзя заранее знать или предпо ложить, когда именно произойдет "сборка мусора". Деструкторы
В языке C# имеется возможность определить метод, который будет вызываться не посредственно перед окончательным уничтожением объекта системой "сборки му сора". Такой метод называется деструктором и может использоваться в ряде особых случаев, чтобы гарантировать четкое окончание срока действия объекта. Например, деструктор может быть использован для гарантированного освобождения системного ресурса, задействованного освобождаемым объектом. Следует, однако, сразу же под черкнуть, что деструкторы — весьма специфические средства, применяемые только в редких, особых случаях. И, как правило, они не нужны. Но здесь они рассматривают ся вкратце ради полноты представления о возможностях языка С#.