708
Глава.25 .Взаимодействие.с.компонентами.WinRT
расширения .NET
Framework, преобразующие модель асинхронного программи-
рования WinRT в более удобную модель программирования C#.
using System; // Необходимо для методов расширения
// из WindowsRuntimeSystemExtensions
...
public async void WinRTAsyncIntro() {
try {
StorageFile file = await KnownFolders.MusicLibrary.GetFileAsync("Song.mp3");
/* Завершение... */
}
catch (OperationCanceledException) { /* Отмена... */ }
catch (SomeOtherException ex) { /* Ошибка... */ }
}
Оператор C#
await
заставляет компилятор провести поиск метода
GetAwaiter
в интерфейсе
IAsyncOperation
, возвращенном методом
GetFileAsync
.
Интерфейс не предоставляет метод
GetAwaiter
, поэтому компилятор ищет метод
расширения. К счастью, разработчик .NET Framework включили в библиотеку
System Runtime WindowsRuntime dll
методы расширения, вызываемые для интер-
фейсов WinRT
IAsyncXxx
.
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static TaskAwaiter GetAwaiter(this IAsyncAction source);
public static TaskAwaiter GetAwaiter(this
IAsyncActionWithProgress source);
public static TaskAwaiter GetAwaiter(this
IAsyncOperation source);
public static TaskAwaiter GetAwaiter(
this IAsyncOperationWithProgress source);
}
}
Во внутренней реализации все эти методы создают объект
TaskCompletionSource
и приказывают объекту
IAsyncXxx
обратиться к методу обратного вызова, который
задает финальное состояние
TaskCompletionSource
при завершении асинхронной
операции. Объект
TaskAwaiter
, возвращаемый методами расширения — то, что
в конечном счете должен получить C#. При завершении асинхронной операции
объект
TaskAwaiter
следит за тем, чтобы код продолжал выполняться через
объект
SynchronizationContext
(см. главу 28), связанный с исходным потоком.
Затем поток выполняет код, сгенерированный компилятором C#, который за-
прашивает свойство
Result
объекта
TaskCompletionSource.Task
; это приводит
к получению результата (
StorageFile
в моем примере), выдаче исключения
OperationCanceledException
в случае отмены или другого исключения в случае
ошибки. Пример внутренней реализации этих методов приведен в конце раздела.
Мы рассмотрели наиболее типичный сценарий вызова асинхронной функции
WinRT API и определения результата. Однако в своем коде я показал, как узнать
об отмене операции, но не объяснил, как на практике производится отмена. Также
709
Проекции.уровня. NET.Framework
остался нерассмотренным вопрос обработки оповещений о ходе выполнения опе-
рации. Чтобы правильно
обработать отмену и оповещения, вместо автоматического
вызова компилятором одного из методов расширения
GetAwaiter
следует явно
вызвать один из методов расширения
AsTask
, также явно определяемых классом
WindowsRuntimeSystemExtensions
.
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static Task AsTask
(this
IAsyncActionWithProgress source,
CancellationToken cancellationToken, IProgress progress);
public static Task AsTask(
this IAsyncOperationWithProgress source,
CancellationToken cancellationToken, IProgress progress);
// Более простые перегруженные версии не показаны
}
}
Итак, пора рассмотреть реализацию в целом. Вот как происходит асинхронный
вызов функций WinRT API с полной поддержкой отмены и оповещений о ходе
выполнения в тех случаях, когда они необходимы:
using S
ystem; // Для AsTask из WindowsRuntimeSystemExtensions
using System.Threading; // Для CancellationTokenSource
internal sealed class MyClass {
private CancellationTokenSource m_cts = new CancellationTokenSource();
// ВНИМАНИЕ: при вызове из потока графического интерфейса
// весь код выполняется в этом потоке:
private async void MappingWinRTAsyncToDotNet(WinRTType someWinRTObj) {
try {
// Предполагается, что XxxAsync возвращает
// IAsyncOperationWithProgress
IBuffer result = await someWinRTObj.XxxAsync(...)
.AsTask(m_cts.Token, new Progress(ProgressReport));
/* Завершение... */
}
catch (OperationCanceledException) { /* Отмена... */ }
catch (SomeOtherException) { /* Ошибка... */ }
}
private void ProgressReport(UInt32 progress) { /* Оповещение... */ }
public void Cancel() { m_cts.Cancel(); } // Вызывается позднее
}
Конечно, многим читателям хотелось бы понять, как методы
AsTask
преобразуют
объект WinRT
IAsyncXxx
в объект .NET Framework
Task
, к которому в конечном
итоге применяется
await
. В следующем коде представлена внутренняя реализация