Существует множество различий между вашей оболочкой и кодом, который вы пытаетесь реплицировать.
Вы избавляетесь от одноразового ресурса, как только возвращается метод async
, а непосле завершения асинхронной операции.Код, который вы пытаетесь реплицировать, не удаляет ресурс до тех пор, пока не завершится асинхронная операция.Если вы когда-либо используете одноразовый ресурс после ожидания чего-либо, он уже будет удален.
Если исключение произойдет до того, как вы вызовете Act
, одноразовый ресурс будет утечкой.В другом коде нет такой возможности утечки одноразового ресурса.
Если вы наберете Act
несколько раз, одноразовый ресурс уже будет утилизирован.
Если вы никогда не наберете Act
на упаковке, то одноразовый ресурс никогда не будет утилизирован.Это в основном # 2, но если автор кода делает это неправильно.
Вы создаете несколько дополнительных объектов, увеличивая нагрузку на память.Это и ваша одноразовая оболочка, и дополнительные async
методы, то есть дополнительные конечные автоматы.
# 1 - это то, что вы можете исправить в своей реализации, не изменяя способ использования вызывающими программами.это, другие присущи тому, как вы это спроектировали, и решение этих проблем потребовало бы довольно значительного изменения способа, которым вызывающие абоненты используют операцию для исправления.
Итак, что касается исправления всех этих вещей,Первым делом было бы просто полностью удалить объект-обертку.Это создает многочисленные возможности для программиста просто сделать это неправильно (№ 3, № 4 и некоторые другие, о которых я не удосужился упомянуть), и является большой частью № 5.Вместо этого просто используйте статический метод, принимающий два аргумента, одноразовый ресурс и действие, которое нужно предпринять.Это дает вам контроль, необходимый для обеспечения того, чтобы всегда (или, по крайней мере, ближе к всегда) выполнялось правильно.
public static TResult UseDisposable<TDisposable, TResult>(TDisposable disposable, Func<TDisposable, TResult> function)
where TDisposable : IDisposable
{
using (disposable)
{
return function(disposable);
}
}
Далее, если вы хотите поддерживать асинхронные методы, вам необходимоспециально для этого нужно перегрузить просто , в котором вы понимаете, что это асинхронный метод и обрабатываете его соответствующим образом.К счастью await
позволяет легко писать.(Обратите внимание, что с технической точки зрения использование async
здесь означает, что мы создаем конечный автомат, которого мы могли бы технически избежать, если бы мы делали это вручную. Если вы хотите избежать этого отличия от оригинала, вам нужно либо выполнить всерукой (что на удивление сложно, если вы хотите убедиться, что все правильные действия по обработке ошибок и отмене выполнены правильно). Если вы можете жить с добавленными выделениями, то код не слишком сложен, чем код, который вы 'мы пытаемся выполнить репликацию.
public static async Task<TResult> UseDisposable<TDisposable, TResult>(TDisposable disposable, Func<TDisposable, Task<TResult>> function)
where TDisposable : IDisposable
{
using (disposable)
{
return await function(disposable);
}
}
Тогда, конечно, вам нужны версии этих двух перегрузок для операций, не возвращающих результат:
public static void UseDisposable<TDisposable>(TDisposable disposable, Action<TDisposable> action)
where TDisposable : IDisposable
{
using (disposable)
{
action(disposable);
}
}
public static async Task UseDisposable<TDisposable, TResult>(TDisposable disposable, Func<TDisposable, Task> action)
where TDisposable : IDisposable
{
using (disposable)
{
await action(disposable);
}
}
Стоит отметить, что вышеупомянутые перегрузкине полностью рассматривайте возможность возникновения исключений до того, как одноразовые ресурсы попадут в using
. Вышеприведенная версия сокращает окно возможностей над вашим кодом и, в частности, устраняет множество возможных его неправильных использований, но этоне устраняет полностью. Если вы обеспокоеныТаким образом, вы можете сделать еще один шаг вперед, и вместо того, чтобы принимать одноразовый ресурс, вы можете принять метод, который генерирует одноразовый ресурс.Это полностью исключает эту возможность.К счастью, все эти перегрузки могут располагаться бок о бок, поэтому вы можете иметь все эти перегрузки и использовать версии одноразового генератора только в том случае, если вы обеспокоены возможными исключениями, происходящими в любом месте между его конструктором и началом использования.
public static TResult UseDisposable<TDisposable, TResult>(Func<TDisposable> disposableGenerator, Func<TDisposable, TResult> function)
where TDisposable : IDisposable
{
using (var disposable = disposableGenerator())
{
return function(disposable);
}
}
public static async Task<TResult> UseDisposable<TDisposable, TResult>(Func<TDisposable> disposableGenerator, Func<TDisposable, Task<TResult>> function)
where TDisposable : IDisposable
{
using (var disposable = disposableGenerator())
{
return await function(disposable);
}
}
public static void UseDisposable<TDisposable>(Func<TDisposable> disposableGenerator, Action<TDisposable> action)
where TDisposable : IDisposable
{
using (var disposable = disposableGenerator())
{
action(disposable);
}
}
public static async Task UseDisposable<TDisposable, TResult>(Func<TDisposable> disposableGenerator, Func<TDisposable, Task> action)
where TDisposable : IDisposable
{
using (var disposable = disposableGenerator())
{
await action(disposable);
}
}
Возможно, вы захотите использовать оригинальные 4 метода расширения перегрузок, если вы идете по пути их использования в течение последних четырех, и вы обнаружите, что используете их достаточно, чтобы это имело смысл.