Смотрите на это сообщение Udi Dahan. Изящный подход для диспетчеризации доменных событий. Предыдущий плакат корректен в высказывании, что Вы не должны использовать механизм события для восстановления с фатальных ошибок, но это - очень полезный шаблон для уведомления в слабо связанных системах:
public class DomainEventStorage<ActionType>
{
public List<ActionType> Actions
{
get
{
var k = string.Format("Domain.Event.DomainEvent.{0}.{1}",
GetType().Name,
GetType().GetGenericArguments()[0]);
if (Local.Data[k] == null)
Local.Data[k] = new List<ActionType>();
return (List<ActionType>) Local.Data[k];
}
}
public IDisposable Register(ActionType callback)
{
Actions.Add(callback);
return new DomainEventRegistrationRemover(() => Actions.Remove(callback)
);
}
}
public class DomainEvent<T1> : IDomainEvent where T1 : class
{
private readonly DomainEventStorage<Action<T1>> _impl = new DomainEventStorage<Action<T1>>();
internal List<Action<T1>> Actions { get { return _impl.Actions; } }
public IDisposable Register(Action<T1> callback)
{
return _impl.Register(callback);
}
public void Raise(T1 args)
{
foreach (var action in Actions)
{
action.Invoke(args);
}
}
}
И использовать:
var fail = false;
using(var ev = DomainErrors.SomethingHappened.Register(c => fail = true)
{
//Do something with your domain here
}
Это кажется нечетным мне также. Существует несколько преимуществ - таких как разрешение нескольких "обработчиков", но семантика существенно отличается к нормальной обработке ошибок. В частности, то, что это автоматически не становится распространенным стек, касается меня - если сами обработчики ошибок не выдают исключение, логика собирается продолжать идти, как будто все было все еще хорошо, когда это должно, вероятно, прерывать текущую операцию.
Другой образ мыслей об этом: предположите, что метод предназначен для возвращения значения, но Вы обнаружили ошибку рано. Какое значение Вы возвращаете? Исключения передают то, что нет никакого соответствующего значения для возврата...
Это выглядит действительно нечетным мне, во-первых IDisposable является Вашим другом, используйте его.
Если Вы имеете дело с ошибками и исключительными ситуациями, необходимо использовать исключения, не события, как его намного более простое, чтобы схватить, отладить и кодировать.
Таким образом, это должно быть
using(var resource = AllocateLotsOfMemory())
{
if(something_bad_happened)
{
throw new SomeThingBadException();
}
}
Если Вы думаете с точки зрения "Ошибок" и "Предупреждений", у меня была большая удача при резервировании событий для категории "Warning" и Исключений для категории "Errors".
Объяснение здесь - то, что события являются дополнительными. Никто не держит оружие к Вашей голове, вынуждающей Вас обработать их. Это, вероятно, хорошо для предупреждений, но когда у Вас есть подлинные ошибки, Вы хотите удостовериться, что к ним относятся немного больше серьезно. Исключения должны быть обработаны, или они будут пузыриться и создавать противное сообщение для пользователя.
Относительно Вашего Большого Объектного вопроса: Вы определенно не раздающие большие объекты, но это не означает, что Вы не можете передать ссылки на большие объекты вокруг. Существует много питания в способности сделать это.
Как приложение, нет ничего останавливающегося от от генерирования события в дополнение к исключению, но снова: если у Вас есть подлинная ошибка, Вы хотите, чтобы что-то вынудило разработчика клиента обработать ее.
1) это необходимо? никакой шаблон не абсолютно необходим
2) Windows Workflow Foundation делает это со всеми результатами Экземпляров Рабочего процесса, работающих в размещенном времени выполнения. Просто помните, что исключения могут произойти при попытке сгенерировать то событие, и Вы могли бы хотеть сделать свой код очистки Расположения или наконец блок в зависимости от ситуации, чтобы гарантировать, что это работает.
Честно говоря, события сигнальные ошибки кажутся мне страшный.
Существует разногласие между лагерями вокруг возврата кодов состояния и выдавания исключения. Упростить (значительно): лагерь кода состояния говорит что выдающее исключения обнаружение мест и обработка ошибки, слишком далекой от кода, вызывающего ошибку. Ограничение броска исключения говорит, что пользователи забывают проверять коды состояния, и исключения осуществляют обработку ошибок.
Ошибки как события походят на худший из обоих подходов. Ошибочная очистка является абсолютно отдельной от кода, вызывающего ошибку, и уведомление об ошибке абсолютно добровольно. Ай.
Мне, если метод не выполнил, это - неявный или явный контракт (это не сделало то, что это, как предполагалось, сделало), исключением является соответствующий ответ. Бросок информации, в которой Вы нуждаетесь в исключении, кажется разумным в этом случае.
Другой основной проблемой с этим подходом являются проблемы параллелизма.
С традиционной обработкой ошибок будут выпущены блокировки, поскольку управление перемещает стек вызовов вверх на обработчик ошибок управляемым способом. В этой схеме будут все еще сохранены все блокировки, когда событие будет вызвано. Любое блокирование, которое происходит в рамках обработчика ошибок (и Вы могли бы ожидать некоторых, если бы там регистрируется) было бы потенциальным источником мертвых блокировок.
У нас есть основной Ошибочный объект и ErrorEvent, который мы используем с шаблоном "команда" в нашей платформе для обработки некритических ошибок (например, ошибок проверки). Как исключения, люди могут прислушаться к основному ErrorEvent или более определенному ErrorEvent.
Также между Вашими двумя отрывками существует значительная разница.
если ресурс. FreeLotsOfMemory () убирает значение StatusProperty вместо того, чтобы просто установить его в NULL, Ваша временная переменная будет содержать недопустимый объект, когда OddException будет создан и брошен.
Эмпирическое правило - то, что Исключения должны только быть выданы в невосстанавливаемых ситуациях. Мне действительно жаль, что C# не поддерживал пункт Бросков, это - единственная вещь, которую я действительно пропускаю от Java.
Первый фрагмент, вероятно, должен быть
resource = AllocateLotsOfMemory();
if (SomeCondition())
{
try
{
OnOddError(new OddErrorEventArgs(resource.StatusProperty));
return;
}
finally
{
resource.FreeLotsOfMemory();
}
}
, иначе вы не освободитесь ваши ресурсы, когда обработчик событий генерирует исключение.
Как сказал Майк Браун, второй фрагмент также имеет проблему, если resource.FreeLotsOfMemory ()
не соответствует содержимому resource.StatusProperty
] вместо установки значения null
.