И эта реализация имеет проблемы: Раб он как бы не совсем двигатель. В данном случае корректнее будет ввести интерфейс IEngine вместо абстрактного базового класса Engine.
Тогда нужен динамический регистратор. В .NET в сборках банально нету кода, который запускается при загрузке её в домен приложения, то есть аналога DllMain.
У меня в проектах достаточно часто встречается сборка всех потомков рефлексией. Но не в статическом конструкторе, а через статическое свойство, которое инициализирует список потомков лениво, при первом запросе.
Эта практика хорошо работает, если надо делать поддержку различных входных данных или выбор пользователем каких-то вещей.
Сделать всё на основе прямых вызовов, когда проект не монолитный и допускает расширения, всё равно не получится.
Такое решение имеет другую проблему: в какой момент и кто должен вызвать этот метод?
В некоторых скриптовых языках базовый класс может получить уведомление о том, что в области видимости появился его потомок. В .NET такое сделать невозможно в силу ленивости сборок и классов.
Зависимость базового класса от потомков в последнем примере — в общем-то не слишком хорошо.
Однако этот код может стать основой для загрузки типов динамически с помощью рефлексии по модели плагинов. Но это уже совсем другая история.
Кстати, в той реализации, которую Вы здесь видите, у таски есть состояния «активна», «заблокирована» и «завершена» и планировщик по-разному себя с ними ведёт.
Там выше есть строчка:
#define TASK_WAIT_FOR(Object) this->WaitFor(Object); this->state = __LINE__; return false; case __LINE__:
Так вот, WaitFor указывает планировщику, что таска заблокирована на указанном объекте, после чего сохраняется состояние. Выполнение с точки case __LINE__: начнётся после того, как объект станет сигнальным. Это чем-то напоминает дескрипторы, на которых выполняются блокирующие вызовы в операционке.
Это прерывания по приходу внешних сигналов. Всего же векторов прерываний куда больше. Только надо иметь в виду, что прерывания таймеров могут использоваться какими-то библиотеками, которые «застолбили» их за собой.
Поздравляю, Вы изобрели очередную простенькую кооперативную многозадачность.
Основная проблема здесь в том, что семантика его использования достаточно страшная и непонятная.
Если сделать нечто наподобие такого (код большой, прячу под спойлер и отбрасываю много чего)
Что-то мешает иметь в посылке с координатами ещё и время формирования этой посылки, и считать в этой самой шкале времени, а не по событиям передачи данных?
По-простому не выходит.
Эта практика хорошо работает, если надо делать поддержку различных входных данных или выбор пользователем каких-то вещей.
Сделать всё на основе прямых вызовов, когда проект не монолитный и допускает расширения, всё равно не получится.
В некоторых скриптовых языках базовый класс может получить уведомление о том, что в области видимости появился его потомок. В .NET такое сделать невозможно в силу ленивости сборок и классов.
Однако этот код может стать основой для загрузки типов динамически с помощью рефлексии по модели плагинов. Но это уже совсем другая история.
Там выше есть строчка:
#define TASK_WAIT_FOR(Object) this->WaitFor(Object); this->state = __LINE__; return false; case __LINE__:
Так вот, WaitFor указывает планировщику, что таска заблокирована на указанном объекте, после чего сохраняется состояние. Выполнение с точки case __LINE__: начнётся после того, как объект станет сигнальным. Это чем-то напоминает дескрипторы, на которых выполняются блокирующие вызовы в операционке.
Основная проблема здесь в том, что семантика его использования достаточно страшная и непонятная.
Если сделать нечто наподобие такого (код большой, прячу под спойлер и отбрасываю много чего)
Да, макросы, но результат явно менее страшный, чем кодирование руками конечного автомата.
Как-то для pet-проектов 250$ — это дороговато.