我有一个像这样的方法:
public void Encrypt(IFile file)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
string tempFilename = GetFilename(file);
using (var stream = new FileStream(tempFilename, FileMode.OpenOrCreate))
{
this.EncryptToStream(file, stream);
file.Write(stream);
}
File.Delete(tempFilename);
}
但是,我想要编写另一个方法,非常相似,但它调用WriteAsync,例如:
public async Task EncryptAsync(IFile file)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
string tempFilename = GetFilename(file);
using (var stream = new FileStream(tempFilename, FileMode.OpenOrCreate))
{
this.EncryptToStream(file, stream);
await file.WriteAsync(stream);
}
File.Delete(tempFilename);
}
但是,我不喜欢有两种方法,实际上重复代码。我怎么能避免这个?正确的方法感觉我应该使用Action / Delegate,但签名是不同的....
思考?
但是,我不喜欢有两种方法,实际上重复代码。我怎么能避免这个?
有一些方法,如我所述 关于棕色字段异步开发的MSDN文章。
1)使异步API成为自然异步API。
这是最激烈的解决方案(在向后兼容性方面),但从技术角度来看,你可以认为它是最正确的。有了这种方法,你会的 更换 Encrypt
同 EncryptAsync
。
虽然这是IMO的最佳方法,但您的最终用户可能不同意。 :)
2)申请 劈 阻止异步版本。
public void Encrypt(IFile file)
{
EncryptAsync(file).GetAwaiter().GetResult();
}
请注意(正如我在我的博客中描述的那样), 为了避免你需要使用的死锁 ConfigureAwait(false)
异步版本中的任何地方。
这个黑客的缺点:
- 你真的必须使用
ConfigureAwait(false)
为每一个人 await
在你的 async
版本和它调用的每个方法。忘掉一个,你就有了死锁的可能性。请注意,某些库不使用 ConfigureAwait(false)
适用于所有平台的所有地方(特别是 HttpClient
在移动平台上)。
3)申请 劈 在线程池线程上运行异步版本并阻塞 那。
public void Encrypt(IFile file)
{
Task.Run(() => EncryptAsync(file)).GetAwaiter().GetResult();
}
这种方法完全避免了死锁情况,但也有其自身的缺点:
- 异步代码必须能够在线程池线程上运行。
- 可以运行异步代码 多 线程池线程贯穿整个存在。如果异步代码隐式地依赖于单线程上下文的同步,或者它是否使用线程本地状态,那么如果没有一些重写,这种方法将无法工作。
4)传递旗帜参数。
如果你不愿采取方法(1),这是我最喜欢的方法。
private async Task EncryptAsync(IFile file, bool sync)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
string tempFilename = GetFilename(file);
using (var stream = new FileStream(tempFilename, FileMode.OpenOrCreate))
{
this.EncryptToStream(file, stream);
if (sync)
file.Write(stream);
else
await file.WriteAsync(stream);
}
File.Delete(tempFilename);
}
public void Encrypt(IFile file)
{
EncryptAsync(file, sync: true).GetAwaiter().GetResult();
}
public Task EncryptAsync(IFile file)
{
return EncryptAsync(file, sync: false);
}
布尔标志肯定是丑陋的 - 并且是正确的OOP设计的红色标志 - 但它隐藏在一个 private
方法,并没有其他黑客那么危险。
我的文章中介绍了其他几个hacks(单线程线程池上下文和嵌套消息循环),但我通常不推荐它们。
另外,如果您的代码确实在使用 FileStream
,您需要显式打开它以进行异步访问以获得真正的异步操作。也就是说,你必须调用构造函数传递 true
为了 isAsync
参数,或设置 FileOptions.Asynchronous
标志的值为 options
参数。
像这样的东西:
public async Task EncryptAsync(IFile file, bool AAsync)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
string tempFilename = GetFilename(file);
using (var stream = new FileStream(tempFilename, FileMode.OpenOrCreate))
{
this.EncryptToStream(file, stream);
if (AAsync)
await file.WriteAsync(stream);
else
file.Write(stream);
}
File.Delete(tempFilename);
}