Использование обработчиков исключений
Использование обработчиков исключений, как и любых других событий, предполагает явно или неявно деление «клиент-сервер».
Коды программ
//Задача: Описать класс РАЦИОНАЛЬНЫЕ ЧИСЛА
using System; //стандартная библиотека — ввод-вывод, базовые классы
using tInteger=System. Int32;
namespace Fraction //логически связанное определение имён (Пространства имён (namespace) предоставляют возможность логической взаимосвязи классов и других типов)
{
class сFraction
{//поля — см. далее Сокрытие данных (set, get- подход)
public tInteger numerator; //числитель
public tInteger denominator;// знаменатель
//не в нормализованном виде, если нужно — напиши метод
//методы:
//консольный ввод и вывод
private tInteger ReadInt(string s)
{
Console. WriteLine(s); //выводим на консоль информацию, которая содержалась в s
string input = Console. ReadLine(); // нет параметров, т. к. системный оператор
return Int32.Parse(input); // преобразование типов(читается строковое, а преобразуется в int)
}
public void read(string s)
{
Console. WriteLine(s);
numerator = ReadInt("Введите числитель: ");
denominator = ReadInt("Введите знаменатель: ");
}
private void WriteInt(string s, Int32 x)
{
Console. WriteLine(s);
Console. WriteLine(x. ToString()); //возвращает строку, представляющую текущий объект.
}
public void write(string s)
{
Console. WriteLine(s);
WriteInt("Числитель:", numerator);
WriteInt("Знаменатель:", denominator);
Console. ReadKey(); //задержка — получает следующий нажатый пользователем символ или функциональную клавишу. Нажатая клавиша отображается в окне консоли.
}
public void assign(сFraction a)//присваивание значений — "клонирование"
{
this. numerator = a. numerator;
this. denominator = a. denominator;
}
public void add(сFraction a) //добавить к this
{
this. numerator = this. numerator * a. denominator + a. numerator * this. denominator;
this. denominator = a. denominator * this. numerator;
}
public void subtract(сFraction a) //вычесть из this
{
this. numerator = this. numerator * a. denominator — a. numerator * this. denominator;
this. denominator = a. denominator * this. numerator;
}
public void multiply(сFraction a) //домножить
{
this. numerator = this. numerator * a. numerator;
this. denominator = a. denominator * this. denominator;
}
public void divide(сFraction a)//поделить
{
this. numerator = this. numerator * a. denominator;
this. denominator = this. denominator * a. numerator;
}
} //cFraction
class Program
{//программа — запускалка
//метод:
static void Main()
{
//сложение двух дробей y<—y+x
сFraction y, x; //указатели
x = new сFraction(); // вызов конструктора (выделение памяти)
y = new сFraction();
y. read("Введите первое слагаемое:");
x. read("Введите второе слагаемое:");
y. add(x);
y. write("Сумма равна:");
}
}// Program
}// Fraction
//Задача: Черепашья графика. Простая модель управления роботом-черепашкой, которая рисует на дискретной сетке.
using System;
using System. Collections. Generic;
using System. Linq;
using System. Text;
namespace Turtle//логически связанное определение имён
{
enum tColor {red, green, blue} //перечисляемый тип, состоящий из цветов
class cCell
{
protected internal tColor Color; // определение переменной Color типа tColor
//set-get:
public tColor GetColor() {return Color;}
cCell (tColor Color) {this. Color= Color;} // конструктор
} //class cCell
class cGrid //сетка
{
protected int Dim; //размерность
public int GetDim() {return Dim;}
protected internal cCell [,] Grid; //сама сетка(массив Grid) типа cCell (т. е. перечисляемого типа {red, green, blue})
// текущая позиция
protected internal int CurrentX; //, где internal означает то, что доступ ограничен текущей сборкой, а protected означает, что доступ ограничен содержащим классом или типами, которые являются производными от содержащего класса.
protected internal int CurrentY;
public int GetCurrentX() {return CurrentX; }
public int GetCurrentY() {return CurrentY; }
protected void Move (int x, int y )
{
//модульная арифметика (выход за границы)
CurrentX+=x; //эквивалентно выражению CurrentX = CurrentX + x
CurrentY+=y;
} // Move
cGrid (int Dim, tColor Color) //конструктор
{
this. Dim= Dim;
CurrentX=0;
CurrentY=0;
Grid= new cCell [Dim, Dim];
for (int i= 0; i<Dim; i++)
for (int j=0; j<Dim; j++)
Grid [i, j].Color= Color;
}
}
class TurtleGraphics: cGrid
{
//поля
public bool PenDown;// состояние пера — карандаш
public tColor PenColor;
public void GoUp()
{
Move(0,1);
if (PenDown) Grid[CurrentX, CurrentY].Color = PenColor;
}//PenGoUp
public void GoRight()
{
Move(1,0);
if (PenDown) Grid[CurrentX, CurrentY]. Color = PenColor;
}//PenRight
public void GoLeft()
{
Move(-1,0);
if (PenDown) Grid[CurrentX, CurrentY]. Color = PenColor;
}//PenLeft
public void GoDown ()
{
Move(0,-1);
if (PenDown) Grid[CurrentX, CurrentY]. Color = PenColor;
}//PenDown
}//class TurtleGraphics: cGrid
class Program
{ //запускалка
static void Main ()
{
}
}//class Program
}//namespace TurtleGraphics
// Компонентно-ориентированное программирование
//Задача: сложная иерархия животных, основанная на разной реализации строения, по передвижению
using System;
using System. Collections. Generic;
using System. Linq;
using System. Text;
using System. Threading. Tasks;
namespace Hierarchy
{
class cAnimals
{
protected string Name;
public void SetName(string x)
{
this. Name = x;
}
public string GetName()
{
return Name;
}
public cAnimals()
{
}
public cAnimals(string x)
{
this. Name = x;
}
}
class cMammals : cAnimals
{
}
class cFishes : cAnimals
{
}
class cBirds : cAnimals
{
}
//Разница между интерфейсами и абстрактными классами
//предок обязан/ может дать реализацию
//иными словами, здесь интерфейс не часть класса, но частный случай — абстрактный класс, наследование которого обязывает к реализации
abstract class FlyAble
{
public abstract void Fly();
}
//интерфейсы: только заголовки методов без модификаторов (public по умолчанию)
//в частности, нет полей (но может быть set-get) и невозможна инициализация
//движение с переменной скоростью
interface IMoveAble
{
void SetSpeed(int x);
int GetSpeed();
}
//вносится коллизия имён для демонстрации проблем множественного наследования
//ошибка проектирования?
//см. далее также обсуждение конфликта реализации и способы разрешения (склеивание, переименование)
interface IFlyAble : IMoveAble
{
void Fly();
}
interface IRunAble : IMoveAble
{
void Run();
}
interface ISwimAble : IMoveAble
{
void Swim();
}
class Elephant : cMammals, IRunAble, ISwimAble
{
//явное описание интерфейса- реализация:
void IRunAble. Run()
{
//выполнение метода:
Console. WriteLine("Elephant" + GetName() + "runs");
}
void ISwimAble. Swim()
{
//выполнение метода
Console. WriteLine("Elephant" + GetName() + "swims");
}
public Elephant()
{
}
public Elephant(string x)
{
this. Name= x;
}
//разрешение коллизии — "склеивание"
void IMoveAble. SetSpeed(int x)
{
}
int IMoveAble. GetSpeed()
{
return 0;
}
//иной вариант — переименование
//реализуем как закрытие, открываем под другими именами
// см. Duck
} //class Elephant
class Dolphin : cMammals, ISwimAble
{
//Явное описание интерфейса (реализация)
void ISwimAble. Swim()
{
//выполнение метода
}
void IMoveAble. SetSpeed(int x)
{
}
int IMoveAble. GetSpeed()
{
return 0;
}
} //class Dolphin
class Duck : cBirds, ISwimAble, IRunAble, IFlyAble
{
//Явное описание интерфейса-реализация:
void ISwimAble. Swim()
{
//Выполнение метода
}
void IRunAble. Run()
{
//выполнение метода
}
void IFlyAble. Fly()
{
//выполнение метода
}
void IMoveAble. SetSpeed(int x)
{
}
int IMoveAble. GetSpeed()
{
return 0;
}
}//class Duck
class Program
{
// запускалка
static void Main()
{
Elephant E1 = new Elephant();
E1.SetName("John");
//E1.Run_Method(); //ошибка — нужно явно выбирать интерфейс
((IRunAble)E1).Run(); //ok
IRunAble E2 = E1;
E2.Run(); // ok
//E2.SetName("???"); //ошибка
// объявление объекта интерфейсного класса
IRunAble E = new Elephant("Pete");
//E. SetName("???"); //ошибка
// объект, функционирующий по правилам метода
E. Run();
ISwimAble obj_Dolphin = new Dolphin();
obj_Dolphin. Swim;
ISwimAble obj_Duck_1= new Duck();
obj_Duck_1.Swim;
IRunAble obj_Duck_2= new Duck();
obj_Duck_2.Run();
IFlyAble obj_Duck_3= new Duck();
obj_Duck_3.Fly();
Console. ReadKey();
}
}// class Program
} //namespace Hierarchy
// Задача: динозавры и блохи
using System; // стандартные операции (часть языка)
using System. Collections. Generic;
using System. Linq;
using System. Text;
namespace Animals // определение имён
{
class cAnimal
{
//поля:
protected string Name;
public virtual string GetName() // get-метод виртуальный, т. к. он переопределяемый (для различныхклассов он будет возвращать не только имя, которое есть у всех, но и род, который у всех различен)
{
return "Animal" + Name; // род и имя животного
}
public cAnimal() //конструктор по умолчанию приходится переопределять, чтобы он был доступен
{
}
public cAnimal(string ThatAnimal) //конструктор
{
this. Name = ThatAnimal; // создаётся животное с каким-то именем (своим прозвищем)
}
//методы(cAnimal):
//virtual — допустимо переопределение в производном классе
public virtual void Move() //описание метода "Двигаться" для животных
{
Console. WriteLine(GetName() + "moves");
}
public virtual void Cry() // описание метода "Кричать" для животных
{
Console. WriteLine(GetName() + "cries");
}
public virtual void Bite(cAnimal ThatAnimal) // описание метода "Кусать" для всех животных в целом
{
Console. WriteLine(GetName() + "bites" + ThatAnimal. GetName()); // животное(род, имя), которое мы описываем, кусает другое животное(род, имя) (т. е в качестве параметра ссылка на другое животное )
}
//пример — вызов "вперёд" виртуальных методов из невиртуального
public void Attacks(cAnimal ThatAnimal) // описание метода "нападать" для животных в целом (это невиртуальный метод- переопределять его запрещено, т. е. в потомках он изменяться не может)
{
// метод Attacks вызывает виртуальный метод
Console. WriteLine(this. GetType() + "attacs" + ThatAnimal. GetName()); //нападение одного животного на другое
//нападение заключается в:
this. Move(); //животное передвигается
this. Bite(ThatAnimal); // кусает другое животное
ThatAnimal. Cry(); // то животное кричит (больно же)
ThatAnimal. Move(); // и убегает
}
}//class cAnimal
//класс "cDinosaur" наследник класса "сAnimal"
public class cDinosaur : cAnimal
{
public cDinosaur() //конструктор по умолчанию (при наследовании без конструктора по умолчанию выдаётся ошибка)
{
}
public cDinosaur(string ThatName) //конструктор
{
this. Name = ThatName;
}
//методы, которые добавляются к наследуемым методам (доопределение предыдущих методов для динозавров):
//модификатор override требуется для расширения или изменения абстрактной или виртуальной реализации унаследованного метода
public override string GetName() // переопределённый по отношению к животным
{
return "Dinosaur" + Name; //род и имя животного
}
public override void Cry() //переопределение метода "Кричать" для динозавров
{
Console. WriteLine(GetName() + "cries r-r!");
}
public void Growl() //описание метода "рычать" для динозавров — невиртуальный метод (для динозавров и всех его потомков не будет изменяться)
{
Cry();
}
public override void Move() //описание метода "двигаться" для динозавров
{
Console. WriteLine(GetName() + "runs");
}
} //class cDinosaur
class cTRex : cDinosaur
{
public cTRex() // конструктор по умолчанию
{
}
public cTRex(string ThatName) // конструктор
{
this. Name = ThatName;
}
public override string GetName()
{
return "TRex" + Name; // род и имя животного
}
public override void Cry() // переопределение метода "кричать" для динозавров
{
Console. WriteLine(GetName() + "cries R-R-R");
}
//обработка событий — слушатель/listener события FleaJumpEvent
private cFlea FleaJumpSender; // Flea To Listen To;- поле, в котором хранится ссылка на блоху
public void SetFleaJumpSender(cFlea x) // получает эту ссылку на эту блоху
{
FleaJumpSender = x; // запомнить источник события
//Итак, блоха нужна для того, чтобы достать лист, который при ней хранится (список слушателей)
FleaJumpSender. FleaJumpEventList += new cFlea. FleaJumpEventHandler(TRexFleaJumpEventHandler);// добавить в список ссылку на свой обработчик
// new — создаётся копияновой переменной функционального типа, который описан. Потом новое значение этого типа добавляется в список
// += — это бинарное присваивание (т. е. имя списка и в левой и в правой части присваивания)
// аналогично можно отписаться от подписки "-="
}
public void TRexFleaJumpEventHandler(cFlea Sender)
{
Console. WriteLine(GetName() + "afraid of" + Sender. GetName() + "jumping");
}
} // class cTRex
// класс "Flea" наследник класса "cAnimal"
class cFlea : cAnimal //производный класс "Блоха"
{
public cFlea() // конструктор по умолчанию
{
}
public cFlea(string ThatName) //конструктор
{
this. Name = ThatName;
}
//методы:
public override string GetName()
{
return "flea" + Name; // род и имя животного
}
public override void Move() // описание метода "двигаться" для блох
{
Console. WriteLine(GetName() + "jumps");
OnFleaJump(); // кратный вызов
}
public override void Cry() //описание метода "плакать"для блох
{
Console. WriteLine(GetName() + "cries Pi-pi"); //блоха
}
//обработка событий — источник/ sender
// организация обработки события FleaJumpEvent
public delegate void FleaJumpEventHandler(cFlea Sender); // типизированная ссылка на функцию — обработчик — заголовок
//-объявление функционального типа с именем FleaJumpEventHandler(типизированной ссылкой, но не на тип данных, а на функцию),где EventHandler — пользовательское соглашение — обработчик событий, а FleaJump-прыжок блохи
//delegate-функциональный тип, где к этому типу относятся любые функции, которые на вход принимают параметр типа блоха (т. е. cFlea Sender), которые возвращают функции
//Таким образом, к этому типу относятся все функции с определённым списком параметров
public event FleaJumpEventHandler FleaJumpEventList; // связь имени события с его обработчиками (реализация — список ссылок на обработчики
//event — событие — объявление переменной списка ссылок ранее объявленного типа
//список называется FleaJumpEventList, содержит он ссылки типа FleaJumpEventHandler
//кратный вызов — применение списка объявленного к нужному аргументу (обработчиков)
protected virtual void OnFleaJump() //protected, т. к. события не должны вызываться извне (т. е. эти события происходят внутри)
//OnFleaJump — по прыжку блохи- стандартное соглашение для методов такого назначения
{ // проверяется пустой ли список
if (FleaJumpEventList!= null) FleaJumpEventList(this); // если список непустой, то применяем список к this (т. е. блоха в качестве сообщения пересылает ссылку на себя),
//передавая значения полей
// Таким образом, не функция применяется к аргументу, а список функциональных ссылок (т. е. по ссылкам "дёргаются" функции, которые применяются поочерёдно к списку аргументов)
}
//теперь везде, где прыгает блоха, вызывается метод OnFleaJump
//OnFleaJump нужно вызвать в методе Move
} //class cFlea
class cProgram
{
static void Main()
{
cDinosaur Dino = new cDinosaur("Dino"); // создаём нового динозавра с именем Dino
cFlea Bug = new cFlea("Bug");// создаём новую блоху с именем Bug
Bug. Attacks(Dino); // полиморфный вызов — неявное уточнение типа cAnimal — блоха атакует динозавра
Console. WriteLine("______________________");
// аналогично
// Dino. cAnimal x;
// x=Dino; // уточнение типа не требует явного преобразования типа
// Bug. Attacks(x);
// отследи вызов последней реализации
cTRex Rex = new cTRex("Rex");
Bug. Attacks(Rex);
Console. WriteLine("______________________");
//обработка событий
cTRex AnotherRex = new cTRex("Another Rex");
Rex. SetFleaJumpSender(Bug); //вызывается метод set, в котором говорится от том, чтобы Rex слушал Bug
AnotherRex. SetFleaJumpSender(Bug);
Bug. Move(); //в этом методе есть OnFleaJump, который вызывает методы всех динозавров
Console. ReadKey(); //задержка
//Распечатываем имя динозавра, говорим, что этот динозавр боится прыжков конкретной блохи (выдаётся имя конкретной блохи)
}
}
}
// Задача: Организация обработки исключения
using System;
using System. Collections. Generic;
using System. Linq;
using System. Text;
using System. Threading. Tasks;
namespace ConsoleApplication1
{
class MyException : Exception // класс Exception- возможность получать пользоватеьские исключения
{
string Info; // некая информация об ошибке (исключительной ситуации)
public MyException(); // конструктор по умолчанию
public MyException(string Message) // конструктор для порождения ошибок
{
}
class ExceptionHandlingPattern //стандартная схема обработки исключительных ситуаций
{
//Заглушки. По умолчанию параметр — текущее состояние
//В конкретной ситуации — относящаяся к ней часть
private void MakeJob() { } // сделать некую работу
private void MakeOtherJob() { } // продолжить работу
private bool CheckState() { return true; } // проверить состояние
private void CorrectState() { Console. WriteLine("Попытка самостоятельно исправить ситуацию"); }//исправить состояние
public void TryToMakeJob() //
{
bool Success; //успех выполнению
bool Danger; //риск не выполнения
int MaxCount = 0; //максимальное число попыток выполнения
int Count = 0; // фактическое число попыток выполнения
do
{
Success = true;
try
{
MakeJob();
Danger = CheckState(); //возможно ли продолжение
if (Danger)
{
MyException ThisException = MyException(); //оформирование исключения
throw (ThisException);
}
MakeOtherJob(); //нормальное продолжение
}
catch (MyException me)
{
Success = false;
if (Count <= MaxCount)
{
Count++;
//level+=1; стек вызовов
CorrectState(); //корректировка ситуации
}
else
{
//level-=1 //откат
MyException ThisException = new MyException();
throw (ThisException);
}
}
}
while (!Success);
}
}//class
}
class Program
{
static void Main(string[] args)
{
//int level=0;
// конкретный пример- открыть и обработать файл
ExceptionHandlingPattern test = new ExceptionHandlingPattern();
try
{
test. TryToMakeJob();
}
catch (MyException ThisException)
{
// изменить начальное состояние
Console. WriteLine(ThisException. Message);
}
//catch() {} …catch() {}
finally
{// финализация. Получилось или нет — востановить среду}
}
}
}
}