トレーニング
モジュール
.NET アプリでファイルとディレクトリを操作する - Training
.NET、C#、System.IO を使用して、ディレクトリ、パス、ファイル、ファイル システムを操作する方法について説明します。
このブラウザーはサポートされなくなりました。
Microsoft Edge にアップグレードすると、最新の機能、セキュリティ更新プログラム、およびテクニカル サポートを利用できます。
ファイルにアクセスする際に非同期機能を使用できます。 非同期機能を使用すると、コールバックの使用や複数のメソッドまたはラムダ式へのコードの分割を行わずに、非同期メソッドを呼び出すことができます。 同期コードを非同期コードにするには、同期メソッドの代わりに非同期メソッドを呼び出して、コードにいくつかのキーワードを追加するだけで済みます。
ファイル アクセスの呼び出しに非同期性を適用する利点には、次のようなものがあります。
このトピックの簡単な例では、File.WriteAllTextAsync と File.ReadAllTextAsync について説明します。 ファイル I/O 操作を微調整するには、FileStream クラスを使用します。これには、オペレーティング システムのレベルで非同期 I/O を発生させるオプションがあります。 このオプションを使用することにより、多くの場合、スレッド プール スレッドがブロックされるのを回避できます。 このオプションを有効にするには、コンストラクター呼び出しで useAsync=true
または options=FileOptions.Asynchronous
引数を指定します。
ファイル パスを指定して StreamReader と StreamWriter を直接開いた場合、それらでこのオプションを使用することはできません。 一方、FileStream クラスによって開かれた Stream を使用する場合は、このオプションを使用できます。 UI アプリでは、スレッド プール スレッドがブロックされても、非同期呼び出しは高速になります。これは、UI スレッドは待機中にブロックされないためです。
次の例では、ファイルにテキストを書き込みます。 各 await ステートメントに達すると、メソッドは直ちに終了します。 ファイル I/O が完了すると、メソッドは await ステートメントの後のステートメントから再開します。 async 修飾子は、await ステートメントを使用するメソッドの定義に含まれます。
public async Task SimpleWriteAsync()
{
string filePath = "simple.txt";
string text = $"Hello World";
await File.WriteAllTextAsync(filePath, text);
}
public async Task ProcessWriteAsync()
{
string filePath = "temp.txt";
string text = $"Hello World{Environment.NewLine}";
await WriteTextAsync(filePath, text);
}
async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
元の例には await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
ステートメントがあります。これは、次の 2 つのステートメントの省略形です。
Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;
最初のステートメントはタスクを返し、ファイル処理を開始します。 await が含まれた 2 番目のステートメントによって、メソッドが直ちに終了し、別のタスクを返します。 ファイル処理が完了すると、await の後のステートメントに実行が戻ります。
次の例では、ファイルからテキストを読み取ります。
public async Task SimpleReadAsync()
{
string filePath = "simple.txt";
string text = await File.ReadAllTextAsync(filePath);
Console.WriteLine(text);
}
テキストはバッファーに格納されます。この例では StringBuilder に配置されます。 前の例と異なり、await の評価で値が生成されます。 ReadAsync メソッドによって Task<Int32> が返されます。処理の完了後、await の評価によって Int32
値 numRead
が生成されます。 詳しくは、「非同期の戻り値の型 (C#)」をご覧ください。
public async Task ProcessReadAsync()
{
try
{
string filePath = "temp.txt";
if (File.Exists(filePath) != false)
{
string text = await ReadTextAsync(filePath);
Console.WriteLine(text);
}
else
{
Console.WriteLine($"file not found: {filePath}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
async Task<string> ReadTextAsync(string filePath)
{
using var sourceStream =
new FileStream(
filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);
var sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
次の例では、10 個のテキスト ファイルを記述する並列処理を示します。
public async Task SimpleParallelWriteAsync()
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 11; index <= 20; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
writeTaskList.Add(File.WriteAllTextAsync(filePath, text));
}
await Task.WhenAll(writeTaskList);
}
WriteAsync メソッドは、ファイルごとにタスクを返します。タスクはタスクの一覧に追加されます。 await Task.WhenAll(tasks);
ステートメントはメソッドを終了し、すべてのタスクのファイル処理が完了すると、メソッド内で再開します。
この例では、タスクの完了後、finally
ブロックのすべての FileStream インスタンスを閉じます。 using
ステートメントで FileStream
が作成された場合は、タスクが完了する前に FileStream
が破棄されることがあります。
パフォーマンスの向上の多くは、非同期処理ではなく並列処理によって実現されます。 非同期性の利点は、複数のスレッドやユーザー インターフェイス スレッドが拘束されない点にあります。
public async Task ProcessMultipleWritesAsync()
{
IList<FileStream> sourceStreams = new List<FileStream>();
try
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 1; index <= 10; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
byte[] encodedText = Encoding.Unicode.GetBytes(text);
var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
Task writeTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
sourceStreams.Add(sourceStream);
writeTaskList.Add(writeTask);
}
await Task.WhenAll(writeTaskList);
}
finally
{
foreach (FileStream sourceStream in sourceStreams)
{
sourceStream.Close();
}
}
}
WriteAsync メソッドと ReadAsync メソッドを使用すると、CancellationToken を指定して、途中で処理をキャンセルすることができます。 詳細については、「マネージド スレッドのキャンセル」を参照してください。
.NET に関するフィードバック
.NET はオープンソース プロジェクトです。 フィードバックを提供するにはリンクを選択します。
トレーニング
モジュール
.NET アプリでファイルとディレクトリを操作する - Training
.NET、C#、System.IO を使用して、ディレクトリ、パス、ファイル、ファイル システムを操作する方法について説明します。
ドキュメント
この上級チュートリアルでは、非同期ストリームを生成し、使用する方法について説明します。 非同期ストリームでは、非同期で生成される可能性があるデータのシーケンスを自然な方法で処理できます。
async と await を使ったタスク非同期プログラミング (TAP) モデル - C#
タスク ベースの非同期プログラミングを使用するタイミングと方法を学習します。これは、C# で非同期プログラミングを実行する簡単な方法です。
C# で Task.WhenAny を使用して複数のタスクを開始し、その結果を開始の順番で処理するのではなく、完了時に処理する方法を説明します。