
- •Введение
- •Сравнение языков С++ и C#
- •Логические выражения
- •Функции для ввода и вывода в языке C#
- •Управление форматом числовых данных:
- •Обработка исключительных ситуаций
- •Методы и модификаторы параметров
- •Неявно типизированные переменные
- •Понятие класса
- •Свойства
- •Индексаторы
- •Одномерные индексаторы
- •Многомерные индексаторы
- •Перегрузка методов
- •Перегрузка знаков операций
- •Наследование
- •Виртуальные функции
- •Работа с файлами
- •Работа с каталогами
- •Абстрактный класс FileSystemInfo
- •Класс DirectoryInfo
- •Сериализация
- •FileSystemWatcher – отслеживание событий, связанных с файлами
- •Обобщения (шаблоны)
- •Интерфейсы
- •Коллекции
- •LINQ
- •Грамматика выражений запросов
- •Синтаксис запросов
- •Проекция и фильтрация
- •Упорядочение
- •Агрегирующие запросы
- •Операции с коллекциями
- •Операция Concat
- •Операция Union
- •Преобразование
- •Объединение последовательностей
- •FirstOrDefault
- •Группировка
- •Групповая адресация
- •Обработка событий
- •Групповое преобразование делегируемых методов
- •Применение методов экземпляра в качестве делегатов
- •Групповая адресация
- •Ковариантность и контравариантность
- •Класс System. Delegate
- •Назначение делегатов
- •Анонимные функции
- •Анонимные методы
- •Передача аргументов анонимному методу
- •Возврат значения из анонимного метода
- •Применение внешних переменных в анонимных методах
- •Лямбда-выражения
- •Лямбда-оператор
- •Одиночные лямбда-выражения
- •Блочные лямбда-выражения
- •События
- •Пример групповой адресации события
- •Применение аксессоров событий
- •Разнообразные возможности событий
- •Применение анонимных методов и лямбда-выражений вместе с событиями
- •Рекомендации по обработке событий в среде .NET Framework
- •Применение делегатов EventHandler<TEventArgs> и EventHandler
- •Практический пример обработки событий

Чернов Э. А. |
- 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();
}