Простейшая реализация шаблона await
public class MyAwaitableType
{
public MinimalAwaiter GetAwaiter()
{
return new MinimalAwaiter();
}
public class MinimalAwaiter : INotifyCompletion
{
public bool IsCompleted => true;
public string GetResult() => "This is a result";
public void OnCompleted(Action continuation)
{
throw new NotImplementedException();
}
}
}
1 Ұнайды
Делегаты — это специальные типы в .NET, и они работают совсем не так, как классы или структуры. Компилятор создает внешне нормально выглядящее определение класса с различными членами, которые мы рассмотрим более подробно позже, но все члены пусты — C # не создает промежуточного языка ни для одного из них. CLR предоставляет их реализацию во время выполнения.
Разберем листинг 9.3 по частям. Начинаем мы, как обычно, с определений типов, доступности и можем при этом использовать те же ключевые слова, которые используем для других типов, например public или internal. (Как и любой тип, типы делегатов могут быть вложены в какой-то другой тип, поэтому вы также можете использовать private или protected.) Далее следует ключевое слово delegate, которое просто сообщает компилятору C#, что мы определяем тип делегата. Оставшаяся часть определения выглядит как объявление метода, и это не случайно. У нас есть возвращаемый тип bool. Вы помещаете имя типа делегата туда, где обычно записано имя метода. Угловые скобки указывают, что это обобщенный тип с единственным аргументом типа T, а ключевое слово in указывает, что T является контравариантным. (Глава 6 описывает контравариантность.) Наконец, сигнатура метода имеет единственный параметр этого типа.
Листинг 9.3. Тип делегата Predicate<T>
public delegate bool Predicate<in T>(T obj);
Листинг 9.2. Метод с делегатом в качестве параметра
public static int FindIndex<T>(
T[] array,
Predicate<T> match)
Листинг 9.1. Поиск в массиве с использованием делегата
public static int GetIndexOfFirstNonEmptyBin(int[] bins) =>
Array.FindIndex(bins, IsGreaterThanZero);
private static bool IsGreaterThanZero(int value) => value > 0;
# предоставляет гораздо более простое решение в виде делегата, который является ссылкой на метод.
В .NET об ошибках, как правило, сообщают с помощью исключений, за вычетом ряда сценариев, где сбои считаются обычным делом, а затраты на исключения оказываются высокими по сравнению с выполняемой задачей. Исключения позволяют коду обработки ошибок располагаться отдельно от рабочего кода. Они также затрудняют игнорирование ошибок — непредвиденные ошибки распространяются вверх по стеку и в конечном итоге приводят к завершению программы и выдаче отчета об ошибках. Блоки catch позволяют нам обрабатывать те исключения, которые мы можем предвидеть. (Вы также можете использовать их для беспорядочного перехвата всех исключений, но это, как правило, плохая идея — если вы не знаете, почему произошло то или иное исключение, вы не можете точно знать, как после него безопасно восстановиться.) Блоки finally дают способ безопасного выполнения очистки независимо от того, выполняется код успешно или с исключениями. Библиотека классов .NET определяет множество полезных типов исключений, но при необходимости мы можем писать и свои собственные.
Когда обработчик получает уведомление, исключение останавливать уже поздно — CLR завершит процесс вскоре после вызова вашего обработчика. Основная причина, по которой существует это событие, заключается в предоставлении места для размещения кода журналирования, чтобы вы в диагностических целях могли записать определенную информацию о сбое. В принципе, вы можете также попытаться сохранить любые несохраненные данные, чтобы облегчить восстановление, если программа перезапустится, но действовать следует осторожно: если вызван ваш обработчик необработанных исключений, то ваша программа по определению находится в подозрительном состоянии, поэтому любые сохраняемые данные могут оказаться некорректными.
Листинг 8.14. Уведомления о необработанных исключениях
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
// Умышленный сбой для демонстрации UnhandledException
throw new InvalidOperationException();
}
private static void OnUnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
Console.WriteLine($"An exception went unhandled: {e.ExceptionObject}");
}
