任务中 IDisposable T 的 IDisposable 和/或 IAsyncDisposable 模式<T>

问题描述 投票:0回答:1

如果我有一个类,其中字段是

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

编辑

回应评论 - 创建一个易于理解但传达复杂性的简单示例总是很困难。在这种情况下,我设想实例化开始与服务器协商以获取不记名令牌和声明等。一旦此数据可用,我不想在每次调用方法时都经历整个身份验证/授权过程。每个方法本身都是异步的,但如果安全协商正在进行,则在启动一系列不同但相关的方法之前将等待其完成。

编辑2

我对示例代码进行了一些更改,因为注释集中在伪代码的意图上,这并不是真正的问题所在 - 我试图使示例更加具体。目的是创建 3 个类,它们都实现相同的接口,但协商不同的 API 以访问用户的 MS Sharepoint、Google 驱动器或 Dropbox 文件和文件夹。我可以编写代码来执行此操作 - 这个问题不是关于解决特定问题。我真的很想知道在实现 IDisposable 的类中处理由任务包装的

IDisposable
对象的有用模式。这可能是一种过于混乱的方法,会导致反模式,这作为一个答案很好。

c# idisposable
1个回答
0
投票

我的理解是你有一堂这样的课:

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

如果能从您这边看到真实的代码那就太好了,这样我就可以向您展示这如何为您工作。

© www.soinside.com 2019 - 2024. All rights reserved.