Сначала конкретный простой пример:
Код: Выделить всё
interface IMyWork
{
Task DoStuff();
}
class MyWork : IMyWork
{
public async Task DoStuff() { await Something(); }
}
// A decorator like any other
class MyDecoratedWork : IMyWork
{
private readonly MyWork _real;
public Task DoStuff()
{
using var scope = Decorate();
await _real.Something();
// Disposal is in the same context as Decorate was - things like AsyncLocal are restored
}
private IDisposable Decorate() { /* return some scoped decoration /* }
}
Код: Выделить всё
class DispatchProxyScopedDecorator : DispatchProxy
{
private TDecorated? _decorated;
private IScopeProvider? _scopeProvider;
private void SetParameters(TDecorated decorated, IScopeProviderscopeProvider)
{
_decorated = decorated;
_scopeProvider = scopeProvider;
}
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
// The important bit
}
public static TDecorated Create(TDecorated decorated, IScopeProvider scopeProvider)
{
object created = Create()!;
((DispatchProxyScopedDecorator)created).SetParameters(decorated, scopeProvider);
return (TDecorated)created;
}
}
Код: Выделить всё
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
using var scope = _scopeProvider.Decorate();
return targetMethod?.Invoke(_decorated, args);
}
Похоже, что единственным требованием является реализация метода GetAwaiter (такие вещи, как AsyncStateMachineAttribute, не обязательно применяются).
Код: Выделить всё
var scope = _scopeProvider.Decorate();
var result = targetMethod.Invoke(_decorated, args);
if (result is null)
{
return null;
}
// This isn't complete - also needs to hunt through assemblies for extension methods!
var awaiterMethod = result.GetType().GetMethod("GetAwaiter");
Код: Выделить всё
async Task InterceptAsync(??? awaitable, MyScope scope)
{
try
{
await awaitable;
}
finally
{
scope.Dispose();
}
}
if (awaiterMethod is not null)
{
return InterceptAsync(result, scope);
}
Как и у любого декоратора, есть очевидные недостатки (производительность, потенциальное изменение типа возвращаемого значения), но осуществимо ли это вообще? Мне не хватает какого-то другого подхода? Я сразу подумал, что если исходный код не возвращает Task, приведенное выше нарушит сигнатуру ожидаемого метода. Но означает ли это, что для каждого отдельного ожидаемого объекта требуется своя собственная реализация?
Мысли оценены по достоинству.
Подробнее здесь: https://stackoverflow.com/questions/791 ... ained-from
Мобильная версия