Ключевое слово this

Каждый объект содержит свой экземпляр полей класса. Методы находятся в па­мяти в единственном экземпляре и используются всеми объектами совместно, поэтому необходимо обеспечить работу методов нестатических экземпляров с по­лями именно того объекта, для которого они были вызваны. Для этого в любой нестатический метод автоматически передается скрытый параметр this, в котором хранится ссылка на вызвавший функцию экземпляр.

В явном виде параметр this применяется для того, чтобы возвратить из метода ссылку на вызвавший объект, а также для идентификации поля в случае, если его имя совпадает с именем параметра метода, например:

class Demo

{ double y;

public Demo T()

{ return this; }

public void Sety(double y)

{ this.y=y;}

}

3.1.5 Конструкторы

 

Конструктор предназначен для инициализации объекта. Он вызывается авто­матически при создании объекта класса с помощью операции new. Имя конст­руктора совпадает с именем класса. Ниже перечислены свойства конструкторов:

- Конструктор не возвращает значение, даже типа void.

- Класс может иметь несколько конструкторов с разными параметрами для раз­ных видов инициализации.

- Если программист не указал ни одного конструктора или какие-то поля не были инициализированы, полям значимых типов присваивается нуль, полям ссылочных типов — значение null.

- Конструктор, вызываемый без параметров, называется конструктором по умолчанию.

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

Конструктор инициализирует объект при его создании. Он имеет такое же имя,что и сам класс, а синтаксически подобен методу. Однако в определении конструкторов не указывается тип возвращаемого значения. Формат записи конструктора такой:

доступ имя_класса{)

{

// тело конструктора

}

Обычно конструктор используется, чтобы придать переменным экземпляра, определенным в классе, начальные значения или выполнить исходные действия, необходимые для создания полностью сформированного объекта. Кроме того, обычно в качестве элемента доступ используется модификатор доступа public, поскольку конструкторы, как правило, вызываются вне их класса.

Все классы имеют конструкторы независимо от того, определите вы их или нет, поскольку С# автоматически предоставляет конструктор по умолчанию, который инициализирует все переменные-члены, имеющие тип значений, нулями, а переменные-члены ссылочного типа — null-значениями. Но если вы определите собственный конструктор, конструктор по умолчанию больше не используется.

Вот пример использования конструктора:

// Использование простого конструктора.

usingSystem;

classMyClass {

publicint x;

publicMyClass() {

x = 10;

}

}

c l a s sConsDemo {

p u b l i c s t a t i c void Main() {

MyClass t l = new MyClass();

MyClass t 2 = new MyClass();

Co n s o l e .W r i t e L i n e ( t l . x + " " + t 2 . x ) ;

}

}

Обратите внимание на public-определение конструктора, которое позволяет вызывать его из кода, определенного вне класса MyClass. Этот конструктор присваивает переменной экземпляра х значение 10. Конструктор MyClass () вызывается оператором new при создании объекта класса MyClass. Например, при выполнении строки MyClasstl = newMyClass();

для объектаtl вызывается конструктор MyClass (), который присваивает переменной экземплярраtl.x значение 10. То же самое справедливо и в отношении объекта t2, т.е. в результате создания объекта t2 значение переменной экземпляра t2.x также станет равным 10.

3.1.5.1 Параметризованные конструкторы

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

// Использование параметризованного конструктора.

using System;

classMyClass {

publicint x;

publicMyClass(inti) {

x = i;

}}

classParmConsDemo {

public static void Main() {

MyClass t1 = new MyClass(10);

MyClass t2 = new MyClass(88);

Console.WriteLine (t1.x + " " + t2.x);

}

}

Результат выполнения этой программы выглядит так:

10 88

В конструкторе MyClass () этой версии программы определен один параметр с именем i, который используется для инициализации переменной экземпляра х. Таким образом, при выполнении строки кода

MyClasst1 = newMyClass(10);

параметру i передается значение 10, которое затем присваивается переменной экземпляра х.

 

3.2 Использование оператора new

Формат оператораnew таков:

 

переменная_типа_класса = newимя_класса() ;

 

элемент переменная_типа_класса означает имя создаваемой переменнойтипа класса, под элементом имя_класса понимается имяреализуемого в объекте класса. Имя класса вместе со следующей за ним парой круглых скобок — это ни что иное, как конструктор реализуемого класса. Если в классеконструктор не определен явным образом, оператор new будет использовать конструктор по умолчанию, который предоставляется средствами языкаС#. Таким образом,оператор new можно использовать для создания объекта любого "классового" типа.

Допустимо использовать оператор new и с типами значений.

Вот пример:

inti = newint();

В этом случае вызывается конструктор по умолчанию для типа int, который инициализирует переменную i нулем.

 

using System;

classnewValue {

public static void Main() {

inti = new int(); // Инициализацияiнулем.

Console.WriteLine("Значениепеременной i равно: " + i);

}}

3.3 Сбор "мусора" и использование деструкторов

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

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

Эту(восстановленную) память снова можно использовать для размещения другихобъектов.

Система сбора мусора действует только спорадически во время выполнения отдельной программы. Эта система может и бездействовать: она не "включается" лишьпотому, что существует один или несколько объектов, которые больше не используются в программе. Поскольку на сбор мусора требуется определенное время, динамическая система С# активизирует этот процесс только по необходимости или в специальных случаях. Таким образом, вы даже не будете знать, когда происходит сбор мусора, а когда — нет.

3.4 Деструкторы

 

Средства языка С# позволяют определить метод, который должен вызываться непосредственно перед тем, как объект будет окончательно разрушен системой сбора мусора. Этот метод называется деструктором, и его можно использовать для обеспечения гарантии "чистоты" ликвидации объекта. Например, вы могли бы использовать деструктор для гарантированного закрытия файла, открытого некоторым объектом.

Формат записи деструктора такой:

~имя_класса() {

// код деструктора

}

Очевидно, что элемент имя_класса здесь означает имя класса. Таким образом, деструктор объявляется подобно конструктору за исключением того, что его имени предшествует символ "тильда" (~).

Чтобы добавить деструктор в класс, достаточно включить его как член. Он вызывается в момент, предшествующий процессу утилизации объекта. В теле деструктора вы указываете действия, которые, по вашему мнению, должны быть выполнены перед разрушением объекта.

using System;

class Destruct {

publicint x;

public Destruct(inti) {

x = i;

}

// Вызывается при утилизации объекта.

~Destruct() {

Console.WriteLine("Деструктуризация " + x) ;

}

// Метод создает объект, который немедленно

// разрушается.

public void generator(inti) {

Destruct o = new Destruct(i);

} }

classDestructDemo {

public static void Main() {

int count;

Destruct ob = new Destruct(0);

for(count=l; count < 100000; count++)

ob.generator(count);

Console.WriteLine("Готово!");

}}

 

Лекция 4.Способы построения классов

4.1 Методыкласса

Метод — это функциональный элемент класса, который реализует вычисления или другие действия, выполняемые классом или экземпляром. Методы опреде­ляют поведение класса.

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

Синтаксис метода:

[ атрибуты ] [ спецификаторы ] тип имя_метода ( [ параметры ] ) тело метода

Первая строка представляет собой заголовок метода. Тело метода, задающее действия, выполняемые методом, чаще всего представляет собой блок — последовательность операторов в фигур­ных скобках.

При описании методов можно использовать спецификаторы 1-7 из табл. 1.2, имеющие тот же смысл, что и для полей, а также спецификаторыvirtual, sealed, override, abstract и extern, которые будут рассмотрены по мере необходимости. Чаше всего для методов задается спецификатор доступа public, ведь методы составляют интерфейс класса - то, с чем работает пользователь, поэтому они должны быть доступны.

Статические (static) методы, или методы класса, можно вызывать, не создавая эк­земпляр объекта. Именно таким образом используется метод Main.Пример простейшего метода:

publicdoubleGety()// метод для получения поля у из листинга

{

return у;

}

Тип определяет, значение какого типа вычисляется с помощью метода. Часто употребляется термин «метод возвращает значение», поскольку после выполне­ния метода происходит возврат в то место вызывающей функции, откуда был вызван метод, и передача туда значения выражения, записанного в операторе return. Если метод не возвращает никакого значения, в его заголовке задается тип void, а оператор return отсутствует.

Параметры используются для обмена информацией с методом. Параметр пред­ставляет собой локальную переменную, которая при вызове метода принима­ет значение соответствующего аргумента. Область действия параметра - весь метод.

Метод, не возвращающий значение, вызывается отдельным оператором, а метод, возвращающий значение, — в составе выражения в правой части оператора при­сваивания.

Например, чтобы вычислить значение синуса для вещественной величины х, мы передаем ее в качестве аргумента в метод Sin класса Math, а чтобы вывести значение этой переменной на экран, мы передаем ее в метод WriteLine класса Console

doubleх = 0.1 ;

doubleу=Math .Sin(x):

Console.WriteLine(x);

При этом метод Sin возвращает в точку своего вызова вещественное значение си­нуса, которое присваивается переменной у, а метод WriteLineничего не возвращает.

Метод, не возвращающий значение, вызывается отдельным оператором, а метод, возвращающий значение, — в составе выражения в правой части оператора при­сваивания.

Параметры, описываемые в заголовке метода, определяют множество значений аргументов, которые можно передавать в метод. Список аргументов при вызо­ве как бы накладывается на список параметров, поэтому они должны попарносоответствовать друг другу.

Для каждого параметра должны задаваться его тип и имя. Например, заголовок метода Sin выглядит следующим образом:

public static double Sin( double a ):

Имя метода вкупе с количеством, типами и спецификаторами его параметров представляет собой сигнатуру метода — то, по чему один метод отличают от других. В классе не должно быть методов с одинаковыми сигнатурами.

В листинге 1.2 в класс Demo добавлены методы установки и получения значения поля у. Кроме того, статическое поле s закрыто, то есть определено по умолчанию как private, а для его получения описан метод Gets, являющий собою пример статического метода.

Листинг 1.2. Простейшиеметоды

using System;

namespace ConsoleApplication1

{

class Demo

{

publicint a=1;

public const double c=1.66;

static string s=”Demo”;

double y;

public double Gety()

{

return y;

}

public void Sety(double y_)

{

y=y_;

}

public static string Gets()

{

return s;

}

}

class Class1

{ static void Main()

{

Demo x=new Demo();

x.Sety(0.12);

Console.WriteLine(x.Gety());

Console.WriteLine(Demo.Gets());

// Console.WriteLine(Gets()); при вызове из другого метода этого объекта

 

}

}

}

Методы класса имеют непосредственный доступ к его закрытым полям. Метод, описанный со спецификатором static, должен обращаться только к статическим полям класса. Обратите внимание на то, что статический метод вызывается через имя класса, а обычный через имя экземпляра.

При вызове метода из другого метода того же класса имя класса/экземпляра можно не указывать.

4.1.1 Параметры методов

Рассмотрим более подробно, каким образом метод обменивается информацией с вызвавшим его кодом. При вызове метода выполняются следующие действия:

1. Вычисляются выражения, стоящие на месте аргументов.

2. Выделяется память под параметры метода в соответствии с их типом.

3. Каждому из параметров сопоставляется соответствующий аргумент (аргумен­ты как бы накладываются на параметры и замещают их).

4. Выполняется тело метода.

Если метод возвращает значение, оно передается в точку вызова; если метод имеет тип void, управление передается на оператор, следующий после вызова.

При этом проверяется соответствие типов аргументов и параметров и при необ­ходимости выполняется их преобразование. При несоответствии типов выдается диагностическое сообщение.