Динамические методы как поддержка полиморфизма
2) Нет, если методы предка объявлены как статические (static), что является опцией по умолчанию.
Динамические методы как поддержка полиморфизма — опции static, virtual, override
В синтаксисе переменный тип связан с:
— Virtual — виртуальный (динамический)- доступный для переопределения потомков (перекрытие методов базового класса)
—Override — метод, который переопределяет родительский (перекрытие методов в производном классе)
—Static-функции — функции, которые могут быть вызваны без определения
объектов. Постоянная ссылка (всегда указывает на одно и то же определение функции, т. е. она однозначно связана с каким — то определением).
Можно ли наследовать от класса, содержащего точку входа?
Да, можно. Точка входа должна быть статическим методом, но этот метод может и не принадлежать статическому классу. (Ключевое слово static означает невозможность создания экземпляров класса, но его методы доступны с момента запуска программы). Такой метод можно вызвать из любого другого метода без объявления ссылочной переменной или создания экземпляров при помощи оператора new (т. е. метод можно вызывать без создания объекта).
В соответствии со строгой типизацией фактические и формальные параметры должны соответствовать по числу и типу, т. е. попытка «засунуть» не тот тип кончается печально. Прелесть полиморфизма заключается в том, что здесь в качестве параметра может выдаваться cAnimal и все предки, т. е. в предке можно описывать общие методы для всех потомков.
Пример: Функционирование блох и динозавров 🙂
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");
}
} // 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"); //блоха
}
} //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("______________________");
Console. ReadKey(); //задержка
//Распечатываем имя динозавра, говорим, что этот динозавр боится прыжков конкретной блохи (выдаётся имя блохи)
}
}
}
Мощь полиморфизма и его сложность заключается в том, что при создании объекта и вызове его в метод, имя связано неоднозначно с реализацией (т. е. имя метода присутствует во всей иерархии).
-если метод статичный, то связь имени с реализацией постоянная (т. е. будет вызываться одна и та же реализация)
-если метод переопределяемый, то возникают нюансы (какая же реализация будет работать?).
В примере переопределяются в потомках все методы, кроме Attacks (нападения), вызывается метод нападения, где блоха набрасывается на динозавра.
Bug. Attacks(Dino); — не является синтаксической ошибкой (хотя в скобках должен быть параметр Animal типа cAnimal )
cAnimal
Attacks
cDinosaur cFlea
cTRex
У блохи не написан метод Attacks. Значит, при вызове виртуального метода берётся ближайшее переопределение.
Таким образом, определяя функцию на Animal, автоматически определяем функции на всех его предках.
Событийное программирование
Обработка событий
Интуитивно, событие (event) – это случай, факт изменения, который всегда можно трактовать как изменение состояния.
Идея событийного программирования отнюдь не нова:
Понятие автомата — поведение как реакция на события. Автомат реагирует, когда поступает поток входных сигналов, изменяя при этом собственные состояния и выдавая что-то на выход.
Событием является сам вызов функции.
Стоит упомянуть о теореме об универсальной функции: