Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лр-2(Классы).docx
Скачиваний:
3
Добавлен:
16.08.2019
Размер:
142.88 Кб
Скачать

Конструкторы и деструкторы

При определении класса в С# определять ассоциируемые с ним конструкторы и деструкторы обычно не обязательно, поскольку, если таковые не предоставляются, компилятор добавляет их автоматически во время сборки кода. Однако если требуется, можно предоставлять свои собственные и с их помощью выполнять, соответственно, инициализацию объектов и очистку после их уничтожения.

Простой конструктор можно добавить в класс с помощью такого синтаксиса:

Кусочек программного кода:

class

MyClass {

public MyClass () {

// Код конструктора.

}

}

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

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

class MyClass {

private MyClass () {

// Код конструктора.

}

}

И, наконец, подобным образом в класс можно добавлять и не используемые по умолчанию конструкторы, просто указанием параметров:

class MyClass {

public MyClass ( ) {

// Код конструктора по умолчанию.

}

public MyClass (int my int) {

// Код конструктора не по умолчанию (с параметром myint)

}

}

Количество предоставляемых конструкторов может быть практически бесконечным (разумеется, нужно принимать во внимание, что ресурсы памяти и возможные наборы параметров ограничены).

Деструкторы объявляются с помощью немного другого синтаксиса. Деструктор, используемый в .NET (и предоставляемый классом System.Object), называется Finalize, но не это имя применяется для объявления деструктора. Вместо переопределения Finalize используется следующий код:

class MyClass {

-MyClass () {

// Тело деструктора.

}

}

То есть деструктор класса объявляется путем указания имени класса (как и конструктор) с префиксом ~ (тильда) впереди. Код в деструкторе выполняется на этапе сборки мусора, позволяя освобождать ресурсы. После вызова данного деструктора также осуществляются и неявные вызовы деструкторов базовых классов, в числе которых и вызов деструктора Finalize корневого класса System.Object. Подобный подход позволяет .NET Framework гарантировать их выполнение, поскольку в случае переопределения деструктора Finalize вызовы деструктором базовых классов нужно было бы выполнять явно, что потенциально опасно (о том, как вызывать методы базовых классов).

Последовательность выполнения конструкторов

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

Первым делом, однако, необходимо более подробно узнать о том, что же происходит по умолчанию при создании экземпляра класса. Во время разработки объекты часто ведут себя не совсем так, как ожидается, из-за ошибок, которые возникают во время вызова конструкторов — обычно либо из-за неправильного создания экземпляра какого-то базового класса в иерархии наследования или неверного предоставления информации конст­рукторам базовых классов. Понимание того, что происходит на этом этапе жизненного цикла объекта, может значительно упростить решение проблем подобного рода.

Чтобы был создан экземпляр производного класса, сначала должен обязательно быть создан экземпляр его базового класса, а чтобы был создан экземпляр этого базового класса, сначала должен быть создан экземпляр его собственного базового класса, и т.д., вплоть до самого класса System.Object (который является для всех классов корневым). Поэтому, какой бы конструктор не использовался для создания экземпляра класса, первым все равно всегда вызывается конструктор System.Object .Object.

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

Листинг 1.1 Иерархия обьектов

0001:

0002:

0003:

0004:

0005:

0006:

0007:

0008:

0009:

0010:

0011:

0012:

0013:

0014:

0015:

0016:

0017:

0018:

0019:

0020:

0021:

0022:

0023:

0024:

0025:

0026:

0027:

0028:

0029:

0030:

using System;

using System.Collections .Generic- using System.Linq;

using System.Text;

using System.Diagnostics;

namespace Ch07Ex01

{

class Program

{

static void Main(string [ ] args)

{

int[] testArray = {4, 7, 4, 2, 7, 3, 7, 8, 3, 9, 1, 9};

int[] maxVallndices;

int maxVal = Maxima (testArray, out maxVallndices) ;

Console. WriteLine ("Maximum value {0} found at element

indices:",

// Вывод максимального значения и его индексов

maxVal);

foreach (int index in maxVallndices)

public class MyBaseClass {

public MyBaseClass ()

{

}

public MyBaseClass(int i)

{

}

}

public class MyDerivedClass : MyBaseClass {

public MyDerivedClass() public MyDerivedClass(int 1) public MyDerivedClass(int i, int j)

}

В такой иерархии экземпляр класса MyDerivedClass может создаваться так:

MyDerivedClass myObj = new MyDerivedClass() ;

Тогда последовательность происходящих событий будет выглядеть следующим образом.

  • Сначала будет выполняться код конструктора System.Object.Object.

  • Затем будет выполняться код конструктора MyBaseClass .MyBaseClass.

  • И, наконец, последним будет выполняться код конструктора MyDerivedClass. MyDerivedClass.

В качестве альтернативного варианта экземпляр класса MyDerivedClass может создавать и так:

MyDerivedClass myObj = new MyDerivedClass(4);

Тогда последовательность событий будет выглядеть, как описано ниже.

  • Сначала будет выполняться код конструктора System.Object .Object.

  • Затем будет выполняться код конструктора MyBaseClass .MyBaseClass.

  • И, наконец, последним будет выполняться код конструктора MyDerivedClass. MyDerivedClass(int i).

И, наконец, экземпляр класса MyDerivedClass может создаваться еще и так:

MyDerivedClass myObj = new MyDerivedClass(4, 8);

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

  • Сначала будет выполняться код конструктора System.Object.Object.

  • Затем будет выполняться код конструктора MyBaseClass.MyBaseClass.

  • И, наконец, последним будет выполняться код конструктора MyDerivedClass. MyDerivedClass (int i, int j).

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

  • Первым выполнялся код конструктора System. Ob ject. Ob ject.

  • Вторым — код конструктора MyBaseClass .MyBaseClass (int i).

  • Третьим — код конструктора MyDerivedClass .MyDerivedClass (int i, int j).

Тогда код, в котором используется параметр int i, можно будет поместить в MyBaseClass (int i), а это значит, что у конструктора MyDerivedClass (int i, int j ) будет меньше работы — ему придется обрабатывать только параметр int j. (Здесь предполагается, что в обоих сценариях предназначение параметра int i выглядит идентично, что может не всегда быть так, но на практике при такой организации обычно именно так и бывает.) Язык С# позволяет при желании задавать подобное поведение.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]