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

Чернов Э. А. |
- 78 - |
Лекции по языку C# v 2.3 |
Для привязки фильтров предоставляется указанное ниже окно.
Запуск мониторинга файлов можно обеспечить, например, по таймеру.
Обобщения (шаблоны)
При работе с базами данных усиленное внимание должно обращаться на контроль типов данных, особенно при вводе информации. С этой целью широко применяются «типизированные» наборы данных, элементами которых являются обобщения (классы), которые позволяют контролировать данные на уровне объектов классов, то есть, контролировать не только простые типы данных (числа, строки и т. д.), но и порядок их следования.
Сильно типизированные языки, к которым относится и язык C# вынуждают выполнять реализацию одного и того же алгоритма применительно к разным типам данных. Например, базовые операции со списками (создание списка, сортировка
Чернов Э. А. |
- 79 - |
Лекции по языку C# v 2.3 |
списка, проход по списку, исключение элемента из списка и т. д.) не зависят от типа данных, составляющих список.
Обеспечить независимость операций со списками от типа данных позволяют обобщения (шаблоны), которые сохраняют семантику определений и вызовов функций, не принося в жертву сильную типизацию языка C#.
Обобщение предоставляет механизм для автоматической генерации экземпляров функций для работы с разными типами данных, то есть при конкретном вызове функции для обработки требуемого типа данного заменяются только формальные параметры. Алгоритм функции остается при этом неизменным. Главным преимуществом обобщений является возможность создавать код с жестким соответствием типов данных, в котором ошибки несоответствия типов выявляются во время компиляции.
Функциональность обобщений обеспечивает сборка System.Collections.Generic.
Обобщения опознаются по наличию угловых скобок, внутри которых записана одиночная буква или строка, например, MyFunc<T>. Формально эти лексемы можно называть «параметрами типа». Более точно их следовало бы называть «параметрами, задающими тип». Имеется в виду, что текст в угловых скобках, являющийся параметром, задает тип данных. Другое название параметров типа – «место подстановки». Это определение подразумевает, что угловые скобки указывают на место, в котором будет выполняться подстановка типа данных.
В качестве параметров могут использоваться как встроенные типы данных (double, string и т. д.), так и типы данных, определенные пользователем (структуры и классы).
Ниже приведен пример подпрограммы, обеспечивающей перестановку двух объектов разных типов (целых чисел, строк, структур и объектов класса: первые три типа данных – значения, объект класса - ссылка). Для структур и классов перестановка не отличается от чисел и строк, но вывод каждого элемента структуры или класса описывается отдельно. (Для сокращения длины программы все переменные объявлены с уровнем доступа public, чтобы не использовать свойства).
class Program
{
// Перестановка местами двух объектов разных типов public struct Stru
{
public int num; public string st;
public Stru(int n, string s)
{
this.num = n; this.st = s;
}
}
public class Cla
{
public int num; public string st1, st2;
Чернов Э. А. |
- 80 - |
Лекции по языку C# v 2.3 |
public Cla(int n, string sa, string sb)
{
this.num = n; this.st1 = sa; this.st2 = sb;
}
}
// Метод для перестановки местами двух объектов разных типов static void Swap<T>(ref T a, ref T b)
{
Console.WriteLine("В метод Swap() передана"+
" переменная типа {0}", typeof(T));
T temp; temp = a; a = b;
b = temp;
}
static void Main(string[] args)
{
Console.WriteLine("Обобщенный метод для перестановок \n");
//Перестановка двух значений типа int. int a = 1, b = 2;
Console.WriteLine("Перед перестановкой чисел: \n\t{0}, {1}", a, b); Swap<int>(ref a, ref b);
Console.WriteLine("После перестановки чисел: \n\t{0}, {1}", a, b); Console.WriteLine();
//Перестановка двух строк,
string st1 = "First", st2 = "Second";
Console.WriteLine("Перед перестановкой строк: \n\t{0} {1}", st1, st2); Swap<string>(ref st1, ref st2);
Console.WriteLine("После перестановки строк: \n\t{0} {1}", st1, st2); Stru sr1 = new Stru(1, "First");
Stru sr2 = new Stru(2, "Second"); Console.WriteLine("\nПеред перестановкой структур: "+
" \n\t{0} {1} <-> {2} {3}", sr1.num, sr1.st, sr2.num, sr2.st); Swap<Stru>(ref sr1, ref sr2);
Console.WriteLine("После перестановки структур: "+
" \n\t{0} {1} <-> {2} {3}", sr1.num, sr1.st, sr2.num, sr2.st); Cla csr1 = new Cla(1, "First", "Cube");
Cla csr2 = new Cla(2, "Second", "Ball"); Console.WriteLine("\nПеред перестановкой классов: "+
"\n\t{0} {1} {2} <-> {3} {4} {5}", csr1.num, csr1.st1, csr1.st2, csr2.num, csr2.st1, csr2.st2);
Swap<Cla>(ref csr1, ref csr2); Console.WriteLine("После перестановки классов: "+
" \n\t{0} {1} {2} <-> {3} {4} {5}", csr1.num, csr1.st1, csr1.st2, csr2.num, csr2.st1, csr2.st2);
Console.ReadKey();
}
}

Чернов Э. А. |
- 81 - |
Лекции по языку C# v 2.3 |
Результат выполнения программы: |
|
|
Класс с обобщениями может иметь ограничения. Ограничения записываются в заголовке объявления класса после слова where, за которым следует двоеточие и перечень ограничений. Ниже приведен пример программы, в которой в качестве ограничений указан класс. Тогда при объявлении класса имеющего ограничения доступны будут только переменные, имеющиеся в ограничивающем классе. Фактически в классе с ограничением доступна переменная, являющаяся объектом ограничивающего класса. Через эту переменную можно обратиться к элементам ограни-
чивающего класса (например, fs = rt1.Ndat.fix + rt2.Ndat.fix;).
public class dat |
|
{ |
|
public int num { get; set; } |
|
public double fix { get; set; } |
|
public string st { get; set; } |
|
public dat() { } |
|
public dat(int n, double f, string s) |
|
{ |
|
this.num = n; |
|
this.fix = f; |
|
this.st = s; |
|
} |
|
} |
|
public class Rest<T> where T : dat |
// Задание ограничения |
{ |
|
T obj; |
|
public Rest () { } |
// Конструкторы |
public Rest (T o) {this.obj = o;} |
|
public T Ndat |
// Свойство для объекта типа T |
{ |
|
get { return obj; } |
|
set { obj = value; } |
|

Чернов Э. А. |
- 82 - |
Лекции по языку C# v 2.3 |
}
}
class Program
{
static void Main(string[] args)
{
dat dt1 = new dat(2, 8.5, "Это"); // Инициализация объектов dat dt2 = new dat(5, 5.5, " конкатенация");
int sum; double fs; string str;
Console.WriteLine("Содержимое dt1 {0}, {1}, {2}", dt1.num, dt1.fix, dt1.st ); Console.WriteLine("Содержимое dt2 {0}, {1}, {2}", dt2.num, dt2.fix, dt2.st);
//Объявление объекта тип Rest с вызовом конструктора для класса dat Rest<dat> rt1 = new Rest<dat>(dt1);
Rest<dat> rt2 = new Rest<dat>(dt2);
//Обращение через объект класса Rest к переменным класса dat
sum = rt1.Ndat.num+ rt2.Ndat.num; Console.WriteLine("Сумма целых {0}", sum); fs = rt1.Ndat.fix + rt2.Ndat.fix; Console.WriteLine("Сумма дробных {0}", fs); str = rt1.Ndat.st + rt2.Ndat.st; Console.WriteLine("Строки: {0}", str); Console.ReadKey();
}
}
Ниже приведен результат выполнения программы.
Таким образом, ограничение по классу позволяет через объект класса обращаться к переменным ограничивающего класса.
Кроме ограничения по классу могут быть перечисленные ниже ограничения.
Форма записи |
Описание |
where T: struct |
При ограничении struct тип T должен иметь тип значения |
where T: class |
При ограничении class тип Т должен иметь тип ссылки. |
where T: IFace |
При этом ограничении тип Т должен реализовать указанный ин- |
|
терфейс. |
where T: Face |
При этом ограничении тип Т должен быть наследником от ука- |
|
занного типа. |
where T: new() |
При этом ограничении тип Т должен иметь конструктор по |
|
умолчанию (пустой). |
where Tl: T2 |
При этом ограничении тип Т1 должен быть наследником от типа |
|
Т2. |