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

Чернов Э. А. |
- 54 - |
Лекции по языку C# v 2.3 |
Log_Rel mg = new Log_Rel(a, b);
if (lg > mg) Console.WriteLine("Площадь больше"); else Console.WriteLine("Площадь меньше"); Console.ReadKey();
}
}
}
Пример выполнения программы.
Кроме перегрузки логических отношений может быть реализована перегрузка значений true и false. Такая перегрузка должна выполняться попарно, и она необходима в случаях, когда объект должен использоваться в сравнениях. Например, обычная ситуация при выборе объектов по соотношению цены и качества не может выполняться путем простого сравнения отдельных составляющих, поскольку отдельные параметры качества могут иметь весовые коэффициенты.
Наследование
Наследование позволяет создать обобщенный класс, в котором определены общие характерные особенности, присущие некоторому множеству элементов. От этого класса могут затем наследовать другие классы, добавляя в него свои индивидуальные особенности. Такой подход позволяет:
1.Упростить процесс программирования, поскольку реализацию новых классов можно выполнять на основе уже существующих.
2.Реализовать полиморфизм, под которым понимается возможность в процессе выполнения приложения выбирать разные алгоритмы реализации команды.
3.Повысить надежность программ, уменьшить трудоемкость разработки новых приложений.
Класс, на основе которого создается новый класс, называется базовым, а вновь создаваемый класс называется производным. Базовый класс определяет все те элементы, которые будут общими для всех производных классов, полученных из этого базового класса. Производный класс наследует эти общие элементы и добавляет новые члены, характерные для данного класса.
Например, в системе Windows определен базовый класс Control, который определяет все доступные элементы управления (кнопки, диалоги, списки и т. д.), и обработчики событий для каждого (действие при щелчке по кнопке, выбор позиции и т. д.). На основании этого базового класса строится все многообразие элементов управления, используемых в приложениях.
Производный класс не может быть наследником от нескольких базовых классов. Но наследование может быть последовательным. Для класса GenTClass можно оформить производный ClassA, от которого может наследовать ClassB. Таким образом ClassB наследует члены, объявленные как в GenClass, так и в ClassA.
Чернов Э. А. |
- 55 - |
Лекции по языку C# v 2.3 |
Существует два вида наследования: наследование реализации и наследование интерфейса.
1.При наследовании реализации (implementation inheritance) производный класс является наследником базового типа, получая доступ ко всем данным и методам (с учетом разграничения доступа). Реализация функций в производном классе может быть переопределена. Такой вид наследования позволяет добавить дополнительные подпрограммы к существующему классу и предоставляет возможность совместного использования общей функциональности нескольких классов.
2.При наследовании интерфейса (interface inheritance) производный класс наследует только сигнатуру функций без их реализации. Реализация функций выполнена в базовом классе, который обеспечивает возможность обращения к к этим функциям из производного класса. Такие базовые классы называются интерфейсами, их имена начинаются с заглавной буквы I (например, IEnumerable, IComparable и т. д.). Они содержат готовые подпрограммы для реализации часто применяемых действий.
Для указания на наследование после имени класса записывают двоеточие, после которого указывают класс, от которого выполняется наследование. Формат записи наследования:
class newClass : BaseClass
{
<элементы, добавляемые к классу NewClass>
}
Указанные в примере имена TnewClassT и TBaseClassT, естественно, могут быть заменены любыми другими.
При наследовании уровни доступа public и private сохраняют свой обычный смысл, но добавлен уровень доступа protected, который применяется при наследовании для доступности элементов базового класса в производном классе.
Следует иметь в виду простой принцип: базовый класс «ничего не знает» о производном классе, но производный класс «знает» только те элементы базового класса, которые для него доступны.
Уровень защиты protected обеспечивает доступ к членам базового класса из его наследников, но не разрешает доступ к этим членам из посторонних классов.
Производному классу всегда доступны члены базового класса с уровнями защиты public и protected и недоступны члены с уровнем защиты private.
Объявленные в базовом классе доступные члены рассматриваются как глобальные в производных классах. Если такие элементы не переопределяются и не перегружаются в производном классе, то их используют в производном классе без указания видимости. Если же переопределение или перегрузка имеет место (например, когда в базовом и производном классах имеются подпрограммы с одинаковыми сигнатурами, но реализующих разные действия), то перед именем подпрограммы следует указывать видимость (точка после имени класса и после этого имя переменной или подпрограммы). Это однозначно определяет, к какому из перегруженных методов выполняется обращение.
Чернов Э. А. |
- 56 - |
Лекции по языку C# v 2.3 |
В языке С# не предусмотрено одновременное наследование одного производного класса от нескольких базовых классов, поэтому для любого производного класса можно задать в качестве базового только один класс. Исключение составляют интерфейсы, которые допускают множественное наследование. Множественное наследование предусматривает запись нескольких имен классов в виде списка через запятую.
У одного базового класса может быть несколько производных классов. Кроме того, любой производный класс может быть базовым для другого производного класса. Таким образом, можно построить иерархию производных классов, в которой каждый последующий производный класс является наследником предыдущего. Производный класс наследует доступные члены своего базового класса (переменные, подпрограммы, аксессоры, индексаторы и т. д.). Эти члены должны быть объявлены с уровнем доступа public или protected. Члены с уровнем доступа private не наследуются.
При необходимости наследование для некоторого класса можно запретить, записав перед словом class ключевое слово sealed. Обычно такой запрет применяется для реализации вспомогательных для приложения операций. Такие классы называются изолированными.
При создании в главной программе связки из базового и производного классов, как правило, объявляется объект только производного класса, поскольку базовый класс является частью этой связки. Объекты создаются в последовательности: сначала базовый класс, затем производный.
Компилятор создает конструкторы по умолчанию, которые инициализируют числовые переменные нулями, а для строковых переменных задаются пустые строки. Если оформляются конструкторы с параметрами или явно задаются конструкторы без параметров, то иерархия вызова конструкторов не должна нарушаться: сначала вызываются конструкторы базового класса, а затем конструкторы производного класса. В частности, при попытке оформления конструктора для производного класса выдается сообщение об ошибке, гласящая о том, что в базовом классе отсутствует конструктор без параметров, если таковой не был оформлен.
Конструкторы для базового класса и производного класса оформляются каждый в своем классе. Если для создания объекта базового класса требуется вызов конструктора с параметрами (когда базовый класс не объявляют), то передать такие параметры можно через конструктор производного класса. Поэтому конструктор производного класса является как бы наследником конструктора базового класса.
Для передачи через конструктор производного класса параметров для конструктора от базового класса применяется ключевое слово base, после которого в круглых скобках перечисляются параметры, передаваемые в конструктор базового класса. Ключевое слово base записывается после двоеточия после закрывающей скобки конструктора производного класса.
Базовый класс и производный могут иметь конструкторы как каждый из них отдельно, так и вместе. Если конструкторы имеются в обоих классах, то порядок их вызова совпадает с последовательностью наследования, то есть конструкторы ба-

Чернов Э. А. |
- 57 - |
Лекции по языку C# v 2.3 |
зового класса выполняются раньше конструкторов производного класса, поскольку базовый класс "не знает" о существовании производного класса. Форма записи конструктора (из примера приведенной ниже программы) имеет вид:
MyDeriv (double sum,double rate, double per) : base (sum)
{
this.saving = sum; // Инициализация члена базового класса из производного
Здесь параметры rate и per передаются в конструктор класса MyDeriv, а параметр sum предназначен для конструктора класса Bank. Обратите внимание на дублирование параметра sum в первом (после MyDeriv) и втором (после base) списках, передаваемого для конструктора базового класса. Тип параметров в скобках после слова base не указываются. Это значит, что значение (в данном примере для sum) передается в списке параметров производного класса, а запись указывает на принадлежность параметра базовому классу.
В литературе говорится о том, что конструктор производного класса «вызывает» конструктор базового класса, но на самом деле это не совсем точно. Поскольку после объявления производного класса оба класса являются единым целым, то в конструкторе производного класса выполняется инициализация члена базового класса, минуя конструктор базового класса, как показано выше.
Одна и та же переменная, являющаяся членом класса, может появиться в трех местах. Везде эта переменная может иметь как разные имена, так и одно и то же.
1.При объявлении члена класса.
2.Как член списка конструктора с параметрами. Если в списке параметров конструктора имя переменной отличается от имени, объявленного в классе, то соответствие устанавливается с помощью оператора присваивания:
3.Как член списка параметров конструктора при объявлении объекта класса. В общем случае конструктор вызывается по сигнатуре.
Вязыке C# можно в качестве имен параметров конструктора можно использовать имена членов класса. Тогда в коде конструктора эти имена могут применяться напрямую, без промежуточного присваивания (см. примечания в тексте программы).
Примечание: Промежуточное присваивание требуется, когда инициализируется элемент базового класса в конструкторе производного класса и имена переменных в конструкторе и при объявлении не совпадают.
Имеется также возможность связать имена параметров конструктора с именами переменных в основной программе. Для этого в списке параметров при объявлении объекта класса записывается имя из списка параметров конструктора, а через двоеточие имя переменной в основной программе. Пусть в приведенной ниже программе переменные объявлены следующим образом:
double s, r, p;
Чернов Э. А. |
- 58 - |
Лекции по языку C# v 2.3 |
Если определить конструктор производного класса:
public CItrst (double saving, double rate, double period) : base (sum)
{
this.rate = rate; this.per = period;
}
Тогда объявление производного класса можно выполнить так
CItrst ab = new CItrst(s, r, p); // Вызов по сигнатуре
Или так (указаны имена параметров конструктора):
CItrst ab = new CItrst(sum: s, rate: r, period: p);
Ниже в примере показана программа обработки вкладов с добавлением функции начисления процентов на вклад. Класс обеспечивает реализацию действий по добавлению денег на вклад и снятие денег со счета с соответствующими проверками. В программе Main объект базового класса не объявляется вообще, но при объявлении производного класса вызывается конструктор базового класса, который запрашивает ввод начальной суммы. Функция начисления процентов определена в производном классе, который является наследником от базового класса.
Обратите внимание на то, что функция начисления процентов полностью автономна и добавляет эту функциональность к базовому классу, не пересекаясь с ним. Единственная связь заключается в указании уровня доступа protected для переменной saving с целью ее видимости в производном классе.
// Базовый класс public class CBank
{
protected double saving;
public CBank() // Конструктор базового класса
{
Console.WriteLine(" Введите начальную сумму"); saving = double.Parse(Console.ReadLine());
}//
public void deposit(double amt)
{
saving += amt; // Добавление суммы на счет
Console.WriteLine(" Остаток = {0}", saving);
}
void withdraw(double amt) // Снятие суммы со счета
{
if (amt < saving)
{
saving -= amt;
Console.WriteLine(" Остаток = {0}", saving);
}
else
{
Console.WriteLine(" Сумма на счете = {0 } меньше нуля, операция"+
“ отменена", saving - amt +" Остаток = {0}", saving);
}
}
Чернов Э. А. |
- 59 - |
Лекции по языку C# v 2.3 |
// Добавление или уменьшение депозита public void insum()
{
ConsoleKeyInfo ch;// double sum;
do
{
Console.WriteLine("\n Вы хотите изменить сумму?"); Console.WriteLine
(" Добавить - буква Y, уменьшить - буква N, далее - буква E "); ch = Console.ReadKey(true); // true, чтобы не было на экране эха буквы
if (ch.Key.Equals(ConsoleKey.E)) break; // Выход из подпрограммы
Console.WriteLine(" Введите сумму"); sum = double.Parse(Console.ReadLine());
if (ch.Key.Equals(ConsoleKey.Y)) deposit(sum); if (ch.Key.Equals(ConsoleKey.N)) withdraw(sum);
} while (true);
}
}
// Объявление производного класса. class CItrst : CBank
{
public double rate, |
//Процентная ставка |
factor, |
//Коэффициент увеличения капитала |
per; |
//Количество периодов начисления процентов |
public CItrst() |
|
{ |
|
insum();// Вызов
Console.WriteLine("\n Введите ставку и количество периодов \n"); rate = double.Parse(Console.ReadLine());
per = double.Parse(Console.ReadLine()); factor = Math.Pow(1 + rate / 100, per);
}
//Это функция-член класса CItrst для вывода остатка на счете public void rest()
{
double res = saving;//
Console.WriteLine("\n Остаток на счете = " + res * factor);
}
}
class Program
{ // Основная программа static void Main()
{
CItrst ab = new CItrst();// Базовый класс не объявляется ab.rest(); //
Console.ReadKey(); // Останов экрана
}
}
При нажатии на клавишу буквы E происходит выход из цикла do-while и тем самым выход из метода insum. Для распечатки результата вызывается метод rest.