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

Чернов Э. А. |
- 60 - |
Лекции по языку C# v 2.3 |
Ниже приведен вариант выполнения программы.
Уровень protected обеспечивает доступ только из производного класса в базовый. В примере основная программа (Main) входит в состав постороннего класса, который не является ни базовым, ни производным классом, поэтому для доступа к подпрограммам rest из основной программы требуется уровень public.
Виртуальные функции
Виртуальная функция определяется внутри базового класса и может быть переоп-
ределена в производном классе. Ключевое слово virtual указывает на виртуальную функцию, например:
public virtual void fun ( )
{
// Операторы реализации функции
}
Поскольку виртуальная функция переопределяется в производном классе, то это напоминает перегрузку функций. Различие заключается в том, что при перегрузке функций различны списки параметров, а в виртуальных функциях количество и типы аргументов должны строго совпадать, различны только алгоритмы.
Ниже приведена программа, состоящая из базового и двух производных классов. В задаче определяется сопротивление цепи, состоящей из двух резисторов. Резисторы могут быть соединены последовательно или параллельно. При последовательном соединении сопротивления суммируются, при параллельном соединении итоговое сопротивление равно сумме проводимостей:
Для двух резисторов общее сопротивление равно произведению исходных сопротивлений, которое делится на их сумму.
class Res // Базовый класс |
|
{ |
|
protected double res1, res2; |
// Сопротивления |
Чернов Э. А. |
- 61 - |
Лекции по языку C# v 2.3 |
public Res() { res1 = res2 = 0.0; } // Конструктор без параметров public virtual double get_chain_resist()
{
return 0.0; // Эта функция должна что-нибудь возвращать
}
//Свойство для первого резистора public double R1
{
get { return res1; } set { res1 = value; }
}
//Свойство для второго резистора public double R2
{
get { return res2; } set { res2 = value; }
}
}
//Этот класс определяет сопротивление цепи при параллельном
//соединении резисторов */
class Paral : Res
{
//Конструктор принимает 2 параметра и передает их в конструктор
//с параметрами базового класса
public Paral(double r1, double r2): base()
{
this.res1 = r1; this.res2 = r2;
}
public override double get_chain_resist()
{
return R1*R2/(R1 + R2);
}
}
//Этот класс определяет сопротивление цепи при
//последовательном соединении резисторов class Sequent : Res
{
//Конструктор c двумя параметрами и наследник пустого
//конструктора базового класса
public Sequent(double r1, double r2) : base()//
{
this.res1 = r1; this.res2 = r2;
}
public override double get_chain_resist()
{
return R1 + R2; // Итоговое сопротивление
}
}
class Program
{

Чернов Э. А. |
- 62 - |
Лекции по языку C# v 2.3 |
static void Main(string[] args)
{
double inr1, inr2;
Console.WriteLine("\n Введите значения сопротивлений \n"); inr1 = double.Parse(Console.ReadLine());
inr2 = double.Parse(Console.ReadLine()); //Объявление базового класса
Res r = new Res ();
// Объявление производного класса для последовательного соединения r = new Sequent(inr1, inr2);
Console.WriteLine(" R при последовательном соединении = " + r.get_chain_resist());
// Объявление производного класса для параллельного соединения r = new Paral(inr1, inr2);
Console.WriteLine(" R при параллельном соединении = " + r.get_chain_resist());
Console.ReadKey(); // Для останова экрана пользователя
}
}
Пример выполнения программы:
В приведенном выше примере виртуальная функция базового класса не имеет выполняемых действий. Полезные операции она выполняет только после переопределения в производных классах. Это распространенная ситуация, при которой базовый класс содержит только набор пустых функций, которые должны переопределяться в производных классах.
У одного базового класса может быть несколько производных классов. В каждом из таких классов виртуальная функция, указанная в базовом классе, может переопределяться по-разному. Ссылка на базовый класс можно использовать для обращения к членам производных классов, а это позволяет иметь одинаковый интерфейс для выполнения разных действий с объектом. Этот принцип называется полиморфизмом.
Для иллюстрации сказанного в приведенной выше программе для получения значений сопротивлений для разных видов соединения применяется одна и та же форма вызова:
r.get_chain_resist(), где r объект базового класса.
Если виртуальная функция в базовом классе не выполняет никаких действий, ее можно объявить абстрактной, чтобы подчеркнуть, что она пустая. Для приведенного выше примера объявление будет иметь вид:
public abstract double get_chain_resist();
Чернов Э. А. |
- 63 - |
Лекции по языку C# v 2.3 |
Если класс имеет хотя бы одну виртуальную функцию, то он называется абстрактным.
Ниже приведена программа из книги Г. Шилдта «Полный справочник по языку C#», в которой рассматривается несколько производных классов. Для этой программе выполнена косметическая правка
class TwoDShape
{
double pri_width; double pri_height;
public TwoDShape() // Конструктор по умолчанию,
{
Width = Height = 0.0; // параметры конструктора определены в свойствах name = "null";
}
// Конструктор с параметрами.
public TwoDShape(double w, double h, string n)
{
Width = w;
Height = h; name = n;
}
//Конструктор объекта с одинаковой шириной и высотой. public TwoDShape(double x, string n)
{
Width = Height = x; name = n;
}
//Конструктор копирования для объекта TwoDShape. public TwoDShape(TwoDShape ob)
{
Width = ob.Width; Height = ob.Height; name = ob.name;
}
//Свойства ширины и высоты объекта,
public double Width
{
get { return pri_width; }
set { pri_width = value < 0 ? -value : value; }
}
public double Height
{
get { return pri_height; }
set { pri_height = value < 0 ? -value : value; }
}
public string name { get; set; } // Свойство для названия public void ShowDim()
{
Console.WriteLine("Ширина и высота равны "+Width + " и " + Height);
}
public virtual double Area()
{
return 0.0;
Чернов Э. А. |
- 64 - |
Лекции по языку C# v 2.3 |
}
}
// Класс для треугольников, производный от класса TwoDShape. class Triangle : Two Shape
{
string Style;
public Triangle() // Конструктор, используемый по умолчанию,
{
Style = "null";
}
// Конструктор для класса Triangle,
public Triangle(string s, double w, double h) : base(w, h, "треугольник")
{
Style = s;
}
//Конструктор для равнобедренного треугольника, public Triangle(double x) : base(x, "треугольник")
{
Style = "равнобедренный";
}
//Конструктор копирования для объекта типа Triangle, public Triangle(Triangle ob) : base(ob)
{
Style = ob.Style;
}
//Переопределить метод Area() для класса Triangle, public override double Area() { return Width * Height / 2; } public void ShowStyle() // Показать тип треугольника
{
Console.WriteLine("Треугольник " + Style);
}
}
//Класс для прямоугольников, производный от класса TwoDShape. class Rectangle : TwoDShape
{
// Конструктор для класса Rectangle,
public Rectangle(double w, double h) : base(w, h, "прямоугольник") { } // Конструктор для квадрата
public Rectangle(double x) : base(x, "прямоугольник") { }
//Конструктор копирования объекта типа Rectangle, public Rectangle(Rectangle ob) : base(ob) { }
//Возвратить логическое значение true, если квадрат. public bool IsSquare()
{
if (Width == Height) return true; return false;
}
//Переопределить метод Area() для класса Rectangle, public override double Area()
{
return Width * Height;
}