Как стать автором
Обновить
14
0.1
Алексей Ткаченко @a-tk

Разработчик ПО

Отправить сообщение
Есть ещё один прикол. Если стоит задача переключения языка на лету, то тут наступает огромное разочарование. Приведу несколько кусочков кода компонента PropertyGrid, они хорошо иллюстрируют всю боль происходящего.

public abstract class MemberDescriptor {
  public virtual string DisplayName {
    get {
      DisplayNameAttribute displayNameAttr = Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
      if (displayNameAttr == null || displayNameAttr.IsDefaultAttribute()) {
        return displayName;
      }   
      return displayNameAttr.DisplayName;
    }
  }

  public virtual string Description {
    get {
      if (description == null) {
        description = ((DescriptionAttribute) Attributes[typeof(DescriptionAttribute)]).Description;
      }
      return description;
    }
  }

  public virtual string Category {
    get {
      if (category == null) {
        category = ((CategoryAttribute)Attributes[typeof(CategoryAttribute)]).Category;
      }
      return category;
    }
  }
}


Если вкратце, то у однажды загруженных свойств описание кэшируется и при смене языка остаётся навсегда тем же. С категорией то же самое. А вот показываемое имя изменяется.
Короче, боль.
Подскажите как локализовать то, что аннотируется через DisplayName/Description/(Category).
По-простому не выходит.
И эта реализация имеет проблемы: Раб он как бы не совсем двигатель. В данном случае корректнее будет ввести интерфейс IEngine вместо абстрактного базового класса Engine.
Тогда нужен динамический регистратор. В .NET в сборках банально нету кода, который запускается при загрузке её в домен приложения, то есть аналога DllMain.
У меня в проектах достаточно часто встречается сборка всех потомков рефлексией. Но не в статическом конструкторе, а через статическое свойство, которое инициализирует список потомков лениво, при первом запросе.
Эта практика хорошо работает, если надо делать поддержку различных входных данных или выбор пользователем каких-то вещей.
Сделать всё на основе прямых вызовов, когда проект не монолитный и допускает расширения, всё равно не получится.
Такое решение имеет другую проблему: в какой момент и кто должен вызвать этот метод?
В некоторых скриптовых языках базовый класс может получить уведомление о том, что в области видимости появился его потомок. В .NET такое сделать невозможно в силу ленивости сборок и классов.
Зависимость базового класса от потомков в последнем примере — в общем-то не слишком хорошо.
Однако этот код может стать основой для загрузки типов динамически с помощью рефлексии по модели плагинов. Но это уже совсем другая история.
Кстати, в той реализации, которую Вы здесь видите, у таски есть состояния «активна», «заблокирована» и «завершена» и планировщик по-разному себя с ними ведёт.
Там выше есть строчка:
#define TASK_WAIT_FOR(Object) this->WaitFor(Object); this->state = __LINE__; return false; case __LINE__:
Так вот, WaitFor указывает планировщику, что таска заблокирована на указанном объекте, после чего сохраняется состояние. Выполнение с точки case __LINE__: начнётся после того, как объект станет сигнальным. Это чем-то напоминает дескрипторы, на которых выполняются блокирующие вызовы в операционке.
Это прерывания по приходу внешних сигналов. Всего же векторов прерываний куда больше. Только надо иметь в виду, что прерывания таймеров могут использоваться какими-то библиотеками, которые «застолбили» их за собой.
Обратите внимание на конструкцию вида
bool TaskFunc(int &state)
{
switch (state)
{
case 0: // начальное
//
  state = __LINE__; return false; case __LINE__: // в одну строку.
default:
  state = -1;
  return true;
}
}
Поздравляю, Вы изобрели очередную простенькую кооперативную многозадачность.
Основная проблема здесь в том, что семантика его использования достаточно страшная и непонятная.
Если сделать нечто наподобие такого (код большой, прячу под спойлер и отбрасываю много чего)
Фрагмент .h-файла
#define TASK_CLASS(TypeName) TypeName

#define TASK_BEGIN(TypeName, Locals) class TASK_CLASS(TypeName) : public StatefullTaskBase { \
private: \
	struct Locals; \
public: \
	virtual bool Step() override { \
switch (this->state) {	\
case -1: return true; \
case 0:


#define TASK_BODY_END ;} return true; }
#define TASK_CLASS_END };

#define TASK_END TASK_BODY_END TASK_CLASS_END

#define TASK_YIELD() this->state = __LINE__; return false; case __LINE__:

#define TASK_WAIT_FOR(Object) this->WaitFor(Object); this->state = __LINE__; return false; case __LINE__:

#define TASK_YIELD_WHILE(cond) this->state = __LINE__; case __LINE__: if ((cond)) return false;


#define SECOND *1000LL
#define SECONDS SECOND
#define MINUTE *(60LL*1000LL)
#define MINUTES MINUTE
#define HOUR *(3600LL*1000LL)
#define HOURS HOUR

#define TASK_SLEEP(timeout) this->sleep.Start(timeout); TASK_WAIT_FOR(&this->sleep);

#define TASK_PERIODICALLY(period, action) for (;;) {this->sleep.Start(period); action; TASK_WAIT_FOR(&this->sleep);}

#define TASK_POLL(action) for(;;) {action; TASK_YIELD();}

#define TASK_WAIT_CONDITION(callback) TASK_WAIT_FOR(callback)

#define TASK_WAIT_SIGNAL(hSignal) TASK_WAIT_FOR(hSignal)

#define TASK_SET_SIGNAL(hSignal) hSignal->Set()

#define TASK_WAIT_VALUE(hValueHolder, variable) TASK_WAIT_FOR(hValueHolder);  variable = hValueHolder->Get();

#define TASK_SET_VALUE(hValueHolder, value) hValueHolder->Set(value);


, то можно делать независимые таски в таком духе:
Пример таски
DEFINE_TELEMETRY(PowerMonitorRecord)
{
	u16 Value;
};

TASK_BEGIN(PowerMonitorTask, {})
TASK_PERIODICALLY(5 SECONDS,
	telemetry << CreateRecord(GetState())
);
TASK_BODY_END


PowerMonitorRecord GetState()
{
	return PowerMonitorRecord{ 0 };
}


TASK_CLASS_END


Да, макросы, но результат явно менее страшный, чем кодирование руками конечного автомата.
Более того, плоха та работа, где развиваться приходится только вне работы.
Именно. Первое.
Вы пробовали многотонную тушу экстренно затормозить?
Да особой интриги нет. Есть буквально 2-3 области, где людей нет или их количество измеряется 3-6. Притом с шагом 3 :)
Что-то мешает иметь в посылке с координатами ещё и время формирования этой посылки, и считать в этой самой шкале времени, а не по событиям передачи данных?
Как хорошо, что там, где находится наше embedded, людей нет. А те 3-6 человек, которые есть, сами уворачиваются :)
Великолепно. Жду поста как приглашение к занятной дискуссии :)
А вы не прикидывали, пойдёт ли вариант распространения продукта на подписках?
Как-то для pet-проектов 250$ — это дороговато.
Здорово, очень хотелось бы почитать. Авось что-то новое о нас узнаю :)

Информация

В рейтинге
3 535-й
Откуда
Минск, Минская обл., Беларусь
Дата рождения
Зарегистрирован
Активность

Специализация

Десктоп разработчик, Архитектор программного обеспечения
Ведущий
От 1 000 000 $
C#
.NET
C++
Git