Компьютерные технологии (программирование на C#)

Изучение особенностей программирования на платформе .NET. Описание библиотеки классов. Конфликт имен и пространство имен. Статический конструктор и класс. Методы Equals и ReferenceEquals. Способы new и virtual, override переопределния членов класса.

Рубрика Программирование, компьютеры и кибернетика
Вид учебное пособие
Язык русский
Дата добавления 12.08.2015
Размер файла 134,2 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

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

Аргументы командной строки можно задавать непосредственно в среде. Для этого выполните команду Project.Project1 Properties…. На закладке Debug открывшегося окна найдите элемент Command Line Arguments. Значение этого элемента - текст аргументов командной строки и, тем самым, фактическое значение параметра args метода Main.

Наберите, к примеру, 1 2 3 (с пробелами!). В метод Main класса _2 вставьте текст так, что метод Main примет вид

static void Main(string[] args)

{

for (int i = 0; i < args.Length; i++)

System.Console.Write(args[i] + " ");

System.Console.ReadLine();

}

Результат в черном окне 1 2 3

Обратите внимание, что в результирующей строке после символов 1 2 3 символ положения каретки | не перенесен на новую строку. Это объясняется тем, что для вывода на экран результата (элементы массива args[i]) используется метод Write класса Console, а не WriteLine.

1.13 Операторы цикла

Оператор цикла for

Оператор System.Console.Write(args[i]+" ") вызывается на каждом шаге цикла

for (int i=0;i<args.Length;i++).

Счетчиком цикла (counter) является локальная переменная i типа int, которая меняется в пределах от нуля до значения args.Length-1. Свойство Length массива args возвращает длину массива - число элементов массива. Оператор ++ увеличивает значение i на единицу на каждом шаге цикла. Оператор i++ равносилен оператору i=i+1 или i+=1.

Структура оператора цикла for выглядит следующим образом

for (<инициализаторы>;<логическое выражение, проверяющее условие конца цикла>;<итераторы>) <выполняемый оператор>.

Любая из трех секций оператора for, стоящих в скобках, может быть пустой. Например, оператор for (;;) образует бесконечный цикл.

При действии оператора for

в начале работает секция инициализаторов,

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

Если условие выполняется, то управление передается выполняемому оператору,

затем выполняются итераторы, и вновь проверяется условие.

Если условие не выполняется, то управление передается внешнему оператору, следующему за оператором цикла.

Таким образом, оператор, стоящий в цикле for, может ни разу не выполниться. Замените в операторе цикла условие i<args.Length на i<0. Проверьте результат.

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

Например, инициализаторы int i=0,j=100 и итераторы i++,--j.

Испытайте разные сочетания элементов оператора for. Если программа выйдет на ошибку в ходе выполнения, вернитесь к редактированию командой Debug.Stop Debugging.

Операторы цикла while и do…while

В языке C# есть еще два оператора цикла. Это

оператор «с предусловием» while <условие> <выполняемый оператор>

и оператор «с постусловием» do <выполняемый оператор> while <условие>.

Заменим цикл с оператором for циклом с оператором while так, что тело метода Main будет иметь вид

int i = 0;

while (i < args.Length) System.Console.Write(args[i++] + " ");

System.Console.ReadLine();

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

Операторы цикла for и while в начале проверяют условие, а затем выполняют требуемые действия.

Оператор цикла «с постусловием» do…while в начале обязательно выполняет действие, а затем проверяет условие выхода из цикла.

Испытайте оператор do…while в нашем коде

int i = 0;

do

System.Console.Write(args[i++] + " ");

while (i < args.Length);

System.Console.ReadLine();

1.14 Операторы инкремент ++ и декремент

Отметим использование в наших последних циклах оператора инкремента i++ для получения индекса элемента массива.

Величина i++ имеет значение i до изменения. Затем действует оператор ++, увеличивающий значение i на 1, как если бы стоял оператор i=i+1.

Выражение ++i в начале увеличивает значение i на 1, а затем возвращает результат.

Замена i++ на ++i в приведенном коде существенно изменит его логику, что приведет к ошибке времени выполнения типа Index was outside the bounds of the array (индекс вышел за границы массива). Проверьте.

Заметим, что в операторе цикла for замена i++ на ++i вполне безвредна. Аналогично операторам инкремента ++ действует оператор декремента --, вычитающий единицу.

Вопросы для самоконтроля

Чем отличаются исполняемые модули .exe и .dll?

Компилируемый модуль vs исполняемый модуль. Сборка.

Пространство имен vs компилируемый модуль.

Типы классов в C#.

Структура классов в C#.

Solution vs project.

Идентификаторы в C#.

Метод Main.

Модификатор static.

Метод класса.

Перегруженный (overloaded) метод.

Константы типа string и escape-последовательности.

Директива using.

Параметры по значению vs параметры по ссылке.

Командная строка.

Оператор цикла for.

Операторы цикла while и do…while.

Операторы инкремент ++ и декремент --.

Занятие 2. Библиотека классов

Добавим в контейнер Solution1 новый проект.

Но прежде заменим имя готового проекта Project1 именем MainExe. Для этого в окне Solution Explorer щелкнем правой кнопкой над узлом Project1 и в контекстном меню выберем команду Rename. Введем новое имя MainExe. Сохраним все файлы командой File.Save All.

Замечание.

Изменения имен проектов следует проводить командами среды, а не файловой системы.

Изменим также имя сборки (Assembly). Командой Project.MainExe Properties…откроем окно со свойствами проекта. Найдем элемент Assembly Name и наберем новое имя MainExe. Вновь сохраним все файлы проекта командой File.Save All.

Теперь откроем новый проект. Для этого в окне Solution Explorer щелкнем правой кнопкой над верхним узлом дерева Solution`Solution1'(1 project). В появившемся контекстном меню вызовем команду Add.NewProject…. В появившемся окне на панели Templates: выберем Empty Project и, набрав Lib в качестве значения Name, щелкнем кнопку Add. В дереве окна Solution Explorer должен появиться узел Lib. Сохраним все файлы.

Теперь добавим к проекту Lib файл кода. Щелкнув правой кнопкой над узлом Lib, вызовем командой Add.New Item… окно с шаблонами. Выберем шаблон Code File, а в строке Name исправим предложенное средой имя файла. Пусть это будет LibCode.cs. Нажмем Add и сохраним все файлы.

2.1 Создание библиотеки классов

Нашей первой целью является создание проекта, результатом которого будет библиотека - исполняемый модуль с расширением .dll.

Для этого вызовем командой Project.Lib Properties… окно со свойствами проекта Lib. Найдем элемент Output Type, в котором по умолчанию должно стоять значение Console Application. Открыв список допустимых значений, выберем Class Library. Теперь результирующим исполняемым модулем проекта Lib будет не exe-файл (как было по умолчанию), а файл с расширением .dll.

В пустом окне файла Lib.cs наберем текст

class _1

{

void Method()

{

System.Console.WriteLine("Lib");

}

}

Имя Method является, конечно, произвольным идентификатором.

Скомпилируем Solution1 командой Build.Build Solution. В каталоге Solution1 должен образоваться каталог Lib, а внутри него, в каталогах obj и bin, после компиляции должен появиться файл Lib.dll.

Попытаемся использовать класс новой библиотеки в проекте MainExe. Для этого необходимо, чтобы, как минимум, сама библиотека Lib.dll была доступна проекту MainExe. С этой целью откроем окно Solution Explorer и щелкнем правой кнопкой над узлом References (ссылки) проекта MainExe. В появившемся меню выберем команду Add Reference…. Откроется окно с тем же именем. В нем на странице с закладкой Projects должна быть строка с именем Lib. Надо выделить эту строку и нажать OK. Убедитесь, что после этого узел References проекта MainExe в окне Solution Explorer содержит ссылку на библиотеку Lib.

Проведите рекомпиляцию Solution1 командой Build.Rebuild Solution. В результате копия файла Lib.dll должна оказаться среди файлов каталога Project1\bin\Debug проекта MainExe.

Предоставив проекту MainExe доступ к библиотеке классов Lib, можно попытаться использовать библиотечный класс _1 в классах MainExe.

Войдем в окно файла CodeFile2 проекта MainExe. Уберем все операторы в методе Main за исключением последнего оператора. Так что код файла CodeFile2 примет вид

class _2

{

static void Main(string[] args)

{

System.Console.ReadLine();

}

}

Попытаемся первым оператором в методе Main вызвать ссылку на класс _1 из библиотеки Lib.

Первая трудность, с которой мы столкнемся, это явный конфликт имен. В модуле CodeFile1 проекта MainExe уже существует класс с тем же именем _1, что и библиотечный. Из-за него не виден библиотечный класс _1. Чтобы разрешить этот конфликт можно

переименовать один из классов _1 - либо в библиотеке Lib, либо в проекте MainExe.

расширить имя одного или обоих классов.

Первый способ не самый лучший. Обычно имена классов несут смысловую нагрузку, которую программист не захочет терять.

2.2 Конфликт имен и пространство имен

Воспользуемся вторым способом.

Конфликт имен наступил из-за того, что имена классов как библиотеки Lib, так и проекта MainExe принадлежат одному и тому же пространству имен. Это корневое пространство имен с псевдонимом global. Изолировать пространства имен можно, поместив код библиотечного класса в файле LibCode в отдельное пространство имен с именем, например, LibClasses так, что новый код будет иметь вид

namespace LibClasses

{

class _1

{

void Method()

{

System.Console.WriteLine("Lib");

}

}

}

Теперь общее имя LibClasses изолирует имена классов, описанных внутри блока {} пространства имен LibClasses. Библиотечный класс _1 получает расширенное имя LibClasses._1, и это позволяет разрешить конфликт имен.

Заметим, что пространство имен System принадлежит корневому пространству имен, описываемому псевдонимом в форме служебного слова global. Убедитесь в этом, расширив в предыдущем коде имя System именем global::System. Обратите внимание на оператор доступа «двойное двоеточие» ::, заменяющий в этом специальном случае оператор «точка» . (dot).

После редакции сохраним все файлы, проведем рекомпиляцию Solution1 командой Build.Rebuild Solution и вновь перейдем в окно CodeFile2 проекта MainExe.

Сделаем еще одну попытку сослаться на библиотечный класс, применив на этот раз расширенное имя LibClasses._1.

Начнем набирать это имя сразу после открывающей скобки { метода Main класса _2. После набора первой буквы L IntelliSense должен сразу подсказать полное имя LibClasses. Но после полного набора LibClasses. (с точкой) ожидаемая подсказка с именем класса _1 не появится! Разберемся в этом.

2.3 Модификаторы доступа к классам

Строка

class _1

является простейшим заголовком описания класса.

Одни классы могут быть доступны только коду сборки (Assembly), частью которой является компилируемый модуль. Тогда в заголовке класса указывается модификатор доступа internal, либо не указывается никакой модификатор доступа. В этом последнем случае, как говорят, «по умолчанию», модификатором доступа также будет internal. Класс с неограниченной областью доступа следует предварять модификатором public. Класс _1 в нашей библиотеке Lib не имеет модификатора доступа и, следовательно, имеет модификатор internal и может быть доступен только коду сборки Lib.

Замечание.

Если сборка состоит из нескольких компилируемых модулей, то класс с модификатором internal доступен коду в любом из модулей. Так класс _1 проекта MainExe, описанный в компилируемом модуле CodeFile1 с модификатором internal, доступен коду в модуле CodeFile2, так как компилируемый модуль CodeFile2 принадлежит тому же проекту MainExe (т.е., является частью одной сборки), что и модуль CodeFile1.

Изменим модификатор доступа к классу LibClasses._1, добавив к его описанию в модуле LibCode модификатор public. Новый код файла LibCode теперь

namespace LibClasses

{

public class _1

{

void Method()

{

System.Console.WriteLine("Lib");

}

}

}

2.4 Экземпляр класса (instance)

Возвратимся в модуль CodeFile2 проекта MainExe, очистим метод Main класса _2 от следов предыдущей неудачной редакции и вновь скомпилируем Solution1 командой Build.Rebuild Solution.

Новая попытка набора расширенного имени класса LibClasses._1 должна завершиться успешно! Наберем новую редакцию модуля CodeFile2 в виде

class _2

{

static void Main(string[] args)

{

LibClasses._1 inst_1;

System.Console.ReadLine();

}

}

Оператор

LibClasses._1 inst_1;

есть оператор описания ссылки на экземпляр (instance) класса LibClasses._1 с именем inst_1. Словосочетание «ссылка на экземпляр» часто будем замещать кратким термином «экземпляр».

У класса LibClasses._1 есть только один метод Method(). Он не имеет модификатора static, поэтому может вызываться только экземплярами класса. Попытаемся вызвать этот метод с помощью экземпляра inst_1. Для этого в следующей строке после описания экземпляра наберем имя inst_1. (с точкой). В списке IntelliSense нет имени Method!

2.5 Модификаторы доступа к членам класса

Дело в том, что у членов класса, в частности, у метода Method() есть также модификаторы доступа. Их разнообразие больше, чем у классов, описанных в пространстве имен. По умолчанию модификатором члена класса является private. Член класса, описанный с модификатором private, доступен лишь другим членам этого же класса и больше никому. При необходимости можно использовать модификатор internal. Тогда доступ члена класса будет распространяться на всю сборку, но не далее. Если модификатор доступа члена класса public и при этом сам класс имеет тот же уровень доступа, то член класса доступен всем. Это именно то, что нам нужно.

Сделаем новую редакцию библиотечного файла в виде

namespace LibClasses

{

public class _1

{

public void Method()

{

System.Console.WriteLine("Lib");

}

}

}

и вновь рекомпилируем Solution1 командой Build.Rebuild Solution.

Вернемся к файлу CodeFile2. Новая попытка набора inst_1. в строке после описания экземпляра должна завершиться успешно. Метод Method должен появиться в списке IntelliSense! Но…

2.6 Создание экземпляра

Добавление строки

inst_1.Method();

и новая компиляция приводят к ошибке компиляции «использование не присвоенной (unassigned) переменной inst_1».

Дело в том, что, описав ссылку на экземпляр inst_1 класса LibClasses._1, мы не создали сам экземпляр в памяти компьютера. Переменная inst_1 содержит пока что лишь ссылку в «никуда» (null). Для создания экземпляра следует добавить оператор вида

inst_1 = new LibClasses._1();

или заменить имеющееся описание экземпляра

LibClasses._1 inst_1;

другим

LibClasses._1 inst_1 = new LibClasses._1();

Так что весь код файла CodeFile2 из проекта MainExe примет вид

class _2

{

static void Main(string[] args)

{

LibClasses._1 inst_1 = new LibClasses._1();

inst_1.Method();

System.Console.ReadLine();

}

}

Скомпилируем Solution1 и дадим команду Debug.Start Debugging. В черном окне должна появиться надпись Lib.

Метод LibClasses._1(), который мы использовали в последней редакции, называется конструктором экземпляра класса. Конструктор экземпляра в языке C# имеет то же имя, что сам класс, и по умолчанию не имеет параметров.

Оператор new

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

Автоматическая сборка мусора (garbage collection)

Обычно, при динамическом использовании памяти от программиста требовалось следить за тем, чтобы занятая память вовремя освобождалась. В данном случае следовало бы освободить память от объекта inst_1 перед окончанием метода Main. Но при программировании в среде .NET освобождение памяти от объектов происходит автоматически!

2.7 Поля класса (field)

До сих пор единственные члены класса, которые мы использовали, были методы - статические методы Main и метод экземпляра Method().

Другими членами класса являются поля. Полями класса являются экземпляры объектов, описанных внутри класса, но вне его методов. Например, экземпляр inst_1 класса LibClasses._1 описан внутри класса _2, но внутри его метода Main. Поэтому inst_1 не является полем класса _2, а лишь локальной переменной метода Main.

Можно перенести строку LibClasses._1 inst_1 = new LibClasses._1();

с описанием экземпляра inst_1 наружу метода Main. Так, что новая редакция кода файла CodeFile2 примет вид

class _2

{

LibClasses._1 inst_1 = new LibClasses._1();

static void Main(string[] args)

{

inst_1.Method();

System.Console.ReadLine();

}

}

В этом случае inst_1 становится полем класса _2.

Но…, новая редакция кода содержит ошибку, которую сразу же обнаружит компилятор.

2.8 static vs. нестатический

Дело в том, что поле inst_1 является нестатическим полем класса _2. Оно примет какое-либо осмысленное значение лишь после создания экземпляра самого класса _2. Метод Main является статическим и его вызов не требует наличия экземпляра класса _2. Использование внутри статического метода нестатических членов класса (полей или методов) недопустимо и поэтому вызывает ошибку при компиляции.

В данном коде достаточно объявить поле inst_1 статическим, добавив в его описание модификатор static, чтобы сделать код осмысленным.

Новая редакция кода

class _2

{

static LibClasses._1 inst_1 = new LibClasses._1();

static void Main(string[] args)

{

inst_1.Method();

System.Console.ReadLine();

}

}

должна быть скомпилирована без ошибок.

2.9 Описание конструктора

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

Войдем в окно библиотечного файла LibCode. Добавим поле s класса string в класс _1 и конструктор с параметром типа string так, что новая редакция кода файла LibCode примет вид

namespace LibClasses

{

public class _1

{

string s; // описание нестатического поля s

//описание конструктора с одним параметром

public _1(string s)

{

this.s = s;

}

public void Method()

{

System.Console.WriteLine(s);

}

}

}

Двойной знак // (double slash) ограничивает область комментария от области кода в конце строки. Комментарий никак не воспринимается компилятором и пишется исключительно для пояснений кода.

Отметим правила описания конструктора экземпляра.

Имя конструктора должно совпадать с именем класса.

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

Обычно предполагается создавать экземпляры класса вне самого класса. Поэтому следует в описание конструктора добавить модификатор доступа, отличный от private (вспомним, что для членов класса модификатор private принимается по умолчанию).

Описание поля s не имеет модификатора доступа и, следовательно, имеет доступ private. Поле s доступно лишь методам класса - в частности конструктору класса и методу Method(), где и используется. Поле s является нестатическим, поэтому имеет смысл лишь как поле экземпляра, но не как поле класса.

this

В теле конструктора поле s записано как this.s, чтобы отличить от параметра конструктора, так же обозначенного s. Служебное слово this является ссылкой на текущий экземпляр (в Delphi экземпляр обозначается self). Например, при создании экземпляра inst_1, которым мы сейчас займемся, в this будет находиться значение inst_1.

Компиляция Solution1 при использовании новой редакции кода библиотечного класса LibClasses._1 приведет к ошибке в коде класса _2 проекта MainExe.

Дело в том, что, описав конструктор класса LibClasses._1 с параметром, мы автоматически отказались от существования конструктора без параметров, предлагаемого компилятором по умолчанию. Теперь в файле CodeFile2 в строке с описанием экземпляра inst_1 класса LibClasses._1 следует в качестве параметра конструктора подставить какую-нибудь строку. Сделаем это и активизируем новую редакцию кода проекта MainExe и библиотеки Lib.

class _2

{

static LibClasses._1 inst_1 = new LibClasses._1("Anything as string");

static void Main(string[] args)

{

inst_1.Method();

System.Console.ReadLine();

}

}

Результат должен быть ожидаемым. Строка

Anything as string

в черном окне.

Перегруженный конструктор (overloaded)

Можно описать произвольное число конструкторов одного класса с разным числом и разными типами параметров. Такое описание называется перегрузкой (overloading).

Например, в библиотечный класс LibClasses._1 можно добавить конструктор без параметров

public _1()

{

}

и воспользоваться этим конструктором (убрав из предыдущего кода ссылку на строку "Anything as string") при создании экземпляра inst_1 в классе _2 проекта MainExe. Проверьте работу новой версии кода. Результатом должно быть пустое черное окно.

При создании экземпляра конструктор обнуляет все поля. Поэтому в поле s, которое Method() должен вывести на экран, ничего нет (пустая строка).

2.10 Отладка проекта (debugging)

Будем считать, что код файла CodeFile2 имеет тот вид, в котором мы его оставили

class _2

{

static LibClasses._1 inst_1 = new LibClasses._1();

static void Main(string[] args)

{

inst_1.Method();

System.Console.ReadLine();

}

}

Пошаговое выполнение кода (Step Into, Step Over, Step Out, Run To Cursor)

Выберем команду Debug.Step Into. Указатель отладчика попадает на открывающую скобку блока { метода Main класса _2, а затем, после повторной команды Debug.Step Into, на первый оператор этого метода

inst_1.Method();

который будет выделенным цветом.

Подведем курсор к элементу inst_1. Среда даст комментарий вида

+|inst_1|{LibClasses._1}

Это означает, что inst_1 есть экземпляр класса LibClasses._1. Поставьте курсор на знак + и получите дополнительный комментарий. Это комментарий о полях экземпляра inst_1

s|null

так как у класса LibClasses._1 есть только одно нестатическое поле s и к моменту комментария его значение null.

Новый вызов команды Debug.Step Into переведет указатель отладчика внутрь метода LibClasses._1.Method(). Имя команды Step Into говорит о том, что в процессе пошаговой отладки проводится вход в каждый из встреченных методов.

Другая команда отладчика Debug.Step Over дает методу выполниться и передает управление оператору после выхода из метода. Чтобы проверить ее действие можно, находясь внутри метода Method(), вызвать в начале команду Debug.Step Out. Оказавшись вновь снаружи, дайте команду Debug.Step Over. Указатель отладчика должен передвинуться на строку

System.Console.ReadLine();

Если в любой момент пошаговой отладки выбрать команду Debug.Continue, то выполнение кода продолжится в обычном, не пошаговом режиме. Прервать пошаговую отладку или выполнение кода можно командой Debug.Stop Debugging. Возобновить выполнение кода с начала - командой Debug.Restart.

Ранее указывалось, что до входа в метод Main класса _2 должен быть выполнен оператор

static LibClasses._1 inst_1 = new LibClasses._1();

Но отладчик на нем не задерживается.

Чтобы все-таки войти в этот оператор, можно использовать еще одну команду отладчика Run To Cursor (из контекстного меню), установив предварительно каретку в область текста

static LibClasses._1 inst_1 = new LibClasses._1();

Помещая указатель мышки на текст inst_1, можно узнать, что экземпляр inst_1 имеет в данной точке значение null.

Точки прерывания (breakpoints) и другие возможности отладчика

Процесс отладки можно запрограммировать, используя так называемые точки прерывания (breakpoints). Любая строка кода может быть помечена как точка прерывания. Для этого достаточно щелкнуть на левом поле строки. Строка будет выделена цветом и слева появится кружок - символ точки прерывания. Теперь, после запуска кода отладчик остановит выполнение в этой точке. Число точек прерывания может быть любым. Попробуйте установить несколько точек прерывания в имеющемся проекте и проверьте их действие. Убирается точка прерывания командой Delete Breakpoint из контекстного меню на символе точки или «кликом» по самой точке.

Преимущество точек прерывания по сравнению с командой Run To Cursor в том, что

можно программировать условия, при которых в данной точке следует приостановить выполнение кода;

можно указать, через какое число проходов через данную точку выполнение должно прерваться;

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

В процессе отладки из меню Debug.Windows можно открывать окна, расширяющие возможности отладки. Например,

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

в окнах Watch дополнительно можно формулировать простые выражения из локальных переменных, оценивая их результат.

если возникает необходимость формулировать дополнительные участки кода с требованием непосредственного выполнения, следует открыть Immediate Window.

Вопросы для самоконтроля

Модификаторы доступа к классам и членам класса.

Описание экземпляра класса и его создание.

Оператор new. Автоматическая сборка мусора.

Поля класса. Статический vs нестатический.

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

Что такое this?

Отладка проекта (команды Step Into, Step Over, Step Out, Run To Cursor, точки прерывания).

Занятие 3. Классы и объекты в C#

Эффективное программирование, особенно в C#, оказывается затруднительным без понимания этих терминов.

Термин объект в самом общем понимании есть область памяти со своим адресом, конкретным объемом и форматом.

Есть два типа данных, с которыми оперирует процессор - адрес объекта и содержимое объекта. Адрес в зависимости от процессора является 32 или 64-битовым (4-ех или 8-ми байтовым) целым числом. Адрес можно считать внутренним представлением ссылки на объект (reference), а содержимое - значением (value) объекта. Содержимое объекта в соответствии с его форматом состоит из его полей (field). Поля находятся в памяти, начиная с адреса объекта в том объеме и формате, в котором они представлены в описании типа объекта.

Тип класса является объектом в том смысле, что он имеет адрес и занимает память. Полями объекта «тип класса» является таблица виртуальных методов и его статические поля. Примерами объекта «тип класса» является LibClasses._1 в библиотеке Lib и классы _1 и _2 в проекте MainExe.

3.1 Статический конструктор и статический класс

Статический конструктор инициализирует тип класса. Внутренне ссылка на тип класса есть адрес той области памяти, где хранятся статические поля класса (включая VMT типа).

Статический конструктор вызывается средой .NET

либо при первом обращении к статическому полю,

либо перед созданием экземпляра класса,

и никогда не вызывается явно. Больше одного раза статический конструктор не вызывается.

Непосредственно перед вызовом статического конструктора статическим полям присваиваются начальные значения, если последние заданы. Например, в классе _2 проекта MainExe есть статическое поле inst_1, которое в последней редакции инициализируется конкретным значением.

static LibClasses._1 inst_1 = new LibClasses._1();

Это происходит непосредственно перед вызовом статического конструктора класса _2. Чтобы убедиться в этом добавьте в блок с членами класса _2 описание статического конструктора в виде

static _2()

{

}

После чего поставьте каретку в область текста строки

static LibClasses._1 inst_1 = new LibClasses._1();

и выберите команду Run To Cursor из контекстного меню. Далее двигайтесь командой Debug.Step Into.

Необходимости в описании статического конструктора обычно не возникает - работает конструктор по умолчанию. У статического конструктора (его еще называют конструктором класса)

не должно быть параметров,

он не должен возвращать какой-либо тип,

его имя должно совпадать с именем класса,

у статического конструктора должен быть модификатор static,

не должно быть модификаторов доступа (типа public)

Статический класс

В языке C# можно описать статический класс, который содержит только статические члены. Статический класс не может создавать экземпляры и имеет модификатор static в описании.

3.2 Нестатический класс и его экземпляры

Нестатический класс может создавать экземпляры. Создает экземпляр класса оператор new с последующим вызовом конструктора экземпляра. Оператор new размещает экземпляр в памяти компьютера и придает ссылке на экземпляр осмысленное (не равное null) значение. Конструктор экземпляра при этом помещает в специальное (не описываемое программистом) поле экземпляра адрес VMT, т.е. адрес таблицы виртуальных методов в объекте «тип класса», экземпляр которого создается. Строка кода

static LibClasses._1 inst_1 = new LibClasses._1();

является тому примером. Экземпляр inst_1 класса LibClasses._1 создается оператором new с участием конструктора LibClasses._1().

Содержимым (значением) экземпляра класса являются нестатические поля.

Нестатические поля могут иметь инициализаторы. Например, поле s класса LibClasses._1 может быть описано в форме

string s="a string";

Код выполняет инициализацию нестатических полей непосредственно перед вызовом конструктора экземпляра. Проверьте, используя команды отладчика.

Неинициализированным нестатическим полям класса конструктор экземпляра присваивает значения по умолчанию (default). Это 0, "", false или null в зависимости от типа поля.

3.3 Ссылочный тип

Все типы классов разделены на две категории в зависимости от того, какую информацию содержат в себе экземпляры (переменные) данного типа

ссылку

или

значение.

Будем называть кратко классы первой категории «ссылочный тип» (reference type) , а второй - «тип-значение» (value type).

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

Примерами классов ссылочного типа, которые нам уже встречались, являются пользовательские классы, описываемые служебным словом class, и встроенный класс string. Служебное слово string является псевдонимом библиотечного класса System.String. К встроенным ссылочным типам относятся также interface - класс с нереализованными методами, delegate - класс, содержащий ссылки на методы, и встроенный класс object (псевдоним библиотечного класса System.Object).

Переменная экземпляра типа-значения размещается в стеке, либо непосредственно в области кода программы (inline). Содержимым этой переменной являются сами нестатические поля класса.

Существенным отличием двух типов классов является то, что при присвоении одной переменной (экземпляра) класса ссылочного типа другой копируется только ссылка, но не содержимое экземпляра. При той же операции присвоения для типов-значений копируется все содержимое - все поля экземпляра. Другими словами присвоение одной переменной типа-значения другой приводит к неявному созданию нового экземпляра соответствующего класса и копированию в него полей исходного экземпляра.

Объекты классов типов-значений обычно требуют для хранения сравнительно небольшие объемы памяти.

Это

классы, описываемые служебным словом struct, - пользовательские структуры;

встроенные представители типа struct:

целые типы

однобайтовые: целые со знаком sbyte и без знака byte - псевдонимы классов System.SByte и System.Byte;

2-байтовые: символы char, целые со знаком short и без знака ushort - псевдонимы классов System.Char, System.Int16 и System.UInt16;

4-байтовые: со знаком int и без знака uint - псевдонимы классов System.Int32 и System.UInt32;

8-байтовые: со знаком long и без знака ulong - псевдонимы классов System.Int64 и System.UInt64;

типы с плавающей запятой (floating-point type)

4-байтовый float - псевдоним класса System.Single;

8-байтовый double - псевдоним класса System.Double:

16-байтовый тип decimal (псевдоним класса System.Decimal), используемый в финансовых расчетах;

логический тип bool - псевдоним класса System.Boolean;

и тип enum - перечислимый тип (нумератор), элементы которого имеют в качестве базовых классов любой целый тип за исключением char.

Экземпляры типов-значений можно перевести в экземпляр ссылочного типа (так называемый boxing - упаковка) и вернуть назад в тип-значение (unboxing - распаковка). Пусть, например, описан экземпляр типа-значения char ch='a'. Упаковка состоит в описании экземпляра ссылочного типа и размещении в нем экземпляра типа-значения object och=ch. При этой операции в динамической памяти (куче, или heap) создается копия символа 'a' и ссылка на эту копию помещается в переменную och. Обратная операция распаковки сводится к описанию экземпляра объекта ch1 типа char и размещению в нем содержимого ссылки och с помощью оператора преобразования типа (char)

char ch1=(char)och.

3.4 Указатели (pointers)

Отдельной категорией типов в C# являются указатели (pointer). Это адреса объектов типов-значений. Только объекты типов-значений и только те, у которых нет полей ссылочного типа (так называемые «неуправляемые типы» - unmanaged types), могут быть поставлены под указатели. Указатели не являются классами. Блок кода или метод, в котором применяются указатели, должен предваряться служебным словом unsafe (небезопасный). При компиляции проекта с небезопасным кодом в свойствах проекта на странице Build должен быть поднят флажок Allow unsafe code.

Наберите для примера код с использованием указателя так, чтобы метод Main класса _2 проекта MainExe имел вид

int i = 256;

unsafe

{

byte* pb = (byte*)&i;

System.Console.WriteLine(*pb);

}

System.Console.ReadLine();

Выполните эту версию проекта MainExe.

В строке

byte* pb = (byte*)&i;

описан указатель pb на тип byte;

указатель pb инициализирован значением адреса & переменной целого типа i;

так как адрес &i относится к типу int* - «указатель на целое», то оператором (byte*) проводится его преобразование в «указатель на байт».

Результатом должен быть байт *pb , «стоящий под указателем» pb. В данном случае это число 0, так как младший (первый) байт целого числа 256 равен нулю, а старший - единице.

В отличие от ссылочного типа указатель существует сам по себе как число, независящее от объекта, на который он «указывает». Поэтому указатели можно складывать как целые числа. Например, указатель pb++ будет содержать адрес на единицу больший pb.

В вышеприведенном примере кода вместо WriteLine(*pb)распечатайте *++pb, затем pb[0] и pb[1]. Указатель ++pb равносилен указателю pb+1 и несет адрес второго байта числа i. Выражения pb[0]и pb[1] равносильны *pb и *(pb+1). Значение *++pb и pb[1]при i=256 должно быть 1. Замените 256 на 512. Посмотрите результат.

Если объекту отвечает некоторый указатель и этот объект оказывается уничтоженным автоматической «сборкой мусора», то адрес указателя теряет смысл. Чтобы избежать такой ситуации в C# применяется оператор fixed, «замыкающий» доступ «сборщика мусора» к объекту в блоке кода, в котором определен и действует указатель на этот объект.

Другой способ избежать сборки мусора при работе с указателями позволяет зарезервировать область стека достаточного объема для хранения данных под указателем. Оператор, резервирующий n байт памяти под указатель buffer, имеет вид byte* buffer = stackalloc byte[n];. Используется служебное слово stackalloc. Такое резервирование возможно лишь внутри блока unsafe. Например, в методе static unsafe void DoSmth(int n)

{

byte* buffer = stackalloc byte[n];

//Код, работающий с буфером buffer

}

Область buffer будет освобождена (возвращена в «кучу») только по окончанию метода DoSmth.

3.5 Наследование (inheritance)

Вернемся к проекту MainExe, к описанию класса _2. В методе Main добавьте строку

LibClasses._1.

В этой точке IntelliSense покажет список, состоящий из двух статических методов Equals и ReferenceEquals. Другими словами, класс LibClasses._1 содержит два статических метода с указанными именами. Но откуда эти методы? Ни один из них явно не описывался в классе LibClasses._1!

Замените добавленную строку другой

string.

На этот раз IntelliSense покажет более длинный список статических членов класса System.String. Среди них есть те же два метода Equals и ReferenceEquals, что и у класса LibClasses._1.

Попробуйте еще строку

double.

Теперь будет отображен список статических членов класса System.Double и в нем встретятся те же два метода Equals и ReferenceEquals.

Одним из важнейших качеств объектно-ориентированного подхода в программировании является способность классов наследовать код других классов. Класс-наследник (derived class) может наследовать любые члены класса-предка (base class) - методы, поля и т.д. Это означает, что класс-наследник использует наследуемые члены класса-предка, как свои собственные.

В языке C# каждый класс может иметь не более одного класса-предка. Но у класса-предка может быть свой предок и т.д. В основе всех классов, используемых в .NET, лежит один общий предок - класс System.Object. Прямым предком описанного нами класса LibClasses._1 является именно класс System.Object. Псевдонимом класса System.Object является служебное слово object.

Наследование и вызов конструктора

Еще раз рассмотрим класс _2 и действие в нем оператора

static LibClasses._1 inst_1 = new LibClasses._1();

Установите каретку в область текста этого оператора. Дайте команду отладчика Run To Cursor (например, из контекстного меню). После того, как отладчик выделит строку с этим оператором, поместите указатель мышки на имя _1 конструктора класса LibClasses._1, стоящего после оператора new. Тогда получите информацию вида

+LibClasses._1|LibClasses._1

Указав на +, получите еще один комментарий

base {object}|object

Смысл этого комментария в том, что базовым классом (или, предком) класса LibClasses._1 является класс object и его конструктор с тем же именем object вызывается каждый раз при вызове конструктора LibClasses._1.

Чтобы убедиться в этом лишний раз, добавьте в пространстве имен LibClasses библиотечного файла LibCode новый класс _1Der с описанием

public class _1Der : _1

{

}

Это будет класс _1Der - наследник класса _1. Класс _1Der пуст. Но, по умолчанию, имеет конструктор с тем же именем и без параметров. Чтобы увидеть это, добавьте в классе _2 после строки

static LibClasses._1 inst_1 = new LibClasses._1();

строку

static LibClasses._1Der inst_1Der = new LibClasses._1Der();

Так будет описан и создан экземпляр класса _1Der, наследующего класс _1.

Установите каретку в область текста этого оператора. Дайте, как и раньше, команду отладчика Run To Cursor (из контекстного меню). После того, как отладчик выделит строку с этим оператором, поместите указатель мышки на имя _1Der конструктора класса LibClasses._1Der, стоящего после оператора new. Тогда получите информацию вида

+LibClasses._1Der|LibClasses._1Der

Указав на +, получите комментарий о базовом классе

+base {LibClasses._1}|LibClasses._1

Вновь указав на +, получите новый комментарий

base {object}|object

Теперь убедитесь, что конструктор базового класса вызывается всегда, когда вызывается конструктор наследника. С этой целью конструктор базового класса _1 (файл библиотеки LibCode) перепишите так, чтобы он имел вид

public _1()

{

System.Console.WriteLine("Base constructor");

}

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

Это связано с тем, что конструктор класса _1 вызывается дважды - строкой кода

static LibClasses._1 inst_1 = new LibClasses._1();

в классе _2, и следующей за ней строкой кода

static LibClasses._1Der inst_1Der = new LibClasses._1Der();

Последняя строка создает экземпляр inst_1Der класса-наследника _1Der. Но при этом конструктор класса _1Der вызывает неявно конструктор своего предка, или базового класса _1. Проверьте ход выполнения кода с помощью команд отладчика Run To Cursor и Step Into.

3.6 Методы Equals и ReferenceEquals

Класс System.Object имеет два статических метода Equals и ReferenceEquals,которые наследуются всеми классами .NET. Оба этих метода содержат два параметра типа object (псевдоним класса System.Object) и возвращают логическое значение true или false (типа bool) в зависимости от того, равны ли экземпляры объектов-параметров (метод Equals) и равны ли ссылки на эти экземпляры (метод ReferenceEquals).

Сотрите либо закомментируйте в конструкторе класса _1 строку

System.Console.WriteLine("Base constructor");

Опишите внутри метода Main класса _2 новый экземпляр inst_1a класса LibClasses._1 и сравните два имеющихся экземпляра inst_1 и inst_1a так, чтобы новый код класса _2 имел вид

class _2

{

static LibClasses._1 inst_1 = new LibClasses._1();

static void Main(string[] args)

{

LibClasses._1 inst_1a = new LibClasses._1();

System.Console.WriteLine(

LibClasses._1.ReferenceEquals(inst_1, inst_1a));

System.Console.ReadLine();

}

}

Результатом выполнения кода будет False.Этот результат вполне понятен. Ссылки на два различных экземпляра класса LibClasses._1 не совпадают.

Замените метод ReferenceEquals методом Equals. Результат будет прежний. Вот это уже не понятно! Ведь поля s экземпляров inst_1 и inst_1a одинаковы?!

Объяснение в том, что по умолчанию метод Equals класса System.Object, как и всех ссылочных типов, сравнивает только ссылки, как и ReferenceEquals. Однако наследники класса System.Object могут изменять поведение метода Equals.

В качестве упражнения воспользуемся этой возможностью, изменив поведение метода Equals при сравнении экземпляров нашего класса LibClasses._1.

3.7 Виртуальные методы (virtual)

Наберите inst_1. (с точкой). В этом случае IntelliSense отображает список доступных нестатических членов экземпляра класса LibClasses._1. Среди них лишь один метод Method описан нами явно в классе. Остальные методы Equals, GetHashCode, GetType и ToString унаследованы от класса System.Object:

метод GetHashCode возвращает целое число типа int, «нумерующее» некоторым, вообще говоря, произвольным образом, экземпляры класса данного типа,

метод GetType возвращает экземпляр объекта типа System.Reflection.Type, «отражающего» (отсюда reflection) в своих методах и свойствах все характеристики того типа класса, экземпляром которого является вызывающий объект,

метод ToString дает «читабельное» описание экземпляра объекта - имя класса для ссылочных типов (кроме string) и значение для типов-значений. Проверьте действие этого метода, набрав в методе Main класса _2 дополнительную строку

System.Console.WriteLine(inst_1.ToString());

Результатом должна быть строка LibClasses._1. То же и при вызове метода ToString другим экземпляром inst_1a.ToString().

Рассмотрим более внимательно нестатический метод Equals.

По названию он совпадает с уже рассмотренным статическим методом, но имеет только один параметр типа object, а не два, как его статический тезка. Чтобы проверить работу нестатической версии Equals, внесите изменение в код класса _2

class _2

{

static LibClasses._1 inst_1 = new LibClasses._1();

static void Main(string[] args)

{

LibClasses._1 inst_1a = new LibClasses._1();

System.Console.WriteLine(inst_1.Equals(inst_1a));

System.Console.ReadLine();

}

}

Здесь для сравнения экземпляров inst_1 и inst_1a класса LibClasses._1 вместо статического метода Equals вызывается его нестатический тезка. Он также сравнивает экземпляры inst_1 и inst_1a. Результат, как и в предыдущем варианте кода, будет False. Опять же, по умолчанию, нестатический вариант метода Equals класса System.Object сравнивает объекты по ссылке.

Изменим содержание класса LibClasses._1 в модуле LibCode, добавив в него описание нового нестатического метода Equals

public override bool Equals(object obj)

{

return s == ((_1)obj).s;

}

Комментарии к коду

Служебное слово override означает, что метод Equals является виртуальным (virtual) и уже описан в методе-предке. При первом описании виртуального метода (в данном случае, в описании нестатической версии метода Equals в классе System.Object) его описание содержит служебное слово virtual. Во всех наследниках, перекрывающих описание виртуального метода, термин virtual заменяется override.

Оператор return уже встречался нам в описании некоторых версий метода Main. Тело нашей версии метода Equals включает служебное слово return. Любой метод, возвращающий объект какого-либо типа, отличного от void, (в нашем случае это тип bool) обязательно должен содержать оператор return. Выражение, следующее за оператором return должно быть того типа, который указан в описании метода. Оператор return прекращает исполнение метода, даже если этот оператор не является последним в теле метода. В методе типа void оператор return может быть опущен. Но если return присутствует в методе типа void, то дальше не должно следовать какое-либо выражение и в этой точке прекращается выполнение метода.

Логическое выражение. Выражение s == ((_1)obj).s возвращает значение типа bool и является логическим выражением. В данном случае в нем сравниваются две строки - поле s текущего экземпляра объекта и поле s объекта-параметра obj. Результатом будет true при совпадении строк и false при их отличии.

Предполагается, что при вызове метода Equals значением объекта-параметра obj будет экземпляр класса LibClasses._1 или его наследника. Объект-параметр obj имеет тип object. У класса object нет поля s. Поэтому перед вызовом поля s проводится приведение типа объекта obj к объекту типа LibClasses._1.

После проведенной редакции проверим результат выполнения кода. Он будет True. Переопределение (overriding) виртуального метода Equals принесло свои плоды. Более того, если, возвращаясь к коду класса _2, заменить вызов нестатического метода inst_1.Equals(inst_1a) вызовом статического метода LibClasses._1.Equals(inst_1, inst_1a), результат будет также True. Это говорит о том, что внутри статического метода Equals класса System.Object, который мы не переопределяли (!), вызывается его нестатический тезка, нами переопределенный. В этом могущество виртуальных методов. Переопределив виртуальный нестатический метод Equals, мы изменили результат работы кода, который не редактировали и который нами даже не контролируется - кода статического метода Equals!

Недостатки кода.

Заметим, что описанный нами метод Equals класса LibClasses._1, имеет как минимум два недостатка.

Если обратиться к методу Equals с параметром null, то есть испытать код inst_1.Equals(null), то возникнет ошибка времени выполнения (проверьте). В методе Equals предполагается, что параметр obj имеет осмысленное значение, не равное null. Если это не так, метод Equals, очевидно, должен возвращать false. Ведь вызывающий объект, раз он существует, не может иметь ссылку null.

Если обратиться к методу Equals, подставив в качестве параметра ссылку на объект, не являющийся экземпляром класса LibClasses._1 или его наследника, то опять возникнет ошибка времени выполнения. Ведь использование оператора приведения типа (_1)obj в коде метода Equals предполагает, что параметр obj приводится к типу LibClasses._1. Если это не так, то неизбежна ошибка. Чтобы убедиться в этом, опишите перед обращением к методу Equals строку с новым объектом класса, отличного от LibClasses._1, например, int i = 0; и проверьте выполнение метода Equals в форме inst_1.Equals(i).

Для исправления кода содержание метода Equals следует переписать в виде

return obj!=null && GetType()==obj.GetType() && s == ((_1)obj).s;

Сделайте это и вновь проверьте работу кода в двух предыдущих ситуациях.

Комментарии к новой версии кода

Оператор obj!=null возвращает true, если объекты не равны, и false в противном случае.

Служебные символы && (двойной амперсанд) описывают операцию логического умножения «и» (and) - возвращает true, только если оба операнда равны true. При этом если логическое выражение obj!=null вернет значение false, дальнейшая проверка будет приостановлена. То же, если следующее логическое выражение GetType()==obj.GetType() вернет false. Благодаря этому свойству оператор && относится к так называемым шунтирующим (short-circuit) операторам. Другая версия оператора логического умножения & (одиночный амперсанд) таким свойством не обладает и требует вычисления всех операндов вне зависимости от их результатов. Если в нашем выражении заменить шунтирующий оператор && на обычный &, то код будет выходить на прежние ошибки времени выполнения при обращениях inst_1.Equals(null) и inst_1.Equals(i). Испытайте и подумайте, почему это так.

Кроме логической операции «и» с операторами & и &&, в C# реализованы еще две бинарные (с двумя операндами) логические операции:

«или» (логическое сложение, или or) с операторами | и || возвращает true, если хотя бы один из операндов true

операция симметрической разности (xor - исключающее «или», или eXclusive OR) с оператором ^ возвращает true, если только один из операндов true.

В качестве унарного (действующего на один операнд) оператора отрицания используется восклицательный знак !.

Операторы &, | и ^ могут применяться к целым операндам. Тогда они действуют побитовым образом - оперируют над отдельными парами битов, из которых состоят целые числа-операнды в бинарном представлении. Унарный оператор ~ проводит «побитовое дополнение» целого операнда (возвращает число, которое в сумме с операндом возвращало бы все биты, равными единице).

Логическое выражение GetType()==obj.GetType() возвращает true только, если тип класса вызывающего объекта и тип класса объекта-параметра obj совпадают. Можно было бы изменить условие, накладываемое на типы объектов, потребовав лишь, чтобы параметр obj был объектом класса LibClasses._1 или его наследника. Тогда выражение obj!=null && GetType()==obj.GetType() достаточно было бы заменить выражением obj is _1. Это выражение возвращает true, если параметр obj не равен null и если его тип «сводится» к типу LibClasses._1 (является экземпляром этого типа или его наследника). Попробуйте эту версию кода в разных вариантах вызова метода Equals. Однако надо понимать, что при использовании последнего варианта кода метода Equals (с оператором is) равными окажутся объекты разных типов (хоть и имеющие общего предка LibClasses._1) с одинаковыми полями s. Поэтому первоначальный вариант кода

return obj!=null && GetType()==obj.GetType() && s == ((_1)obj).s;

предпочтительней.

3.8 Наследование (синтаксис)

Наследование расширяет число типов, к которым относятся объекты класса-наследника - эти объекты имеют тип самого класса-наследника и всех его предков.

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

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

Один способ «прячет» унаследованное поле или метод. Для этого описывается свое поле с тем же именем и того же типа или свой метод с тем же именем и сигнатурой и в описание добавляется служебное слово new.

Например, в классе-предке Base описано поле aField типа type и метод aMethod

public type aField;

public void aMethod();

В классе-наследнике Der можно описать

public new type aField;

public new void aMethod();

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

При этом способе переопределенные поля и методы предка будут доступны из объекта DerInst класса-наследника Der путем преобразования типов. То есть, операторы ((Base) DerInst).aField, ((Base)DerInst).aMethod() будут обращаться к полю aField класса Base и, соответственно, вызывать метод aMethod() также класса Base.

...

Подобные документы

  • Концепция объектно-ориентированного программирования. Объектно-ориентированные языки программирования: Smalltalk, Object Pascal, CLOS и C++. Понятие "Объект" и "Класс". Управление доступом к элементам данных классов. Определение функций-членов класса.

    реферат [24,5 K], добавлен 28.10.2011

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

    курсовая работа [483,1 K], добавлен 07.04.2014

  • Изучение структуры доменных имен и описание возможностей их системы по использованию символьных наименований узлов в IP-сетях. Записи ресурсов домена и функции сети по расширению имен и зон обратного просмотра. Делегирование ответственности за домены.

    презентация [104,2 K], добавлен 25.10.2013

  • Оценка функциональных возможностей стандартных классов представления данных на примерах использования избранных методов ("detect: ifNone:" класса Set, "to:by:do:" класса Number и "copy: ReplaceFrom: to: with:" класса OrderedCollection), их тестирование.

    лабораторная работа [1,1 M], добавлен 14.10.2012

  • Создание класса wind, характеризующего ветровой режим, и программы, демонстрирующей применение разработанного класса. Программный модуль на языке программирования C++ в среде программирования C++Builder6/0, демонстрирующая работу с классом wind.

    курсовая работа [123,5 K], добавлен 24.06.2010

  • Изучение принципов объектно-ориентированного программирования, в котором основными концепциями являются понятия классов и объектов. Свойства этого вида программирования: инкапсуляция, полиморфизм, наследование. Описание класса. Конструкторы и деструкторы.

    презентация [74,8 K], добавлен 14.10.2013

  • Механизм классов в C++. Инициализация внутреннего объекта с помощью конструктора. Управление доступом к классу. Защищенные члены класса. Графические средства компилятора Borland C 3.1. Библиотека стандартных шаблонов. Реализация и использование класса.

    курсовая работа [2,7 M], добавлен 16.05.2012

  • Разработка программы, выполняющей обработку базы данных, элементами которой являются объекты класса Student. Организация пользовательского диалога для ввода информации и просмотра объектов. Определение классов и глобальных имен. Инструкция программиста.

    контрольная работа [18,4 K], добавлен 13.10.2013

  • Освоение методики проектирования программных комплексов на базе объектно-ориентированного программирования. Описание понятий класс, конструктор и деструктор, наследование простое и множественное. Реализация объектной модели на языке программирования с++.

    курсовая работа [468,5 K], добавлен 11.12.2011

  • Причины возникновения объектно-ориентированного программирования. Графическое представление классов; их отличия от других абстрактных типов данных. Типы абстракции, используемые при построении объекта. Сущность инкапсуляции, наследования и полиморфизма.

    контрольная работа [222,1 K], добавлен 04.06.2014

  • Основные понятия объектно-ориентированного программирования в PHP5. Структурный и объектно-ориентированный подход. Класс как абстрактный тип. Реализация класса. Конструкторы и деструкторы. Функция l_visited_style изменение стиля посещенных ссылок.

    курсовая работа [433,2 K], добавлен 13.06.2008

  • Разработка структуры класса "Экран курсового проектирования", которая будет основой для хранения информации о студентах, выполняющих курсовые работы. Реализация визуального приложения для тестирования иерархии классов на языке программирования С++.

    курсовая работа [3,3 M], добавлен 18.03.2011

  • Компьютерные сети: основные понятия, преимущества, проблемы, история развития. Разработка технологии межсетевого взаимодействия. Протоколы, службы и сервисы, мировая статистика Интернета. Адресация узлов сети. Система доменных имен. База данных DNS.

    презентация [3,9 M], добавлен 25.11.2013

  • Создание программы для обработки информации об объектах предметной области "Бытовая техника" в среде визуального программирования C++. Иерархия родственных классов. Описание логической структуры программы. Реализация файлового ввода/вывода данных.

    курсовая работа [711,4 K], добавлен 27.07.2014

  • Принципы работы с графикой средствами GDI+. Пространство имен и служебные типы System. Drawing. Возможности класса Graphics. Использование программного компонента "Таймер". Графическое Windows-приложение "Созвездие", его структура и элементы, функции.

    курсовая работа [348,0 K], добавлен 03.03.2016

  • Изучение принципов объектно-ориентированного программирования. Понятие класса в Delphi, в основе которых лежат три фундаментальные принципы - инкапсуляция, наследование и полиморфизм. Разработка классов транспортных средств и структур классов (кошки).

    курсовая работа [29,7 K], добавлен 29.10.2011

  • Назначение и сущность системы доменных имен (DNS) и службы имен Интернет для Windows (WINS). Запросы, зоны и инструменты DNS. Служебные программы командной строки. Установка и настройка DNS-сервера. Записи ресурсов узлов, псевдонимов и размещения службы.

    презентация [553,6 K], добавлен 10.11.2013

  • Классы, объекты и объектные ссылки. Особенности статических методов. Конструкторы, специальные переменные, наследование. Создание объектов внутренних классов. Соглашения об именовании. Некоторые методы класса Object. Абстрактные классы и атрибуты.

    лекция [130,6 K], добавлен 21.06.2014

  • Описание классов данных. Основное меню программы. Добавление и удаление объектов. Вывод устройств хранения в указанном ПК. Устройство хранения, класс ноутбук, класс жёсткий диск, класс персональный компьютер, класс планшет и класс Flash-память.

    курсовая работа [1,8 M], добавлен 30.03.2014

  • Понятия шаблонов функции и класса, правила описания на языке С++. Разработка и отлаживание в среде программирования программ, содержащих шаблоны функций и классов. Шаблон функции square, возвращающей квадрат переменной. Создание шаблона класса массива.

    лабораторная работа [162,6 K], добавлен 25.05.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.