如果我有一个类,其中字段是
Task
的实例,并且该任务返回一个实现 IDisposable
的类,那么在实例上实现 IDisposable
(+/- IAsyncDisposable
) 的模式是什么由 Task
包裹,请记住任务可能已完成,可能仍在进行中并需要取消,或者可能在调用 Dispose 时失败。快速浏览伪代码:
public sealed Class MyClass : IDisposable
{
private readonly CancellationTokenSource _cancelTokenSrc;
private readonly Task<SecurityTokens> _securityTask;
private bool _isDisposed;
public MyClass(Uri endPoint) {
_cancelTokenSrc= new CancellationTokenSource;
_securityTask = GetServerSecurityData(endpoint, _cancelTokenSrc.Token);
}
public Task<IEnumerable<File>> GetContainedFiles(string folderName) {
var data = await GetPrivate(...);
...
}
public Task UploadFile(File file) {
var data = await GetPrivate(...);
...
}
public Task<Stream> DowloadFiles(string fileId) {
var data = await GetPrivate(...);
...
}
private async Task<HttpRepsonse> GetPrivate(uri uri) {
var s = await _securityTask;
// now send get request & return response
// include security data contained in s with every request
...
}
///!!! This is where I would appreciate peoples thoughts & expertise!!!
public void Dispose() {
if (!_isDisposed) {
if (_securityTask.IsCompleted) {
_securityTask.Result.Dispose();
}
else if (!_securityTask.IsFaulted && !_securityTask.IsCanceled)
{
_cancelTokenSrc.Cancel();
}
disposed = true;
GC.SupressFinalize()
}
}
}
总之,您可以帮助我实现一个更强大的 dispose() 实现,以确保任务返回的 Stream 始终被正确处理,以及可能如何实现
IAsyncDisposable
?
回应评论 - 创建一个易于理解但传达复杂性的简单示例总是很困难。在这种情况下,我设想实例化开始与服务器协商以获取不记名令牌和声明等。一旦此数据可用,我不想在每次调用方法时都经历整个身份验证/授权过程。每个方法本身都是异步的,但如果安全协商正在进行,则在启动一系列不同但相关的方法之前将等待其完成。
我对示例代码进行了一些更改,因为注释集中在伪代码的意图上,这并不是真正的问题所在 - 我试图使示例更加具体。目的是创建 3 个类,它们都实现相同的接口,但协商不同的 API 以访问用户的 MS Sharepoint、Google 驱动器或 Dropbox 文件和文件夹。我可以编写代码来执行此操作 - 这个问题不是关于解决特定问题。我真的很想知道在实现 IDisposable 的类中处理由任务包装的
IDisposable
对象的有用模式。这可能是一种过于混乱的方法,会导致反模式,这作为一个答案很好。
我的理解是你有一堂这样的课:
public class MyClass
{
public Task<MyDisposable> MyDisposable;
}
并且您希望正确管理
IDisposable
的 MyDisposable
生命周期。
我的标准方法是编写方法,让我注入要在一次性设备上执行的代码,然后在方法结束之前调用
.Dispose()
。
以下是您可能需要的三个:
public class MyClass
{
private Task<MyDisposable> CreateMyDisposableAsync() => Task.Run(() => new MyDisposable());
public async Task<R> UsingMyDisposableAsync<R>(Func<MyDisposable, Task<R>> consumeAsync)
{
using (var myDisposable = await this.CreateMyDisposableAsync())
{
return await consumeAsync(myDisposable);
}
}
public async IAsyncEnumerable<R> UsingMyDisposableAsync<T, R>(IEnumerable<T> source, Func<MyDisposable, T, Task<R>> consumeAsync)
{
using (var myDisposable = await this.CreateMyDisposableAsync())
{
foreach (var r in source)
{
yield return await consumeAsync(myDisposable, r);
}
}
}
public async Task UsingMyDisposableAsync(Func<MyDisposable, Task> actionAsync)
{
using (var myDisposable = await this.CreateMyDisposableAsync())
{
await actionAsync(myDisposable);
}
}
}
我已经实现了这个类来向您展示它们是如何工作的:
public class MyDisposable : IDisposable
{
public Task<int> GetValueAsync(int t) => Task.Run(() => t * 2);
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
Console.WriteLine("MyDisposable Disposed!");
// TODO: dispose managed state (managed objects)
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
disposedValue = true;
}
}
bool disposedValue;
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
// ~MyResource()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
请注意,
GetValueAsync
只是将输入数字加倍。
现在我可以写这个:
var myClass = new MyClass();
var v = await myClass.UsingMyDisposableAsync<int>(mr => mr.GetValueAsync(42));
Console.WriteLine(v);
await foreach (var x in myClass.UsingMyDisposableAsync<int, int>(
new[] { 1, 2 },
(mr, t) => mr.GetValueAsync(t)))
{
Console.WriteLine(x);
}
int j = 2;
int k = 3;
await myClass.UsingMyDisposableAsync(async mr =>
{
j = await mr.GetValueAsync(j);
k = await mr.GetValueAsync(j + k);
});
Console.WriteLine(j);
Console.WriteLine(k);
输出:
MyDisposable Disposed!
84
2
4
MyDisposable Disposed!
MyDisposable Disposed!
4
14
如果能从您这边看到真实的代码那就太好了,这样我就可以向您展示这如何为您工作。