IAsyncDisposable参考实现出现错误?

3

微软为实现同时需要实现IDisposable和IAsyncDisposable接口的类提供了一个参考实现,例如因为它们有这两个接口的成员。

https://learn.microsoft.com/zh-cn/dotnet/standard/garbage-collection/implementing-disposeasync

下面是完整的实现代码。我不理解Dispose(disposing)中的这些行是什么意思。

(_asyncDisposableResource as IDisposable)?.Dispose();
...
_asyncDisposableResource = null;

似乎如果我有一个CustomDisposable实例,并且在DisposeAsync()之前调用了Dispose(),那么_asyncDispoableResource字段将调用Dispose()而不是DisposeAsync(如果有),然后无条件地设置为null。在这种情况下,似乎_asyncDispoableResource永远不会被正确处理,即使稍后在CustomDisposable实例上调用DisposeAsync()也是如此。
完整的参考代码:
using System;
using System.IO;
using System.Threading.Tasks;

namespace Samples
{
    public class CustomDisposable : IDisposable, IAsyncDisposable
    {
        IDisposable _disposableResource = new MemoryStream();
        IAsyncDisposable _asyncDisposableResource = new MemoryStream();

        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        public async ValueTask DisposeAsync()
        {
            await DisposeAsyncCore();

            Dispose(disposing: false);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _disposableResource?.Dispose();
                (_asyncDisposableResource as IDisposable)?.Dispose();
            }

            _disposableResource = null;
            _asyncDisposableResource = null;
        }

        protected virtual async ValueTask DisposeAsyncCore()
        {
            if (_asyncDisposableResource is not null)
            {
                await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false);
            }

            if (_disposableResource is IAsyncDisposable disposable)
            {
                await disposable.DisposeAsync().ConfigureAwait(false);
            }
            else
            {
                _disposableResource.Dispose();
            }

            _asyncDisposableResource = null;
            _disposableResource = null;
        }
    }
}

抑制 SuppressFinalize 调用的目的是什么? - MikeJ
“Dispose模式”表明还应该有一个终结器来调用this.Dispose(true)。在本例中,它被省略了。因此,您可以抑制终结器以确保不尝试处理已处置的资源。 - Emanuel Vintilă
1个回答

2
引用您的参考:
实现IAsyncDisposable接口时,通常类也会实现IDisposable接口。 IAsyncDisposable接口的良好实现模式是准备同步或异步处理Dispose。
这意味着通过“经典”Dispose释放对象应该足够了,不需要调用DisposeAsync。这就是为什么资源被设置为null的原因,这样就清楚地表明它已经被异步处理释放了。可回收引用的实际实现必须为此做好准备。

1
但是 IAsyncDisposable 没有实现 IDisposable,所以这个转换可能会失败 (_asyncDisposableResource as IDisposable)?.Dispose();,在这种情况下,异步资源将永远不会被处理。 - Emanuel Vintilă
1
建议任何实现都应该实现IDisposable,这样它就可以工作。正如我所写的,是否考虑此建议取决于实现。如果实现没有考虑到它,你是正确的,它可能会失败。 - Alex AIT
Alex:这里的想法是调用者会选择同步或异步处理,但不会同时处理?无论他们选择哪种方式,都会清理一切吗? - Adam V. Steele
1
在非异步Dispose()调用期间不应该处理异步可处置对象是有道理的。我不明白为什么在AsyncDispose调用期间不处理非异步可处置对象。但我心中更大的问题是关于终结器的。似乎使用带有终结器的AsyncDisposable没有任何意义。希望能得到相关指导。 - MikeJ
1
在我的理解中,调用者可以选择他们想要的处理方式。无论是同步、异步还是两者都有,都不应该有影响。这就是为什么参考实现即使对于同步处理,也会进行“null”检查,以防止它被调用时与异步处理同时存在。 - Alex AIT
是的,我认为你已经回答了这个问题,因为你理解了微软的意图。看起来他们可以澄清和完善这个参考设计,因为它对人们来说相当令人困惑。 - Adam V. Steele

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接