Цель состоит в том, чтобы отсеять неправильное использование ConfigureAwait в очень большой кодовой базе.
blockquote>Некоторые команды выбирают для этого инструмент анализа кода. Есть несколько доступных. Наиболее распространенный подход, который я видел, состоит в том, чтобы потребовать a
ConfigureAwait
для каждогоawait
и явно указать либоtrue
, либоfalse
. Это гарантирует, что каждыйawait
был рассмотрен и поток контекста явно. Другие команды применяют специфичные для проекта правила «всегда использоватьConfigureAwait(false)
» и просто зависят от проверки кода для проектов, которые не могут следовать этому правилу.Проблема с вашим примером кода состоит в том, что невозможно для
DoWhatever
узнать, был ли он вызван косвенно, из-заTask.Run
. Если вы переписываете эти методы, это становится ясным:public static async Task CapturesContext() { var task = Task.Run(() => DoWhatever()); await task; } public static async Task DoesNotCaptureContext() { var task = Task.Run(() => DoWhatever()); var configuredTask = task.ConfigureAwait(false); await configuredTask; }
Первые строки переписанных методов должны прояснить, что
DoWhatever
не имеет представления, захватят лиCapturesContext
илиDoesNotCaptureContext
контекст или не. Обратите внимание на «волю» (будущее время) - вполне возможно, чтоDoWhatever
выполняется и заканчивает выполнение до того, какConfigureAwait(false)
даже будет вызван.Теперь вы можете проверить изнутри задачи, выполняется ли она в контексте прямо сейчас . Но в этом случае для обоих примеров методов
DoWhatever
не увидят контекст из-заTask.Run
. Так что это не поможет вам обнаружить тот факт, чтоCapturesContext
действительно захватывает контекст;DoWhatever
не видит контекст, поэтому не может его обнаружить.Пользовательский
SynchronizationContext
является хорошим решением для модульных тестов, но его было бы неудобно использовать во время выполнения, так как у вас есть некоторые методы, которым нужен контекст. По этой причине большинство команд предпочитают полагаться на проверку кода и / или инструменты анализа кода.
Да - базовому классу нужен виртуальный деструктор, даже если он пуст. Если этого не сделать, то, когда что-то удалит производный объект
через базовый указатель / ссылку, объекты-члены производного объекта не получат шанса уничтожить себя должным образом.
Производные классы не нужны объявлять или определять свой собственный деструктор, если им не нужно что-то кроме поведения деструктора по умолчанию.
Вам не нужно делать деструктор абстрактным, просто дайте ему пустую реализацию:
virtual ~criterion() { }
Таким образом, вы не обязаны реализовывать его в каждом дочернем классе, но все равно в каждом из них. будет иметь (унаследованный) виртуальный деструктор.
Рекомендуется вставить
virtual ~criterion() {}
, чтобы избежать удаления из проблемы с указателем базового класса. В противном случае вы потеряете память, так как не будут вызываться деструкторы производных классов.
criterion *c = new fastFilter();
delete c; // leaks
One small change from what others have already answered:
Instead of
virtual void ~criterion() = 0;
the required version is:
virtual ~criterion() {} //Note: Removed void as destructors not allowed
// a return type
To know more about virtual destructor have a look at this link from FAQ When should my destructor be virtual?