Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В предыдущей статье рассматриваются наиболее распространенные шаблоны событий. .NET Core имеет более расслабленный шаблон. В этой версии определение EventHandler<TEventArgs>
больше не имеет ограничения, что TEventArgs
должен быть классом, производным от System.EventArgs
.
Это повышает гибкость для вас и обеспечивает обратную совместимость. Начнем с гибкости. Реализация для System.EventArgs использует метод, определенный в System.Object, в одном методе: MemberwiseClone(), которая создает неглубокую копию объекта. Этот метод должен использовать отражение для реализации функций любого класса, производного от EventArgs
. Эта функция проще создавать в определенном производном классе. Это фактически означает, что наследование от System.EventArgs является ограничением, которое сужает возможности ваших разработок, но при этом не дает никаких дополнительных преимуществ. На самом деле можно изменить определения FileFoundArgs
и SearchDirectoryArgs
, чтобы они не были производными от EventArgs
. Программа работает точно так же.
Вы также можете изменить SearchDirectoryArgs
на структуру, если внести еще одно изменение:
internal struct SearchDirectoryArgs
{
internal string CurrentSearchDirectory { get; }
internal int TotalDirs { get; }
internal int CompletedDirs { get; }
internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
{
CurrentSearchDirectory = dir;
TotalDirs = totalDirs;
CompletedDirs = completedDirs;
}
}
Дополнительное изменение заключается в вызове конструктора без параметров перед вводом конструктора, который инициализирует все поля. Без этого дополнения правила C# указывают на то, что обращение к свойствам происходит до их присвоения.
Не следует изменять FileFoundArgs
из класса (ссылочного типа) на структуру (тип значения). Протокол для обработки отмены требует передачи аргументов событий по ссылке. Если вы внесли то же изменение, класс поиска файлов никогда не мог наблюдать никаких изменений, внесенных любым из подписчиков событий. Новая копия структуры будет использоваться для каждого подписчика, и эта копия будет отличаться от копии, которую видел объект поиска файлов.
Далее рассмотрим, как это изменение может быть обратно совместимым. Удаление ограничения не влияет на существующий код. Все существующие типы аргументов событий по-прежнему являются производными от System.EventArgs
. Обратная совместимость является одной из основных причин, почему они продолжают использовать System.EventArgs
. Все существующие подписчики событий являются подписчиками на событие, которое следует классическому образцу.
После аналогичной логики любой тип аргумента события, созданный сейчас, не будет иметь подписчиков в существующих базах кода. Новые типы событий, не производные от System.EventArgs
, не нарушают эти базы кода.
События с подписчиками Async
Последний шаблон, который вам нужно изучить: как правильно писать подписчиков событий, которые вызывают асинхронный код. Задача описана в статье об асинхронном асинхронном и ожидании. Асинхронные методы могут иметь тип возвращаемого значения void, но этого следует избегать. Если код подписчика события вызывает асинхронный метод, у вас нет выбора, кроме создания метода async void
. Требуется подпись обработчика событий.
Вам нужно примирить эти противоречивые указания. Как-то необходимо создать безопасный метод async void
. Основы шаблона, который необходимо реализовать, показаны в следующем коде:
worker.StartWorking += async (sender, eventArgs) =>
{
try
{
await DoWorkAsync();
}
catch (Exception e)
{
//Some form of logging.
Console.WriteLine($"Async task failure: {e.ToString()}");
// Consider gracefully, and quickly exiting.
}
};
Сначала обратите внимание, что обработчик помечается как асинхронный обработчик. Поскольку он назначается делегату типа обработчика событий, он имеет возвращаемое значение типа void. Это означает, что необходимо выполнить шаблон, показанный в обработчике, и не разрешать исключение из контекста асинхронного обработчика. Так как он не возвращает задачу, нет задачи, которая может сообщить об ошибке, войдя в состояние сбоя. Так как метод является асинхронным, метод не может вызвать исключение. (Вызывающий метод продолжает выполнение, так как он async
.) Фактическое поведение среды выполнения определяется по-разному для разных сред. Он может завершить поток или процесс, принадлежащий потоку, или оставить процесс в неопределенном состоянии. Все эти потенциальные результаты являются весьма нежелательными.
Необходимо упаковать выражение await
для асинхронной задачи в собственном блоке try. Если это приведет к сбою задачи, можно записать ошибку. Если это ошибка, из которой приложение не может восстановиться, вы можете быстро и корректно выйти из программы.
В этой статье описаны основные обновления шаблона событий .NET. Вы можете увидеть множество примеров предыдущих версий в библиотеках, с которыми вы работаете. Тем не менее, вы должны понять, какие сейчас последние тенденции. Готовый код для примера можно увидеть на Program.cs.
В следующей статье этой серии вы узнаете, как использовать delegates
и events
в проектах. Они похожие понятия, и эта статья помогает вам принять лучшее решение для ваших программ.