Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по ООП (язык C#).pdf
Скачиваний:
190
Добавлен:
16.05.2015
Размер:
1.54 Mб
Скачать

Чернов Э. А.

- 38 -

Лекции по языку C# v 2.3

Для обращения к членам класса Product в классе Order объявлен объект tov. Однако переменные в этом классе объявлены с атрибутом private, поэтому для обращения к ним оформлены функции get_num(), get_name() и get_price(). Структура этих функций одинакова, они содержат единственный оператор return, который возвращает соответствующее значение (например, return Prod_Num;). Привилегии доступа для них установлены public, поэтому в классе Order к ним можно обращаться через объект pr. Этот способ обращения к членам класса хотя и разрешен в языке C#, но его использовать не рекомендуется. Для обращения к членам класса рекомендуется применять «свойства».

Значения атрибутам товара в классе Product присваивается с помощью конструктора с параметрами. Для упрощения примера параметры в конструктор переданы в виде констант.

В связи с объявлением классов вводятся некоторые определения.

Термин

Тип-значение

Тип-ссылка

Типизированный набор данных

Что обозначает

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

Часто применяется термин «ссылочный тип». К этому типу относятся классы. Содержит ссылку на объект. В принципе это неявный указатель. При копировании ссылочных объектов в копии содержатся новые адреса всех компонентов исходного объекта. Класс, который содержит только информационное описание объекта (без методов для обработки данных). Применяется при работе с базами данных и при создании списков, обеспечивая проверку соответствия типов данных на уровне компиляции. В примере выше – Order обычный класс (выполняется обработка), а класс Product является типизированным набором данных (только описание и вспомогательные операции).

При создании объектов для них выделяется память. Если некоторый объект больше не используется, память, занятая им, должна освобождаться. В языке C# предусмотрена автоматическая «сборка мусора». Под этим понимается, что память, занятая объектом освобождается, если ссылки на объект отсутствуют. Пользователь может вручную выполнить освобождение памяти, вызвав «деструктор», который оформляется также, как и конструктор, но перед именем деструктора записывается тильда (‘~’). Однако, выполнять сборку мусора вручную не рекомендуется, поскольку среда времени выполнения осуществляет эту операцию во время минимальной загрузки центрального процессора (для повышения быстродействия). При закрытии приложения эта операция выполняется принудительно и автоматически, при этом обеспечивается отсутствие «утечки памяти».

Свойства

Инкапсуляция данных не позволяет напрямую обращаться к закрытым членам класса из другого класса. Для работы с такими данными могут создаваться подпрограммы, входящие в состав класса, и имеющие статус доступа public. (см. пример

Чернов Э. А.

- 39 -

Лекции по языку C# v 2.3

выше: функции get_num(), get_name() и get_price()). В языке C# предусмотрена возможность вместо таких функций применять так называемые «свойства» (принятый в литературе термин неудачный, может быть следовало бы применять термин «аксессор», но он уже используется к фрагментам get и set). Формат оформления «свойства» имеет вид:

public <тип> <имя свойства>

 

{

 

get { return <имя переменной>; }

// Получение значения

set { <имя переменной> = value;}

// Установка значения

}

 

Имеется в виду, что переменная, имя которой указано, имеет закрытый доступ. <имя свойства> и <имя переменной> должны быть разными. Часто применяется прием, при котором эти имена отличаются одной или несколькими заглавными или строчными буквами. Например, в примере ниже <имя переменной> price, а <имя свойства> Price. В отличие от функции круглые скобки после имени «свойства» не записываются. Лексеме value присваивается значение вызывающим кодом. Эта лексема является контекстуальным ключевым словом, и, когда входит в состав фрагмента с set, всегда имеет тип, совпадающий с типом «свойства». Например, если объявлен объект tov, то обращение к элементам класса Product с помощью «свойств» будет иметь вид:

double cost = tov.Price;

// Использование «свойства» get

tov.Price = 125.50;

// Использование «свойства» set

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

Ниже приведена программа, которая была приведена выше, но в ней вместо функ-

ций get_num(), get_name() и get_price() оформлены «свойства» Prod_Num, Prod_Name и Price. Изменена также форма обращения к элементам класса Product. В этом примере значения только извлекаются из объекта pr класса Product, поэтому свойства set можно было бы не оформлять. Если их пропустить, то при попытке использования «свойства» будет выдано сообщение о том, что соответствующая переменная доступна только для чтения.

using System;

using System.Collections.Generic; using.Linq;

using System.Text;

namespace Project2

 

 

{

 

 

class Product

// Товар

{

 

 

private int prod_num;

 

// Штрих код товара

private string prod_name;

 

// Название товара

private double price;

 

// Цена

public int Prod_Num

 

 

{

 

 

get { return prod_num; }

// Получение значения

set { prod_num = value;}

// Установка значения value см ниже

Чернов Э. А.

- 40 -

Лекции по языку C# v 2.3

}

public string Prod_Name

{

get { return prod_name; } set { prod_name = value;}

}

public double Price

{

get { return price; } set { price = value; }

}

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

public Product(int Num, string Name, double Pr)

{// Присвоение значений аргументов переменным класса prod_num = Num;

prod_name = Name; price = Pr;

}

}

class Order // Заказ

{

private static double Order_Cost; // Стоимость заказа private static string Client_Name; // Фамилия клиента private static int Product_Quan; // Количество

static void Main(string[] args)

{

//

Product tov = new Product(123456, "Молоток", 120.75); Console.WriteLine("\n Введите имя клиента и количество товара \n");

//

Client_Name = Console.ReadLine(); Product_Quan= int.Parse(Console.ReadLine());

Order_Cost = tov.Price * Product_Quan;

Console.WriteLine("\n Клиент " + Client_Name + " заказал товар\n " + tov.Prod_Name + " (штрих код "+ tov.Prod_Num+") в количестве "+

Product_Quan + "\n на сумму " + Order_Cost); Console.ReadKey(); // Останов экрана пользователя

}

}

}

Свойства могут быть оформлены упрощенно. Просто после имени переменной можно вставить

{ get; set; }

Однако такая форма может применяться только для переменных, объявленных с уровнем доступа public, поэтому не видно разницы между обычным обращением к таким переменным без использования и с использованием «свойств». Преимущество состоит в том, что появляется возможность разграничения доступа для считывания и изменения значения, а также можно выполнять предварительные вычисления. Так, если записать:

Чернов Э. А.

- 41 -

Лекции по языку C# v 2.3

public int x{ get; private set; }

 

 

В этом случае переменная x доступна только для чтения.

Индексаторы

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

мерными.

Одномерные индексаторы

Ниже приведена общая форма одномерного индексатора:

тип_элемента this[int индекс]

{

//Аксессор для получения данных, get

{

//Возврат значения, которое определяет индекс.

}

//Аксессор для установки данных, set

{

//Установка значения, которое определяет индекс, через value.

}

}

где тип_элемента указывает на тип элемента, с которым «работает» индексатор. Параметр «индекс» указывает на индекс элемента, к которому осуществляется доступ. Этот параметр должен быть целочисленного типа данных, например, типа int.

В теле индексатора определены два аксессора (т.е. средства доступа к данным): get

иset. Аксессор подобен методу, за исключением того, что он не имеет типа возвращаемого значения и параметров (эти типы заданы в заголовке индексатора). Аксессоры вызываются автоматически в основной программе при обращении к массиву экземпляров класса, в котором объявлен индексатор, и них передается индекс, указанный при вызове. Если индексатор экземпляра класса указан слева от оператора присваивания, то вызывается аксессор set и массиву в экземпляре класса, на который указывает индекс, с помощью параметра value, присваивается значение, записанное справа от знака равенства. В противном случае вызывается аксессор get

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

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

Ниже приведен пример. Для сокращения примера массив в объекте класса выбран размерности 4 и заполняется из вспомогательного массива. Для наглядности тип значения для индекса и для индексатора выбраны разными:

class Ind_Test

Чернов Э. А.

- 42 -

Лекции по языку C# v 2.3

{

 

 

double[ ] vec;

// массив, хранящийся в классе Ind_Test

public Ind_Test(int Size)

// Конструктор

 

{

 

 

vec = new double[Size];

// Объявления размерности массива

}

 

 

// Индексатор

 

 

public double this[int i]

// Объявление типа элементов и типа индекса

{

 

 

set

// Аксессор set

 

{

 

 

vec[i] = value;

// Присвоение значения элементу массива

}

 

 

get

// Аксессор get

 

{

 

 

return vec[i];

// Получение значения элемента массива

}

 

 

}

}

class Program

{

static void Main()

{

Ind_Test ar = new Ind_Test(4); // Объявление объекта класса Ind_Test double [ ] n = new double [ ] { 2.5, 3.4, 4.3, 5.2}; // Вспомогательный массив

// Значение для каждого элемента массива vec объекта ar класса Ind_Test for (int i = 0; i < n.Length; i++)

{

// Аксессор set присваивает значение для элемента массива vec ar[i] = n[i];

Console.Write( "{0} ", ar[i]); // Просмотреть заполнение массива

}

Console.WriteLine("\n"); // for (int i = 0; i < n.Length; i++)

// Аксессор get получает значение vec из массива ar, затем умножение на 2

{

Console.Write( "{0:F} ", 2 * ar[i]);

}

Console.ReadKey(); //

}

}

Вывод программы имеет вид:

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

Следует отметить, что индексатор может быть и не связан с массивом. Если есть некоторая функция, вычисление которой зависит от номера, (например, степень целого числа, число Фиббоначи и т. д.), то для получения значений такой функции может применяться индексатор.

Чернов Э. А.

- 43 -

Лекции по языку C# v 2.3

Примечание: Значение, выдаваемое индексатором, не может передаваться в качестве ссылочного параметра при вызове подпрограммы (параметр не может иметь

модификатор ref или out). Это связано с тем, что индексатор обращается к члену своего класса, поэтому сам не может быть ссылкой.

Ниже приведен пример программы, которая вычисляет число Фиббоначи по введенному номеру.

// Индексатор, вычисляющий n-ое число Фиббоначи: class FibbNumber

{

public int this[int i] // Индексатор для чтения

{

get

{// i – номер числа

if (i <=0) throw new Exception("Номера начинаются с 1");

else if (i==1 || i==2) return 1;

// Первые 2 числа: 1 и 1

else

 

{

 

int prev = 1, curr = 1, next;

for (int k=3; k<=i; ++k)

// Цикл до введенного номера

{

 

next = prev + curr;

// Очередное число

prev = curr;

// Перестановка

curr = next;

 

}

return curr;

}

}

}

}

class Program

{

static void Main()

{

Console.Write("n=");

Fibbonachy fib = new Fibbonachy();

try// Исключительная ситуация: ввод 0 или меньше

{

int n=int.Parse(Console.ReadLine()); Console.WriteLine("fib[{0}] = {1}", n, fib [n]);

}

catch (Exception e) // Исключительная ситуация пользователя

{

Console.WriteLine(e.Message); // Ошибка ввода

}

Console.ReadKey();

}