c# - 修复“IDisposable”的此实现以符合处置模式
问题描述
我在这里有这个工作代码,它是下载一个有进度的文件。当我运行 SonarScanner 时,它抱怨我应该考虑
修复“IDisposable”的这个实现以符合处置模式。
public class HttpClientDownloadWithProgress : IDisposable
{
private readonly string _downloadUrl;
private readonly string _destinationFilePath;
private readonly string _username;
private readonly string _password;
private HttpClient _httpClient;
public delegate Task ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage, string fileName);
public event ProgressChangedHandler ProgressChanged;
public HttpClientDownloadWithProgress(string downloadUrl, string destinationFilePath, string username, string password)
{
_downloadUrl = downloadUrl;
_destinationFilePath = destinationFilePath;
_username = username;
_password = password;
}
public async Task StartDownload()
{
_httpClient = new HttpClient { Timeout = TimeSpan.FromDays(1) };
var byteArray = Encoding.ASCII.GetBytes($"{_username}:{_password}");
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
using (var response = await _httpClient.GetAsync(_downloadUrl, HttpCompletionOption.ResponseHeadersRead)) {
await DownloadFileFromHttpResponseMessage(response);
}
}
private async Task DownloadFileFromHttpResponseMessage(HttpResponseMessage response)
{
response.EnsureSuccessStatusCode();
var totalBytes = response.Content.Headers.ContentLength;
using (var contentStream = await response.Content.ReadAsStreamAsync()) {
await ProcessContentStream(totalBytes, contentStream);
}
}
private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream)
{
var totalBytesRead = 0L;
var readCount = 0L;
var buffer = new byte[8192];
var isMoreToRead = true;
using (var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
{
do
{
var fileName = Path.GetFileName(_destinationFilePath);
var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
isMoreToRead = false;
await TriggerProgressChanged(totalDownloadSize, totalBytesRead, fileName);
continue;
}
await fileStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
readCount += 1;
if (readCount % 100 == 0) {
// change status to "DOWNLOADED"
await TriggerProgressChanged(totalDownloadSize, totalBytesRead, fileName);
}
// change status to "DOWNLOADING"
}
while (isMoreToRead);
}
}
private async Task TriggerProgressChanged(long? totalDownloadSize, long totalBytesRead, string fileName)
{
if (ProgressChanged == null)
return;
double? progressPercentage = null;
if (totalDownloadSize.HasValue)
progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);
await ProgressChanged(totalDownloadSize, totalBytesRead, progressPercentage, fileName);
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
我的Dispose()
方法有什么问题?如何实现处置模式?
解决方案
一些较旧的 linter 喜欢这种模式:
- 实施
IDisposable
并将呼叫转发到Dispose(true)
- 实现一个转发到的终结器
Dispose(false)
- 添加一个
virtual protected void Dispose(bool disposing)
方法,根据调用是来自终结器还是析构器来实际处理资源。
这确保了 GC 终结器线程有机会清除非托管资源(如果有)。
沿着这些思路
protected void Dispose(bool disposing)
{
if(disposing){
_httpClient?.Dispose();
GC.SuppressFinalize(this); // no need to run the finalizer we have dispose everything we need
}
}
public void Dispose()=>Disposing(true);
public ~HttpClientDownloadWithProgress()=>Disposing(false);
}
但是在您的情况下,继承是否会成为前进的方向是值得怀疑的。我宁愿组合 anHttpClient
和 aProgress
并让客户端存活更长时间,以便其他组合可以使用它。
推荐阅读
- php - 如何在调用php时将php变量传递给mysql存储过程
- laravel - Laravel 视图中的多种功能
- javascript - 如何从谷歌地图 API 的 URL 获取地理编码数据,而不是在 IONIC WebAPP 中添加地理编码功能?
- curl - 在 FTPS 服务器中使用 CURL 下载文件但输出大小错误
- ibm-cloud - 如何订购 SoftLayer_Virtual_ReservedCapacityGroup
- android - 如何在谷歌地图等地图路线上显示交通和估计时间?
- django - 从 django 中的模板访问呈现模板的视图
- ios - 如何为特定类型的字典定义扩展名?
- javascript - 如何使用 jQuery 在新窗口上显示模式
- android - Android 房间迁移代码多次执行