Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Если вы пишете класс с некоторыми операциями, которые могут привести к заметным задержкам, рассмотрите возможность предоставления ему асинхронной функциональности путем реализации асинхронного шаблона на основе событий.
Асинхронный шаблон на основе событий предоставляет стандартный способ упаковки класса с асинхронными функциями. При реализации с вспомогательными классами, например AsyncOperationManager, класс будет работать правильно в любой модели приложения, включая ASP.NET, консольные приложения и приложения Windows Forms.
Пример реализации асинхронного шаблона на основе событий см. в статье "Практическое руководство. Реализация компонента, поддерживающего асинхронный шаблон на основе событий".
Для простых асинхронных операций можно найти подходящий BackgroundWorker компонент. Дополнительные сведения смотрите в разделе BackgroundWorker.
В следующем списке описываются функции асинхронного шаблона на основе событий, описанные в этом разделе.
Возможности реализации асинхронного шаблона на основе событий
Именование асинхронных методов
Поддержка отмены по желанию
Поддержка свойства IsBusy по выбору
При необходимости обеспечьте поддержку для представления отчетов о прогрессе
При необходимости предоставляется поддержка возврата добавочных результатов
Обработка параметров out и Ref в методах
Возможности реализации асинхронного шаблона на основе событий
Рассмотрите возможность реализации асинхронного шаблона на основе событий, когда:
Клиенты вашего класса не нуждаются в том, чтобы объекты WaitHandle и IAsyncResult были доступны для асинхронных операций, что означает, что для опроса и использования WaitAll или WaitAny клиенту потребуется самостоятельно их разработать.
Вы хотите, чтобы асинхронными операциями управлял клиент, используя знакомую модель событий и делегатов.
Любая операция является кандидатом на асинхронную реализацию, но те, которые, по вашему мнению, будут испытывать длительные задержки, следует рассмотреть. Особенно подходящими являются операции, в которых клиенты вызывают метод и уведомляются о завершении без дальнейшего вмешательства. Кроме того, это операции, которые выполняются непрерывно, периодически уведомляя клиентов о ходе выполнения, добавочных результатах или изменениях состояния.
Дополнительные сведения о принятии решения о поддержке асинхронного шаблона на основе событий см. в статье "Выбор момента реализации асинхронного шаблона на основе событий".
Именование асинхронных методов
Для каждого синхронного метода MethodName , для которого требуется предоставить асинхронный аналог:
Определите асинхронный метод MethodName, который:
Возвращает
void
.Принимает те же параметры, что и метод MethodName .
Принимает несколько вызовов.
При необходимости определите перегрузку MethodNameAsync, идентичную AsyncMethodName, но с дополнительным параметром, который имеет userState
значение объекта. Если вы готовы управлять несколькими одновременными вызовами вашего метода, то значение userState
будет передано всем обработчикам событий для различения этих вызовов метода. Вы также можете сделать это просто в качестве места для хранения пользовательского состояния для последующего извлечения.
Для каждой отдельной подписи метода MethodNameAsync :
Определите следующее событие в том же классе, что и метод:
Public Event MethodNameCompleted As MethodNameCompletedEventHandler
public event MethodNameCompletedEventHandler MethodNameCompleted;
Определите следующий делегат и AsyncCompletedEventArgs. Они, скорее всего, будут определены вне самого класса, но в том же пространстве имен.
Public Delegate Sub MethodNameCompletedEventHandler( _ ByVal sender As Object, _ ByVal e As MethodNameCompletedEventArgs) Public Class MethodNameCompletedEventArgs Inherits System.ComponentModel.AsyncCompletedEventArgs Public ReadOnly Property Result() As MyReturnType End Property
public delegate void MethodNameCompletedEventHandler(object sender, MethodNameCompletedEventArgs e); public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { public MyReturnType Result { get; } }
Убедитесь, что класс MethodNameCompletedEventArgs предоставляет свои члены как свойства только для чтения, а не поля, так как поля препятствуют привязке данных.
Не определяйте производные AsyncCompletedEventArgsклассы для методов, которые не создают результаты. Просто используйте сам экземпляр AsyncCompletedEventArgs .
Замечание
Повторное использование делегатов и типов AsyncCompletedEventArgs совершенно приемлемо, если это возможно и уместно. В этом случае именование не будет совпадать с именем метода, так как заданный делегат и AsyncCompletedEventArgs не будет привязан к одному методу.
Поддержка отмены по желанию
Если класс будет поддерживать отмену асинхронных операций, отмена должна быть предоставлена клиенту, как описано ниже. Прежде чем определить поддержку отмены, необходимо пройти два этапа принятия решения.
- Имеет ли класс, включая будущие ожидаемые дополнения к нему, только одну асинхронную операцию, которая поддерживает отмену?
- Могут ли асинхронные операции, которые поддерживают отмену, также поддерживать выполнение нескольких ожидающих операций одновременно? То есть, принимает ли метод MethodNameAsync параметр
userState
и позволяет ли он несколько вызовов, прежде чем ожидать завершения каждого?
Используйте ответы на эти два вопроса в таблице ниже, чтобы определить, какая подпись для метода отмены должна быть.
Visual Basic
Поддерживается несколько одновременных операций | Только одна операция за раз | |
---|---|---|
Одна асинхронная операция во всем классе | Sub MethodNameAsyncCancel(ByVal userState As Object) |
Sub MethodNameAsyncCancel() |
Несколько асинхронных операций в классе | Sub CancelAsync(ByVal userState As Object) |
Sub CancelAsync() |
C#
Поддерживается несколько одновременных операций | Только одна операция за раз | |
---|---|---|
Одна асинхронная операция во всем классе | void MethodNameAsyncCancel(object userState); |
void MethodNameAsyncCancel(); |
Несколько асинхронных операций в классе | void CancelAsync(object userState); |
void CancelAsync(); |
При определении CancelAsync(object userState)
метода клиенты должны быть осторожны при выборе значений состояния, чтобы сделать их способными различать все асинхронные методы, вызываемые в объекте, а не только между всеми вызовами одного асинхронного метода.
Решение назвать версию MethodNameAsyncCancel с одной асинхронной операцией основано на том, что позволяет проще обнаружить метод в среде разработки, такой как IntelliSense Visual Studio. Это группирует связанные члены и отличает их от других членов, которые не имеют ничего общего с асинхронной функциональностью. Если вы ожидаете, что в последующих версиях могут быть добавлены дополнительные асинхронные операции, лучше определить CancelAsync
.
Не определяйте несколько методов из приведенной выше таблицы в одном классе. Это не имеет смысла или приведет к загромождению интерфейса класса избыточным количеством методов.
Эти методы обычно возвращаются немедленно, и операция может или не может на самом деле отменить. В обработчике события MethodNameCompleted, объект MethodNameCompletedEventArgs содержит Cancelled
поле, которое клиенты могут использовать для определения того, произошла ли отмена.
Соблюдайте семантику отмены, описанную в рекомендациях по реализации асинхронного шаблона на основе событий.
Поддержка свойства IsBusy по выбору
Если класс не поддерживает несколько одновременных вызовов, рассмотрите возможность предоставления IsBusy
свойства. Это позволяет разработчикам определить, выполняется ли метод MethodNameAsync без необходимости перехватывать исключение из метода MethodNameAsync.
Соблюдайте IsBusy
семантику, описанную в рекомендациях по реализации асинхронного шаблона на основе событий.
При необходимости обеспечьте поддержку для представления отчетов о прогрессе
Часто рекомендуется сообщать о ходе выполнения асинхронной операции. Асинхронный шаблон на основе событий предоставляет руководство по выполнению этой задачи.
При необходимости определите событие, вызываемое асинхронной операцией и вызываемое в соответствующем потоке. Объект ProgressChangedEventArgs содержит целочисленный индикатор хода выполнения, который, как ожидается, составляет от 0 до 100.
Присвойте этому событию имя следующим образом:
ProgressChanged
если класс имеет несколько асинхронных операций (или ожидается, что в будущих версиях он будет расширяться и включать несколько асинхронных операций);MethodNameProgressChanged , если класс имеет одну асинхронную операцию.
Этот выбор имени аналогичен тому, что был сделан для метода отмены, как описано в разделе "Опционально поддержка отмены".
Это событие должно использовать подпись делегата ProgressChangedEventHandler и класс ProgressChangedEventArgs. Кроме того, если можно указать более конкретный для домена индикатор хода выполнения (например, прочитанные байты и общие байты для операции загрузки), то следует определить производный класс ProgressChangedEventArgs.
Обратите внимание, что для класса существует только одно ProgressChanged
или событие MethodNameProgressChanged , независимо от количества асинхронных методов, которые он поддерживает. Ожидается, что клиенты будут использовать userState
объект, передаваемый в методы MethodNameAsync, чтобы различать обновления хода выполнения для нескольких параллельных операций.
Могут возникнуть ситуации, в которых несколько операций способствуют прогрессу, и каждая из них возвращает разные индикаторы для отображения хода выполнения. В этом случае одно ProgressChanged
событие не подходит, и вы можете рассмотреть возможность поддержки нескольких ProgressChanged
событий. В этом случае используйте шаблон именования MethodNameProgressChanged для каждого метода MethodNameAsync.
Соблюдайте принципы отчетности о ходе выполнения, как описано в Рекомендациях по реализации асинхронного шаблона на основе событий.
При необходимости предоставляется поддержка возврата добавочных результатов
Иногда асинхронная операция может возвращать добавочные результаты до завершения. Существует ряд вариантов, которые можно использовать для поддержки этого сценария. Ниже приведены некоторые примеры.
Класс с одной операцией
Если класс поддерживает только одну асинхронную операцию, и эта операция может возвращать добавочные результаты, то:
ProgressChangedEventArgs Расширьте тип для передачи добавочных данных результатов и определите событие MethodNameProgressChanged с помощью этих расширенных данных.
Вызовите это событие MethodNameProgressChanged, если имеется промежуточный результат для отчета.
Это решение применяется специально к классу единственной асинхронной операции, поскольку нет проблем с возвратом добавочных результатов с помощью того же события для "всех операций", как это делает событие MethodNameProgressChanged.
Класс с несколькими операциями с однородными добавочными результатами
В этом случае класс поддерживает несколько асинхронных методов, каждый из которых способен возвращать добавочные результаты, и эти добавочные результаты имеют одинаковый тип данных.
Следуйте описанной выше модели для классов с одной операцией, так как одна и та же EventArgs структура будет работать для всех добавочных результатов. Определите ProgressChanged
событие вместо события MethodNameProgressChanged , так как оно применяется к нескольким асинхронным методам.
Класс с несколькими операциями с разнородными добавочными результатами
Если класс поддерживает несколько асинхронных методов, каждый из которых возвращает разные типы данных, необходимо:
Отделите добавочные отчеты о результатах от отчетов о ходе выполнения.
Определите отдельное событие MethodNameProgressChanged с соответствующим EventArgs для каждого асинхронного метода для обработки добавочных данных результата этого метода.
Вызовите этот обработчик событий в соответствующем потоке, как описано в рекомендациях по реализации асинхронного шаблона на основе событий.
Обработка параметров out и Ref в методах
Хотя использование out
и ref
является, как правило, не рекомендуется в .NET, ниже приведены правила, которые следует соблюдать, когда они присутствуют:
Учитывая синхронный метод MethodName:
out
параметры methodName не должны быть частью AsyncmethodName. Вместо этого они должны быть частью MethodNameCompletedEventArgs с тем же именем, что и его эквивалент параметра в MethodName (если нет более подходящего имени).ref
параметры MethodName должны отображаться как часть MethodNameAsync и как часть MethodNameCompletedEventArgs с тем же именем, что и их параметр-эквивалент в MethodName (если нет более подходящего имени).
Например, если:
Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);
Асинхронный метод и его AsyncCompletedEventArgs класс будут выглядеть следующим образом:
Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)
Public Class MethodNameCompletedEventArgs
Inherits System.ComponentModel.AsyncCompletedEventArgs
Public ReadOnly Property Result() As Integer
End Property
Public ReadOnly Property Arg2() As String
End Property
Public ReadOnly Property Arg3() As String
End Property
End Class
public void MethodNameAsync(string arg1, string arg2);
public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public int Result { get; };
public string Arg2 { get; };
public string Arg3 { get; };
}
См. также
- ProgressChangedEventArgs
- AsyncCompletedEventArgs
- Практическое руководство. Реализация компонента, поддерживающего асинхронный шаблон на основе событий
- Как выполнить операцию в фоновом режиме
- Как создать форму, использующую фоновую операцию
- Выбор момента реализации асинхронного шаблона на основе событий
- Рекомендации по реализации асинхронного шаблона на основе событий
- Шаблон асинхронного программирования на основе событий (EAP)