流扩展将流内容转换为字符串或字节数组

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

使用 C# 10,我正在创建

Stream
扩展以将内容放入字符串或字节数组中。

类似于 Microsoft Net 6 中的

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)

c# .net-core .net-6.0 c#-10.0
1个回答
0
投票

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

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