'Stream Extensions to convert Stream content into String or Byte array

Using C# 10 I am creating Stream extensions to get content into a String or Byte array.

Something similar to File.ReadAllTextAsync in Microsoft's Net 6.

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;
}

Is there a better way to do this?

I am not sure about the ConfigureAwait(false) that I picked on some code online.



Solution 1:[1]

A better alternative for the ReadAllBytesAsync is

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 mem.ToArray();
    }
}

For the ReadAllLinesAsync, the async stream in C# 8 can make the code cleaner:

public IAsyncEnumerable<string> ReadAllLinesAsync(this Stream stream)
{
    using var reader = new StreamReader(stream)
    while (await reader.ReadLineAsync() is { } line)
    {
        yield return line;
    }
}

notice that the empty brace { } here is actually a property pattern that is only available after C# 8, it checks whether reader.ReadLineAsync() is null, if it's not, assign it to the line variable.

Usage:

var lines = await stream.ReadAllLinesAsync();
await foreach (var line in lines) 
{
    // write your own logic here
}

P.S.:

The ConfigureAwait(false) is kinda useless if your app is single-threaded like console apps, it instructs the awaiter not to capture the SynchronizationContext and let continuation run on the thread that runs the await statement, this method is useful when you're writing a library or SDK, since your user may use your library in a GUI application, and the combination of block waiting such as calling Task.Wait() and the capturing of SynchronizationContext often leads to deadlock, and ConfigureAwait(false) solves this. For detail explanation see ConfigureAwait FAQ

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1