使用 C# 10,我正在创建
Stream
扩展以将内容放入字符串或字节数组中。
File.ReadAllTextAsync
。
public static async Task<string> ReadAllTextAsync(this Stream stream). {
string result;
using (var reader = new StreamReader(stream)) {
result = await reader.ReadToEndAsync().ConfigureAwait(false);
}
return result;
}
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream) {
using (var content = new MemoryStream()) {
var buffer = new byte[4096];
int read = await stream.ReadAsync(buffer, 0, 4096).ConfigureAwait(false);
while (read > 0) {
content.Write(buffer, 0, read);
read = await stream.ReadAsync(buffer, 0, 4096).ConfigureAwait(false);
}
return content.ToArray();
}
}
public static async Task<List<string>> ReadAllLinesAsync(this Stream stream) {
var lines = new List<string>();
using (var reader = new StreamReader(stream)) {
string line;
while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) {
lines.Add(line);
}
}
return lines;
}
有更好的方法吗?
我不确定我在网上挑选的一些代码的
ConfigureAwait(false)
。
ReadAllBytesAsync
的更好替代方案是
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream)
{
switch (stream)
{
case MemoryStream mem:
return mem.ToArray();
default:
using (var m = new MemoryStream())
{
await stream.CopyToAsync(m);
return m.ToArray();
}
}
}
对于
ReadAllLinesAsync
,C# 8 中的异步流可以使代码更简洁:
public IAsyncEnumerable<string> ReadAllLinesAsync(this Stream stream)
{
using var reader = new StreamReader(stream)
while (await reader.ReadLineAsync() is { } line)
{
yield return line;
}
}
注意,这里的空大括号
{ }
实际上是一个属性模式,只有在C# 8之后才可用,它检查reader.ReadLineAsync()
是否是null
,如果不是,则将其分配给line
变量。
用途:
var lines = await stream.ReadAllLinesAsync();
await foreach (var line in lines)
{
// write your own logic here
}
附注:
如果您的应用程序像控制台应用程序一样是单线程的,则
ConfigureAwait(false)
有点无用,它指示awaiter
不要捕获SynchronizationContext
并让继续在运行await
语句的线程上运行,此方法是当您编写库或 SDK 时很有用,因为您的用户可能在 GUI 应用程序中使用您的库,并且块等待(例如调用 Task.Wait()
和捕获 SynchronizationContext
)的组合通常会导致死锁,并且 ConfigureAwait(false)
解决了这个问题。详细说明请参阅ConfigureAwait FAQ