Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Массивы и строки. Оператор foreach





25. Классы, создание объектов.

C# является языком объектно-ориентированного программирования, поэтому классы играют в нем основополагающую роль. Более того, все типы данных C#, как встроенные, так и определенные пользователем, порождены от базового класса object. Иными словами, в отличие от Java, где примитивные типы данных отделены от объектных типов, все типы данных в C# являются классами и могут быть разделены на две группы:

· ссылочные (reference types);

· обычные (value types).

Внешне ссылочные и обычные типы очень похожи, так как аналогично Cи++ в них можно объявлять конструкторы, поля, методы, операторы и т.д. Однако, в отличие от Cи++, обычные типы в C# не позволяют определять классы и не поддерживают наследования. Они описываются с помощью ключевого слова struct и в основном используются для создания небольших объектов. Ссылочные же типы описываются с помощью ключевого слова class и являются указателями, а экземпляры таких типов ссылаются на объект, находящийся в куче (heap). Продемонстрируем сказанное на примере:

using System;

class CValue

{

public int val;

public CValue(int x) {val = x;}

}

class Example_1

{

public static void Main()

{

CValue p1 = new CValue(1);

CValue p2 = p1;

Console.WriteLine(”p1 = {0}, p2 = {1}”,

p1.val, p2.val);

p2.val = 2;

Console.WriteLine(”p1 = {0}, p2 = {1}”,

p1.val, p2.val);

}

}

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

p1 = 1, p2 = 1p1 = 2, p2 = 2

Как нетрудно видеть, p2 является всего лишь ссылкой на p1. Тем самым становится очевидно, что при изменении поля val экземпляра класса p2 в действительности изменяется значение соответствующего поля p1. Подобный подход не очень удобен при работе с примитивными типами данных, которые должны содержать само значение, а не ссылку на него (Complex, Point, Rect, FileInfo и т.д.). Для описания таких объектов и предназначены типы значений:

using System; struct SValue{ public int val; public SValue(int x) {val = x;}} class Example_2{ public static void Main(){SValue p1 = new SValue(1);SValue p2 = p1;Console.WriteLine(”p1 = {0}, p2 = {1}”,p1.val, p2.val);p2.val = 2;Console.WriteLine(”p1 = {0}, p2 = {1}”,p1.val, p2.val);}}

Вот что получится после запуска вышеприведенной программы:

p1 = 1, p2 = 1p1 = 1, p2 = 2

Из этого следует, что экземпляр класса p2 является самостоятельным объектом, который содержит собственное поле val, не связанное с p1. Использование обычных типов позволяет избежать дополнительного расходования памяти, поскольку не создаются дополнительные ссылки, как в случае с экземплярами классов. Конечно, экономия невелика, если у вас имеется всего несколько небольших объектов типа Complex или Point. Зато для массива, содержащего несколько тысяч таких элементов, картина может в корне измениться. В таблице приведены основные отличия типов class и struct.

Методы класса и их параметры.

Свойства класса.

С точки зрения пользователя, свойства выглядят практически так же, как и обычные поля класса. Им можно присваивать некоторые значения и получать их обратно. В то же время свойства имеют бо,льшую функциональность, так как чтение и изменение их значений выполняется с помощью специальных методов класса. Такой подход позволяет изолировать пользовательскую модель класса от ее реализации. Поясним данное определение на конкретном примере:

using System;using System.Runtime.InteropServices;class Screen{[DllImport(”kernel32.dll”)]static extern bool SetConsoleTextAttribute(int hConsoleOutput, ushort wAttributes);[DllImport(”kernel32.dll”)]static extern int GetStdHandle(uint nStdHandle);const uint STD_OUTPUT_HANDLE = 0x0FFFFFFF5;static Screen(){output_handle = GetStdHandle(STD_OUTPUT_HANDLE);m_attributes = 7;}public static void PrintString(string str){Console.Write(str);}public static ushort Attributes{get{return m_attributes;}set{m_attributes = value;SetConsoleTextAttribute(output_handle, value);}}private static ushort m_attributes;private static int output_handle;}class Example_4{public static void Main(){for (ushort i = 1; i < 8; i++){Screen.Attributes = i;Screen.PrintString(”Property Demo\n”);}}}

Программа выводит сообщение «Property Demo», используя различные цвета символов (от темно-синего до белого). Давайте попробуем разобраться в том, как она работает. Итак, сначала мы импортируем важные для нас функции API-интерфейса Windows: SetConsoleTextAttribute и GetStdHandle. К сожалению, стандартный класс среды.NET под названием Console не имеет средств управления цветом вывода текстовой информации. Надо полагать, что корпорация Microsoft в будущем все-таки решит эту проблему. Пока же для этих целей придется воспользоваться службой вызова платформы PInvoke (обратите внимание на использование атрибута DllImport). Далее, в конструкторе класса Screen мы получаем стандартный дескриптор потока вывода консольного приложения и помещаем его значение в закрытую переменную output_handle для дальнейшего использования функцией SetConsoleTextAttribute. Кроме этого, мы присваиваем другой переменной m_attributes начальное значение атрибутов экрана (7 соответствует белому цвету символов на черном фоне). Заметим, что в реальных условиях стоило бы получить текущие атрибуты экрана с помощью функции GetConsoleScreenBufferInfo из набора API-интерфейса Windows. В нашем же случае это несколько усложнило бы пример и отвлекло от основной темы.

В классе Screen мы объявили свойство Attributes, для которого определили функцию чтения (getter) и функцию записи (setter). Функция чтения не выполняет каких-либо специфических действий и просто возвращает значение поля m_attributes (в реальной программе она должна бы возвращать значение атрибутов, полученное с помощью все той же GetConsoleScreenBufferInfo). Функция записи несколько сложнее, так как кроме тривиального обновления значения m_attributes она вызывает функцию SetConsoleTextAttribute, устанавливая заданные атрибуты функций вывода текста. Значение устанавливаемых атрибутов передается специальной переменной value. Обратите внимание на то, что поле m_attributes является закрытым, а стало быть, оно не может быть доступно вне класса Screen. Единственным способом чтения и/или изменения этого метода является свойство Attributes.

Наследование и виртуальные функции.

Механизмы наследования

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

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

Интерфейс может быть объявлен для расширения одного или нескольких интерфейсов.

Наследование позволяет определять новые классы в терминах существующих классов. В языке C# поддерживается только простое наследование: любой подкласс является производным только от одного непосредственного суперкласса. При этом любой класс может наследоваться от нескольких интерфейсов.

Классы – интерфейсы.

любой класс может наследоваться от нескольких интерфейсов.

Производные классы

В среде VisualStudio.NET новый производный класс можно создать, используя окно ClassView (выполнив в нем команду контекстного меню Add|Add Class). Рисунок 16.1 иллюстрирует страницы диалога C# Class Wizard, предлагаемого средой VisualStudio.NET для создания нового класса.


Рис. 16.1. Диалог C# Class Wizard

Имя создаваемого класса указывается в поле Class Name на странице Class Options диалога C# Class Wizard.

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

В поле Access выбирается модификатор доступа для создаваемого класса.

Для класса в языке C# возможно использование двух модификаторов доступа:

· public - определяет, что нет ограничений на использование класса;

· internal - определяет, что класс будет доступен для файлов, входящих в ту же сборку.

Сборка - это физический файл, который состоит из нескольких PE-файлов (portable executable), генерируемых компилятором среды.NET. В сборку входит декларация (manifest), содержащая описание сборки для управляющей среды.NET.

Класс может имеет один из следующих модификаторов класса:

· abstract - определяет, что класс должен быть использован только как базовый класс других классов. Такие классы называются абстрактными классами;

· sealed - определяет, что класс нельзя использовать в качестве базового класса. Такие классы в языке C# иногда называются изолированными классами.

Очевидно, что изолированный класс не может быть одновременно и абстрактным классом.

Объявление класса может иметь следующее формальное описание:

МодификаторДоступа МодификаторКласса class ИмяКласса: ИмяНаследуемогоКласса {ТелоКласса }

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

Например:

namespace CA1{ public abstract class Class2: CA1.Class1 { public Class2() { // TODO: Add constructor logic here } }}

Методы - члены класса

В среде VisualStudio.NET добавить в класс новый метод можно, используя контекстное меню окна Class View. На рис. 16.2 приведен диалог C# MetodWizard, позволяющий определить модификаторы метода и список формальных параметров.


Рис. 16.2. Диалог C# MetodWizard

Язык C# поддерживает следующие модификаторы метода члена класса:

· static - определяет статический метод, доступный без создания экземпляра класса;

· abstract - определяет абстрактный метод, который является членом абстрактного класса;

· virtual - метод, реализация которого может быть переопределена в производных классах;

· extern - метод, имеющий реализацию вне данного класса (внешний метод);

· override- метод, выполняющий переопределение виртуальной функции, наследуемой от базового класса;

· new - метод, скрывающий в производном классе наследуемый метод с тем же именем (если ключевое слово не указано, то имя скрывается, но при компиляции отображается предупреждение warning).

Порядок указания модификаторов доступа и модификаторов метода несущественен.

Виртуальные и абстрактные методы всегда должны указываться с модификатором доступа public.

Виртуальные методы

Виртуальные методы объявляются в базовом классе с ключевым словом virtual, а в производном классе могут быть переопределены. Метод, который переопределяет виртуальный, указывается ключевым словом override. Прототипы виртуальных методов как в базовом, так и в производном классе должны быть одинаковы.

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

На этапе компиляции строится только таблица виртуальных методов, а конкретный адрес проставляется уже на этапе выполнения.

При вызове метода - члена класса действуют следующие правила:

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

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

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

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

Делегаты.

Делегаты являются ссылками на методы, инкапсулирующими настоящие указатели и предоставляющими удобные сервисы для работы с ними. Ссылки представляют собой объекты соответствующего типа. Все делегаты являются объектами типа System.Delegate или System.MulticastDelegate, который является производным от первого.

Уже к окончанию разработки среды исполнения, ее архитекторы пришли к выводу, что целесообразно использовать только класс MulticastDelegate, а класс Delegate является избыточным. Но, опасаясь серьезных архитектурных нестыковок, разработчики были вынуждены оставить класс Delegate в составе общей библиотеки классов.

Различие между этими классами заключается в том, что экземпляры первого (делегаты) могут хранить лишь одну ссылку на метод, а экземпляры второго могут содержать сразу несколько ссылок на методы. Благодаря этому, можно подсоединять к одному делегату несколько методов, каждый из которых при единственном обращении к делегату будет вызваться по цепочке. Таким образом, из программы будет виден лишь один делегат, за которым скрывается несколько методов Эта возможность очень удобна для поддержки событий, поскольку позволяет без использования дополнительных механизмов присоединить к событию несколько функций обработчиков. Фактически, делегат представляет собой объект — черный ящик, скрывающий в своих недрах указатели на функции. Важно понять, что делегаты, по сути дела, ничем не отличаются от обычных пользовательских объектов. Главная их особенность состоит лишь в том, что они имеют поддержку со стороны среды исполнения. Об их свойствах, в отличие от обычных объектов, знают даже компиляторы, предоставляющие удобные специальные сервисы для работы с ними.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...