Here are the helper methods I've created for a NetStandart 2.0 class library
, that was used both in NetCore 3.1
and NetFramework 4.7.2
projects.
These implementations have matched exactly the names and signatures of the net core 3.1 / net standard 2.1 File
class methods, so you only need to put them in any public class. (FileHelper for example...):
Also, this should be most efficient and similar to the source code of .net implementation.
private const int DefaultBufferSize = 4096;
// File accessed asynchronous reading and sequentially from beginning to end.
private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
public static async Task WriteAllTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using FileStream sourceStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None,
DefaultBufferSize, true);
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
public static async Task<IEnumerable<string>> ReadAllLinesAsync(string filePath)
{
var lines = new List<string>();
using var sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read,
DefaultBufferSize, DefaultOptions);
using var reader = new StreamReader(sourceStream, Encoding.Unicode);
string line;
while ((line = await reader.ReadLineAsync()) != null) lines.Add(line);
return lines;
}
public static async Task<string> ReadAllTextAsync(string filePath)
{
using var sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read,
DefaultBufferSize, DefaultOptions);
using var reader = new StreamReader(sourceStream, Encoding.Unicode);
return await reader.ReadToEndAsync();
}
Edit
Apparently that the StreamReader "async" methods block the current thread for a considerable amount of time before returning an incomplete Task.
(Even the netcore 3.1 File.ReadAllLinesAsyn,File.ReadAllTextAsync
currently aren't seems to be fully async. as you can check in source code, they are based on the StreamReader "async" methods).
So, I'm sharing an implementation that seems like the most efficient way currently. \
It's better than options like to run the sync methods in Task.Run(()=>File.ReadAllLines(...))
, since its a very bad practice to wrap your sync code with Task.Run
and expect this to be full async flow. \ Actually, it breaks the internal queues mechanism of the real asynchronous dotnet structure.
public static async Task<string> ReadAllTextAsync(string filePath)
{
using (var sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read,
DefaultBufferSize, DefaultOptions))
{
var sb = new StringBuilder();
var buffer = new byte[0x1000];
var numRead = 0;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
sb.Append(Encoding.Unicode.GetString(buffer, 0, numRead));
return sb.ToString();
}
}
Testing Time
Here is my test and its output that displays clearly that the actual run is async:
var stopwatch = Stopwatch.StartNew();
var fileTask = FileHelper.ReadAllTextAsync("48MB_file.txt");
var duration1 = stopwatch.ElapsedMilliseconds;
var isCompleted = fileTask.IsCompleted;
stopwatch.Restart();
await fileTask;
var duration2 = stopwatch.ElapsedMilliseconds;
Console.WriteLine($"Creation took: {duration1:#,0} ms, Task.IsCompleted: {isCompleted}");
Console.WriteLine($"Calling await took: {duration2:#,0} ms, Task.IsCompleted: {fileTask.IsCompleted}");
Creation took: 43 ms, Task.IsCompleted: False
Calling await took: 508 ms, Task.IsCompleted: True
You can find more in the comments, and in this question: File.ReadAllLinesAsync() blocks the UI thread