我有一个像这样的方法:
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,但签名是不同的......
思想?
答案 0 :(得分:18)
但是,我不喜欢有两种方法,几乎是重复的代码。我怎么能避免这个?
我的MSDN article on brownfield async development中列出了一些方法。
1)使自然异步API仅异步。
这是最激烈的解决方案(在向后兼容性方面),但从技术角度来看,你可以说它是最正确的。使用这种方法,您可以用Encrypt
替换 EncryptAsync
。
虽然这是IMO的最佳方法,但您的最终用户可能不同意。 :)
2)在异步版本上应用 hack 阻止。
public void Encrypt(IFile file)
{
EncryptAsync(file).GetAwaiter().GetResult();
}
请注意(正如我在博客中所述),to avoid deadlocks you'll need to use ConfigureAwait(false)
everywhere in your asynchronous version。
这个黑客的缺点:
ConfigureAwait(false)
版本中的每个await
及其调用的每个方法都必须使用async
。忘掉一个,你就有了死锁的可能性。请注意,某些库不会在所有平台上使用ConfigureAwait(false)
(特别是移动平台上的HttpClient
)。3)在线程池线程上应用运行异步版本的 hack ,并在上阻止。
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
方法中,而不像其他黑客那样危险。
我的文章中介绍了其他一些黑客攻击(单线程线程池上下文和嵌套的消息循环),但我通常不推荐它们。
另外,如果您的代码确实使用FileStream
,则需要显式打开它以进行异步访问以获得真正的异步操作。也就是说,您必须调用构造函数为true
参数传递isAsync
,或在FileOptions.Asynchronous
参数的值中设置options
标记。
答案 1 :(得分:-1)
这样的事情:
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);
}