Существует четыре основных срока службы объектов в императивных приложениях:
- Выражение (временное) время жизни
- Срок службы
- Время жизни состояния (между событиями)
- Срок службы приложения
c # был разработан без поддержки RAII, а затем добавил оператор using в качестве средства обеспечения необходимого механизма для автоматизации времени жизни области. Время жизни приложения - это управляемая область приложения, потому что закрытие приложения примерно настолько детерминировано, насколько это возможно для вызова сборки мусора. Это оставляет время жизни выражения и состояния необработанным с любым языковым механизмом для автоматического детерминированного уничтожения.
Существуют ли общие схемы, которые решают эти фундаментальные потребности в c #? Я уверен, что подобные решения общих проблем уже решались несколько раз, но, глядя в Интернете, я не могу найти никаких статей по этому вопросу.
В c ++ dtors во временных объектах времени жизни используются для предоставления прокси или оберток для выражений и встречаются в различных идиомах для изменения выражений, временного изменения состояний потока и продвинутых оптимизаций, таких как шаблоны выражений. Для состояний общее решение OO состоит в том, чтобы использовать шаблон State и помещать объекты с временем жизни данного состояния внутри объекта состояния в качестве членов. Таким образом, например, если у вас есть окно дисплея для данного состояния, ctor отобразит объект, а dtor удалит его из системы отображения.
Когда я выполняю поиск таких вещей, как «шаблоны выражений» или «шаблон состояний» в Интернете, полученные результаты не включают автоматические вызовы финализатора. Вместо этого я вижу вызовы для очистки состояния, оставленные в виде отдельного вызова функции (поэтому, если у вас есть несколько состояний с экранным объектом, как показано в примере, вы должны вызывать очистку в каждом состоянии, а не просто писать код очистки один раз в объекте, и аналогичным образом запомните это для всех объектов каждого состояния). И я вижу, что будет одним выражением, разбитым на несколько операторов, чтобы включить явное завершение временных значений.
Что касается временных, я обычно склонялся к
(/*expression that builds proxy at some point*/).Dispose();
в пользу многопоточных решений.
Для недавнего проекта для состояний жизни я сделал следующий класс
namespace libAutoDisposable
{
public class AutoDisposable
{
public void AutoDispose()
{
// use reflection to get the fields of this
FieldInfo[] infos = GetType().GetFields();
// loop through members
foreach (FieldInfo info in infos)
{
// now try to call AutoDispose or Dispose on each member
if (typeof(AutoDisposable).IsAssignableFrom(info.FieldType))
{
// get the value object
AutoDisposable value = (AutoDisposable)info.GetValue(this);
// and invoke
value.AutoDispose();
}
else if (typeof(IDisposable).IsAssignableFrom(info.FieldType))
{
// get the value object
IDisposable value = (IDisposable)info.GetValue(this);
// so invoke
value.Dispose();
}
}
}
}
}
, который будет перебирать членов объекта состояния, и все объекты, нуждающиеся в финализации (т.е. производные от IDisposable), будут вызывать Dispose при вызове AutoDispose объекта состояния. Кроме того, я сделал рекурсивным поддержку повторного использования объектов. Это избавляет от необходимости писать код очистки в каждом состоянии, вместо этого позволяя моим автоматам вызывать AutoDispose один раз в коде перехода.
Однако у этого есть ряд недостатков, в том числе:
- Оставляет объекты невостребованными перед лицом исключений
- Определяет методы, вызываемые во время выполнения при каждом переходе состояния, а не один раз за класс (надеюсь) время выполнения или (лучшее из миров) время перевода.
- Подход к отражению базового класса настолько хакерский / навязчивый, насколько это возможно.
Я уверен, что хорошие архитекторы работали над проектами в c # и должны были решить эти проблемы. Какие закономерности эволюционировали, чтобы дать автоматическое уничтожение выражений и состояний жизни?
РЕДАКТИРОВАТЬ: я писал о недостатках моего решения о состоянии, но я забыл упомянуть, почему я нахожу выражение или решение с временным временем жизни, которое я склонен использовать неудовлетворительно. С точки зрения языка домена, вызов Dispose на прокси-сервере идентифицирует выражение как имеющее прокси. Обычно смысл прокси заключается в том, что есть некоторые выражения с ними, а некоторые без них, и то, возвращает ли выражение в некоторой точке прокси, деталь реализации. Например, у вас может быть
mySerialisationStream.Serialise(obj1).Serialise(obj2);
и
mySerialisationStream.Serialise(specialFormatter).Serialise(obj1).Serialise(obj2);
Во втором случае может быть вставлен специальный форматер, который длится длину строки вызовов Serialise, а затем возвращается к форматированию по умолчанию.Необходимость добавить вызов Dispose означает, что я знаю, когда в строке есть специальный объект форматирования, хотя, возможно, я пытаюсь сделать что-то общее.Затем мне нужно либо изменить дизайн, чтобы добавить класс Dispose в MySerialisationStream, когда только прокси-сервер должен предпринимать какие-либо действия.Это добавляет комбинаторную сложность по мере увеличения числа типов, с которыми я работаю в выражении.