C# using varとは?using文との違い・スコープ・使い方をわかりやすく解説
はじめに
C#でファイル、ストリーム、データベース接続などを扱うときによく登場するのがusingです。特にC# 8.0以降では、従来のusing文に加えて、using varという書き方が使えるようになりました。
using varは、リソースの解放処理であるDisposeを自動で呼び出すための便利な構文です。
たとえば、次のように書けます。
C#using var stream = File.OpenRead("sample.txt");
一見するとただのvar宣言にusingが付いただけに見えますが、実際には「この変数はスコープの終わりで自動的にDisposeする」という意味を持ちます。
この記事では、C#のusing varとは何か、従来のusing文との違い、スコープ、Disposeされるタイミング、実践的な使い分けまでわかりやすく解説します。
1. C#のusing varとは?
1-1. using varは「using宣言」と「var」を組み合わせた書き方
using varとは、C# 8.0で導入されたusing宣言の代表的な書き方です。
C#using var resource = new SomeDisposable();
これは、次のような意味になります。
C#var resource = new SomeDisposable();
// スコープの終わりで自動的にresource.Dispose()が呼ばれる
通常のvarは、単に型推論を使って変数を宣言するだけです。
C#var name = "Taro";
一方、using varは、変数の宣言に加えて、スコープ終了時にDisposeを呼び出すというリソース管理の意味を持ちます。
つまり、using varは単なる省略記法ではなく、IDisposableを実装したオブジェクトを安全に扱うための構文です。
1-2. using varでできること:Disposeを自動実行する
using varの目的は、Disposeの呼び忘れを防ぐことです。
C#では、ファイル、ネットワーク接続、データベース接続、ストリームなど、使い終わったあとに明示的な後片付けが必要なオブジェクトがあります。これらの多くはIDisposableインターフェースを実装しています。
たとえば、ファイルを開くStreamReaderはIDisposableを実装しているため、使い終わったらDisposeする必要があります。
C#using var reader = new StreamReader("sample.txt");
var text = reader.ReadToEnd();
Console.WriteLine(text);
このコードでは、reader.Dispose()を自分で書いていません。しかし、using varを使っているため、スコープの終わりで自動的にDisposeが呼び出されます。
1-3. using varが使えるC#のバージョン
using varは、C# 8.0から使える構文です。
そのため、C# 7.3以前のプロジェクトでは使用できません。古い環境で次のように書くと、コンパイルエラーになることがあります。
C#using var stream = File.OpenRead("sample.txt");
C# 8.0以降を使っているかどうかは、プロジェクトファイルのLangVersionで確認できます。
XML<PropertyGroup>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
または、最新のC#バージョンを使う場合は次のように設定できます。
XML<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
ただし、実務ではプロジェクトやチームの方針に合わせて言語バージョンを決めるのが一般的です。
1-4. まずは基本構文を確認
using varの基本構文は次のとおりです。
C#using var 変数名 = IDisposableを実装したオブジェクト;
具体例は次のようになります。
C#using var stream = new FileStream("sample.txt", FileMode.Open);
varによって型は自動推論されます。この例では、streamの型はFileStreamになります。
型を明示して書くこともできます。
C#using FileStream stream = new FileStream("sample.txt", FileMode.Open);
つまり、using varのvarは必須ではありません。using宣言では、varを使っても、型名を明示しても構いません。
2. using varの基本的な使い方
2-1. using varの書き方
using varは、通常のローカル変数宣言にusingを付けるような感覚で書けます。
C#using var resource = new MyResource();
このとき、resourceは宣言されたスコープの終わりで自動的にDisposeされます。
たとえば、次のメソッドを見てください。
C#void Execute()
{
using var resource = new MyResource();
resource.DoSomething();
} // ここでresource.Dispose()が呼ばれる
using varは、見た目がシンプルでありながら、内部的にはtry-finallyに近い形でDisposeを保証してくれます。
2-2. ファイル読み込みで使う例
using varがよく使われる例のひとつが、ファイル読み込みです。
C#using var reader = new StreamReader("sample.txt");
string content = reader.ReadToEnd();
Console.WriteLine(content);
StreamReaderはファイルリソースを扱うため、使い終わったら解放する必要があります。using varを使うことで、reader.Dispose()を明示的に書かなくても安全にリソースを解放できます。
メソッド全体で見ると、次のようになります。
C#void ReadFile()
{
using var reader = new StreamReader("sample.txt");
string content = reader.ReadToEnd();
Console.WriteLine(content);
} // reader.Dispose()が呼ばれる
ファイルを開いたままにすると、他の処理からそのファイルを操作できなくなることがあります。using varを使えば、処理の終了時に確実にファイルを閉じることができます。
2-3. DB接続で使う例
データベース接続でもusing varはよく使われます。
C#using var connection = new SqlConnection(connectionString);
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Users";
var count = command.ExecuteScalar();
Console.WriteLine(count);
この例では、connectionとcommandの両方がusing varで宣言されています。
スコープの終わりに、commandとconnectionがDisposeされます。複数のusing varがある場合は、基本的に宣言と逆順にDisposeされます。
つまり、上の例では次のような順番です。
C#command.Dispose();
connection.Dispose();
データベース関連のオブジェクトは、接続やコマンドなど複数のリソースを扱うことが多いため、using varを使うとコードをすっきり書けます。
2-4. 複数のusing varを使う例
using varは、同じスコープ内で複数使うことができます。
C#using var stream = File.OpenRead("sample.txt");
using var reader = new StreamReader(stream);
string text = reader.ReadToEnd();
Console.WriteLine(text);
この場合、Disposeされる順番は宣言と逆順です。
C#reader.Dispose();
stream.Dispose();
これは重要です。readerは内部でstreamを利用しているため、先にreaderをDisposeし、そのあとにstreamをDisposeするのが自然です。
従来のusing文で書くと、次のようになります。
C#using (var stream = File.OpenRead("sample.txt"))
using (var reader = new StreamReader(stream))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
using varを使うと、ネストが減り、上から下へ読みやすいコードになります。
2-5. varを使わず型を明示する書き方
using varではなく、型を明示して書くこともできます。
C#using FileStream stream = File.OpenRead("sample.txt");
using StreamReader reader = new StreamReader(stream);
この書き方もusing宣言です。
varを使う場合は型推論によって短く書けます。
C#using var stream = File.OpenRead("sample.txt");
using var reader = new StreamReader(stream);
一方、型を明示すると、変数の型がコード上ですぐにわかります。
C#using StreamReader reader = new StreamReader("sample.txt");
型が明らかな場合はusing varで十分ですが、型がわかりにくい場合や、戻り値の型を明示したい場合はusing 型名の書き方も検討するとよいでしょう。
3. using varとusing文の違い
3-1. 従来のusing文とは
従来のusing文は、C#で長く使われてきたリソース管理の構文です。
C#using (var reader = new StreamReader("sample.txt"))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
この書き方では、usingのブロックを抜けたタイミングでreader.Dispose()が自動的に呼び出されます。
つまり、従来のusing文では、Disposeのタイミングがusingブロックの終わりになります。
C#using (var resource = new MyResource())
{
resource.DoSomething();
} // ここでDisposeされる
3-2. using varとの構文の違い
using文とusing varの大きな違いは、ブロックを書くかどうかです。
従来のusing文は次のように書きます。
C#using (var resource = new MyResource())
{
resource.DoSomething();
}
using varは次のように書きます。
C#using var resource = new MyResource();
resource.DoSomething();
using varでは波括弧{ }を使いません。変数は、宣言されたスコープの終わりまで有効です。
そのため、次のような違いがあります。
C#void UseUsingStatement()
{
using (var resource = new MyResource())
{
resource.DoSomething();
} // ここでDispose
}
void UseUsingVar()
{
using var resource = new MyResource();
resource.DoSomething();
} // ここでDispose
見た目は似ていますが、Disposeされるタイミングが変わる可能性がある点に注意が必要です。
3-3. インデントが浅くなるメリット
using varの大きなメリットは、インデントが浅くなることです。
従来のusing文で複数のリソースを扱うと、ネストが深くなりがちです。
C#using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM Users";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine(reader["Name"]);
}
}
}
}
using varを使うと、次のように書けます。
C#using var connection = new SqlConnection(connectionString);
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM Users";
using var reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader["Name"]);
}
ネストが減ることで、処理の流れが上から下へ自然に読めるようになります。
特に、複数のIDisposableオブジェクトを順番に作成する処理では、using varの効果が大きくなります。
3-4. Disposeされるタイミングの違い
using文とusing varで最も注意したい違いは、Disposeされるタイミングです。
従来のusing文では、usingブロックの終わりでDisposeされます。
C#using (var resource = new MyResource())
{
resource.DoSomething();
} // ここでDispose
一方、using varでは、宣言されたスコープの終わりでDisposeされます。
C#void Execute()
{
using var resource = new MyResource();
resource.DoSomething();
Console.WriteLine("まだresourceはDisposeされていない");
} // ここでDispose
この違いは、コードの長さが短い場合はあまり問題になりません。しかし、メソッドが長い場合や、途中でリソースを解放したい場合には重要になります。
次の例では、using varで宣言したreaderは、メソッドの最後までDisposeされません。
C#void Execute()
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Console.WriteLine(text);
DoOtherWork();
} // reader.Dispose()はここで呼ばれる
ファイル読み込みが終わった直後にDisposeしたい場合は、従来のusing文や明示的なブロックを使ったほうがよいことがあります。
3-5. 使い分けの判断基準
using varと従来のusing文は、どちらが常に正しいというものではありません。
基本的には、次のように使い分けるとわかりやすいです。
リソースをメソッドやブロックの終わりまで使う場合は、using varが向いています。
C#using var connection = new SqlConnection(connectionString);
connection.Open();
// このスコープ内でconnectionを使う
一方、特定の短い範囲だけでリソースを使い、すぐにDisposeしたい場合は、従来のusing文が向いています。
C#using (var reader = new StreamReader("sample.txt"))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
// ここではreaderはすでにDispose済み
DoOtherWork();
コードを短くしたいからといって、何でもusing varに置き換えるのではなく、Disposeのタイミングが自然かどうかを考えることが大切です。
4. using varのスコープとDisposeのタイミング
4-1. using varのスコープは宣言されたブロックの終わりまで
using varで宣言した変数は、通常のローカル変数と同じようにスコープを持ちます。
C#void Execute()
{
using var resource = new MyResource();
resource.DoSomething();
} // このブロックの終わりでDispose
この場合、resourceのスコープはExecuteメソッドのブロック内です。そのため、メソッドの終わりでDisposeされます。
より正確には、using varで宣言された変数は、宣言されたスコープを抜けるときにDisposeされます。
C#{
using var resource = new MyResource();
resource.DoSomething();
} // ここでDispose
明示的にブロックを作れば、そのブロックの終わりでDisposeできます。
4-2. メソッド内で使った場合のDisposeタイミング
メソッド直下でusing varを使うと、Disposeされるのはメソッドの終わりです。
C#void Process()
{
using var stream = File.OpenRead("sample.txt");
// streamを使う処理
DoSomething(stream);
// ここでもまだstreamはDisposeされていない
DoOtherWork();
} // ここでstream.Dispose()
この性質は便利な一方で、リソースを長く保持しすぎる原因になることもあります。
たとえば、ファイルを読み終わったあとに長い処理を行う場合、ファイルハンドルを必要以上に保持することになります。
C#void Process()
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
// この処理が長い場合でも、readerはまだDisposeされていない
HeavyProcess(text);
} // ここでreader.Dispose()
読み込み直後にファイルを閉じたい場合は、スコープを短くする工夫が必要です。
4-3. if文・for文・try文の中で使った場合
using varは、if文、for文、try文などのブロック内でも使えます。
たとえば、ifブロック内で使った場合は、ifブロックの終わりでDisposeされます。
C#if (File.Exists("sample.txt"))
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Console.WriteLine(text);
} // reader.Dispose()
for文の中で使った場合は、各ループのブロックを抜けるたびにDisposeされます。
C#foreach (var file in files)
{
using var reader = new StreamReader(file);
string text = reader.ReadToEnd();
Console.WriteLine(text);
} // 各ループごとにreader.Dispose()
tryブロック内で宣言した場合は、tryブロックを抜けるときにDisposeされます。
C#try
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Console.WriteLine(text);
} // tryブロックを抜けるときにDispose
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
ただし、tryブロックの外では、readerはスコープ外になるため使えません。
4-4. using文よりDisposeのタイミングが遅くなるケース
using varを使うと、従来のusing文よりDisposeのタイミングが遅くなることがあります。
たとえば、従来のusing文では次のようになります。
C#void Execute()
{
using (var reader = new StreamReader("sample.txt"))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
} // ここでDispose
DoOtherWork();
}
一方、using varで単純に置き換えると、Disposeのタイミングがメソッドの最後になります。
C#void Execute()
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Console.WriteLine(text);
DoOtherWork();
} // ここでDispose
この場合、DoOtherWork()の実行中もreaderはDisposeされていません。
処理内容によっては問題ありませんが、ファイルやDB接続などのリソースを早めに解放したい場面では注意が必要です。
4-5. スコープを短くしたい場合の書き方
using varでDisposeのタイミングを短くしたい場合は、明示的にブロックを作る方法があります。
C#void Execute()
{
string text;
{
using var reader = new StreamReader("sample.txt");
text = reader.ReadToEnd();
} // ここでreader.Dispose()
DoOtherWork(text);
}
このように書けば、using varのシンプルさを保ちながら、Disposeのタイミングを制御できます。
ただし、スコープを短くしたいだけであれば、従来のusing文を使ったほうが自然な場合もあります。
C#void Execute()
{
string text;
using (var reader = new StreamReader("sample.txt"))
{
text = reader.ReadToEnd();
}
DoOtherWork(text);
}
読みやすさを優先するなら、無理にusing varへ統一する必要はありません。
5. using varを使うメリット
5-1. コードが短く読みやすくなる
using varを使うと、コードを短く書けます。
従来のusing文では、ブロックを書く必要があります。
C#using (var stream = File.OpenRead("sample.txt"))
{
using (var reader = new StreamReader(stream))
{
var text = reader.ReadToEnd();
Console.WriteLine(text);
}
}
using varなら次のように書けます。
C#using var stream = File.OpenRead("sample.txt");
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
Console.WriteLine(text);
処理の流れが一直線になり、余計な波括弧も減ります。
特に、短いメソッドではusing varを使うことで、コード全体が読みやすくなります。
5-2. ネストを減らせる
using varの大きなメリットは、ネストを減らせることです。
ネストが深いコードは、どの処理がどのブロックに属しているのかがわかりにくくなります。
C#using (var a = new ResourceA())
{
using (var b = new ResourceB())
{
using (var c = new ResourceC())
{
c.Execute();
}
}
}
using varを使うと、次のように平坦に書けます。
C#using var a = new ResourceA();
using var b = new ResourceB();
using var c = new ResourceC();
c.Execute();
ネストが減ると、コードレビューもしやすくなります。処理の本質に集中できるため、可読性の向上につながります。
5-3. Dispose忘れを防げる
IDisposableを実装したオブジェクトは、使い終わったらDisposeする必要があります。
手動でDisposeを書く場合、次のように書くこともできます。
C#var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
reader.Dispose();
しかし、この書き方には問題があります。途中で例外が発生すると、Disposeが呼ばれない可能性があるからです。
C#var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd(); // ここで例外が発生する可能性がある
reader.Dispose();
using varを使えば、例外が発生してもスコープを抜けるときにDisposeされます。
C#using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Dispose忘れを防げることは、using varの重要なメリットです。
5-4. リソース管理の意図が伝わりやすい
using varを使うと、その変数が「使い終わったらDisposeすべきリソース」であることが一目でわかります。
C#using var connection = new SqlConnection(connectionString);
このコードを見れば、connectionがリソース管理の対象であることがすぐに伝わります。
通常のvar宣言だけでは、Disposeが必要なオブジェクトなのかどうかがわかりにくい場合があります。
C#var connection = new SqlConnection(connectionString);
using varを使うことで、コード上に「このオブジェクトは自動的に解放される」という意図を明確に表現できます。
6. using varを使うときの注意点
6-1. IDisposableを実装していない型には使えない
using varは、基本的にIDisposableを実装している型に対して使います。
たとえば、次のようなコードはエラーになります。
C#using var name = "Taro";
stringはIDisposableを実装していないため、using varでは扱えません。
using varが使える代表的な型には、次のようなものがあります。
C#using var stream = new FileStream("sample.txt", FileMode.Open);
using var reader = new StreamReader(stream);
using var connection = new SqlConnection(connectionString);
「後片付けが必要なリソース」に対して使う構文だと考えると理解しやすいです。
6-2. Disposeのタイミングを誤解しやすい
using varで特に注意すべきなのが、Disposeされるタイミングです。
次のコードでは、readerは読み込みが終わった直後ではなく、メソッドの終わりでDisposeされます。
C#void Execute()
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Console.WriteLine(text);
DoOtherWork();
} // ここでDispose
using varを書いた行の近くでDisposeされるわけではありません。
using varのDisposeタイミングは、宣言されたスコープの終わりです。
この点を誤解すると、ファイルやDB接続を必要以上に長く保持してしまうことがあります。
6-3. varによって型がわかりにくくなる場合がある
using varは便利ですが、varを使うことで型がわかりにくくなる場合があります。
たとえば、次のようなコードです。
C#using var result = factory.Create();
このコードだけを見ると、resultが何の型なのかわかりにくい場合があります。
型が重要な場面では、次のように明示したほうが読みやすくなります。
C#using FileStream stream = File.OpenRead("sample.txt");
または、変数名で型や役割が伝わるようにします。
C#using var fileStream = File.OpenRead("sample.txt");
using var reader = new StreamReader(fileStream);
varは便利ですが、読み手に負担をかける場合は型を明示することも大切です。
6-4. 例外が発生してもDisposeされる仕組み
using varは、例外が発生した場合でもDisposeされるように設計されています。
たとえば、次のコードでReadToEnd()中に例外が発生したとします。
C#void Execute()
{
using var reader = new StreamReader("sample.txt");
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
この場合でも、スコープを抜けるときにreader.Dispose()が呼び出されます。
イメージとしては、内部的に次のようなtry-finallyに近い形になります。
C#StreamReader reader = new StreamReader("sample.txt");
try
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
finally
{
if (reader != null)
{
reader.Dispose();
}
}
実際のコンパイラの変換は状況によって異なりますが、「finallyでDisposeされるようなもの」と考えると理解しやすいです。
6-5. using varを使わないほうがよいケース
using varは便利ですが、使わないほうがよいケースもあります。
たとえば、リソースを短い範囲で確実に解放したい場合です。
C#void Execute()
{
using var reader = new StreamReader("sample.txt");
var text = reader.ReadToEnd();
LongRunningProcess(text);
} // readerはここまでDisposeされない
この場合、LongRunningProcessの実行中もファイルリソースが保持されます。
読み込み直後に解放したいなら、従来のusing文が適しています。
C#void Execute()
{
string text;
using (var reader = new StreamReader("sample.txt"))
{
text = reader.ReadToEnd();
}
LongRunningProcess(text);
}
また、型がわかりにくい場合や、チーム内でusing文を使う方針がある場合も、無理にusing varへ置き換える必要はありません。
7. using varとusing declarationの関係
7-1. using varはusing declarationの代表的な書き方
using varは、C# 8.0で導入されたusing declaration、つまりusing宣言の一種です。
C#using var resource = new MyResource();
この構文では、resourceが宣言されたスコープの終わりで自動的にDisposeされます。
using declarationには、型を明示する書き方もあります。
C#using MyResource resource = new MyResource();
つまり、using varはusing declarationの中でも、型推論を使った書き方です。
7-2. using宣言とusingステートメントの違い
C#には、似た名前の構文としてusing宣言とusingステートメントがあります。
usingステートメントは、従来からあるブロック付きの構文です。
C#using (var resource = new MyResource())
{
resource.DoSomething();
}
一方、using宣言は、C# 8.0以降で使えるブロックなしの構文です。
C#using var resource = new MyResource();
resource.DoSomething();
違いをまとめると、次のようになります。
C#// usingステートメント
using (var resource = new MyResource())
{
resource.DoSomething();
} // ここでDispose
// using宣言
using var resource = new MyResource();
resource.DoSomething();
// スコープの終わりでDispose
usingステートメントはDispose範囲を明示しやすく、using宣言はネストを減らして簡潔に書けるのが特徴です。
7-3. using varとusing 型名の違い
using varとusing 型名の違いは、型推論を使うかどうかです。
using varでは、右辺から型が推論されます。
C#using var reader = new StreamReader("sample.txt");
この場合、readerの型はStreamReaderです。
型を明示すると、次のようになります。
C#using StreamReader reader = new StreamReader("sample.txt");
どちらもDisposeのタイミングは同じです。違いは、コード上で型を明示しているかどうかだけです。
型が明らかな場合はusing varで問題ありません。
C#using var reader = new StreamReader("sample.txt");
一方、メソッドの戻り値から生成される場合など、型が読み取りにくい場合は型を明示したほうがよいことがあります。
C#using DbConnection connection = factory.CreateConnection();
7-4. using宣言が内部的にどのようにDisposeされるか
using宣言は、スコープを抜けるときにDisposeが呼ばれるようにコンパイラが処理します。
次のコードを考えます。
C#void Execute()
{
using var resource = new MyResource();
resource.DoSomething();
}
これは概念的には、次のようなコードに近い動きになります。
C#void Execute()
{
var resource = new MyResource();
try
{
resource.DoSomething();
}
finally
{
if (resource != null)
{
resource.Dispose();
}
}
}
そのため、途中で例外が発生したとしても、スコープを抜ける際にDisposeが実行されます。
複数のusing宣言がある場合は、後から宣言されたものから順にDisposeされます。
C#using var a = new ResourceA();
using var b = new ResourceB();
using var c = new ResourceC();
// 処理
Disposeの順番は、概念的には次のようになります。
C#c.Dispose();
b.Dispose();
a.Dispose();
依存関係のあるリソースを扱う場合は、この順番も意識しておくとよいでしょう。
8. using varとawait usingの違い
8-1. await usingとは
await usingは、非同期でDisposeするための構文です。
通常のusing varは、IDisposable.Dispose()を呼び出します。
C#using var resource = new MyDisposable();
一方、await usingは、IAsyncDisposable.DisposeAsync()を呼び出します。
C#await using var resource = new MyAsyncDisposable();
非同期処理の中で、リソースの解放にも非同期処理が必要な場合に使います。
たとえば、非同期ストリームや非同期対応のデータベース関連オブジェクトなどで使われることがあります。
8-2. IDisposableとIAsyncDisposableの違い
IDisposableは、同期的にリソースを解放するためのインターフェースです。
C#public interface IDisposable
{
void Dispose();
}
一方、IAsyncDisposableは、非同期でリソースを解放するためのインターフェースです。
C#public interface IAsyncDisposable
{
ValueTask DisposeAsync();
}
using varはIDisposableに対応します。
C#using var resource = new MyDisposable();
await usingはIAsyncDisposableに対応します。
C#await using var resource = new MyAsyncDisposable();
同期的な後片付けで十分ならusing var、非同期の後片付けが必要ならawait usingを使います。
8-3. 非同期処理でawait usingを使うケース
非同期処理の中で、リソースの解放にも非同期処理が必要な場合はawait usingを使います。
C#await using var resource = new MyAsyncResource();
await resource.ExecuteAsync();
たとえば、非同期でデータを書き込んだあと、解放処理でもバッファのフラッシュやネットワーク通信が必要になる場合があります。
より具体的な例として、IAsyncDisposableを実装したクラスは次のように使えます。
C#public class MyAsyncResource : IAsyncDisposable
{
public async ValueTask DisposeAsync()
{
await Task.Delay(100);
Console.WriteLine("非同期でDisposeしました");
}
}
利用側は次のようになります。
C#await using var resource = new MyAsyncResource();
Console.WriteLine("処理中");
スコープを抜けるときに、DisposeAsync()がawaitされます。
8-4. using varとawait usingの使い分け
using varとawait usingは、対象のオブジェクトがどのインターフェースを実装しているかで使い分けます。
IDisposableを実装している場合はusing varです。
C#using var reader = new StreamReader("sample.txt");
IAsyncDisposableを実装している場合はawait usingです。
C#await using var resource = new MyAsyncResource();
非同期メソッドの中だからといって、すべてawait usingにするわけではありません。
たとえば、非同期メソッド内でも、対象がIDisposableだけを実装しているならusing varを使います。
C#public async Task ExecuteAsync()
{
using var reader = new StreamReader("sample.txt");
string text = await reader.ReadToEndAsync();
Console.WriteLine(text);
}
一方、対象がIAsyncDisposableを実装していて、非同期解放が必要ならawait usingを使います。
C#public async Task ExecuteAsync()
{
await using var resource = new MyAsyncResource();
await resource.ExecuteAsync();
}
9. using varのよくあるエラーと対処法
9-1. 「型はIDisposableに変換できません」と表示される
using varを使ったときに、次のような意味のエラーが出ることがあります。
型はIDisposableに変換できません
これは、using varで宣言したオブジェクトがIDisposableを実装していない場合に発生します。
たとえば、次のコードはエラーになります。
C#using var number = 10;
using var text = "hello";
intやstringはIDisposableを実装していないため、using varでは扱えません。
対処法は、usingを外して通常のvarにすることです。
C#var number = 10;
var text = "hello";
using varは、ファイル、ストリーム、DB接続など、Disposeが必要なオブジェクトに対して使いましょう。
9-2. using varで宣言した変数をスコープ外で使えない
using varで宣言した変数は、通常のローカル変数と同じスコープルールに従います。
次のコードは、スコープ外で変数を使おうとしているためエラーになります。
C#if (File.Exists("sample.txt"))
{
using var reader = new StreamReader("sample.txt");
var text = reader.ReadToEnd();
}
Console.WriteLine(reader); // エラー
readerはifブロック内で宣言されているため、ブロックの外では使えません。
必要な値だけを外側の変数に入れるようにします。
C#string? text = null;
if (File.Exists("sample.txt"))
{
using var reader = new StreamReader("sample.txt");
text = reader.ReadToEnd();
}
Console.WriteLine(text);
スコープ外で使いたいのは、Dispose対象のオブジェクトではなく、そこから得た結果であることが多いです。
9-3. Dispose後のオブジェクトを使ってしまう
using varやusing文でDisposeされたオブジェクトを、その後に使おうとするとエラーの原因になります。
たとえば、次のコードでは、ブロックを抜けたあとにstreamを使おうとしています。
C#Stream stream;
{
using var fileStream = File.OpenRead("sample.txt");
stream = fileStream;
} // fileStream.Dispose()
// streamはDispose済みのオブジェクトを参照している
Disposeされたオブジェクトを使うと、ObjectDisposedExceptionが発生することがあります。
対処法は、Dispose対象のオブジェクトをスコープ外に持ち出さないことです。
C#byte[] data;
using (var fileStream = File.OpenRead("sample.txt"))
{
using var memoryStream = new MemoryStream();
fileStream.CopyTo(memoryStream);
data = memoryStream.ToArray();
}
// dataを使う
必要なデータだけを取り出し、Dispose対象のオブジェクト自体は外に出さないようにしましょう。
9-4. C#の言語バージョンが古くて使えない
using varはC# 8.0以降の機能です。
古いプロジェクトで次のように書くと、構文エラーになる場合があります。
C#using var reader = new StreamReader("sample.txt");
対処法は、C#の言語バージョンを上げることです。
プロジェクトファイルで次のように指定できます。
XML<PropertyGroup>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
また、環境によっては.NET SDKのバージョンも関係します。
言語バージョンを変更できない場合は、従来のusing文を使います。
C#using (var reader = new StreamReader("sample.txt"))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
古い環境でも、using文で同じようにDisposeを自動実行できます。
10. using varの実践的な使い分け
10-1. using文を使うべき場面
従来のusing文は、リソースを使う範囲を明確にしたい場合に向いています。
C#string text;
using (var reader = new StreamReader("sample.txt"))
{
text = reader.ReadToEnd();
}
// ここではreaderはDispose済み
Process(text);
このように、ファイルを読み終わったらすぐに閉じたい場合は、using文のほうが意図が明確です。
また、リソースを使う範囲が短く、ブロックで囲んだほうが読みやすい場合にもusing文が適しています。
C#using (var transaction = connection.BeginTransaction())
{
// トランザクション内の処理
}
「この範囲だけで使う」ということを強調したい場合は、using文を使うとよいでしょう。
10-2. using varを使うべき場面
using varは、リソースを現在のスコープの終わりまで使う場合に向いています。
C#using var connection = new SqlConnection(connectionString);
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM Users";
using var reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader["Name"]);
}
このように、複数のリソースを順番に作成し、同じスコープ内で処理する場合はusing varが読みやすくなります。
また、メソッドが短く、Disposeのタイミングがメソッド終端で問題ない場合もusing varが適しています。
C#void WriteLog(string message)
{
using var writer = new StreamWriter("log.txt", append: true);
writer.WriteLine(message);
}
コードをシンプルに保ちたい場合に効果的です。
10-3. await usingを使うべき場面
await usingは、非同期でリソースを解放する必要がある場合に使います。
C#await using var resource = new MyAsyncResource();
await resource.ExecuteAsync();
対象がIAsyncDisposableを実装している場合は、DisposeAsync()を呼び出すためにawait usingを使います。
たとえば、非同期ストリームや非同期対応のリソースを扱う場合です。
C#await using var stream = CreateAsyncStream();
await stream.WriteAsync(data);
ただし、対象がIDisposableしか実装していない場合は、非同期メソッド内であってもusing varを使います。
C#public async Task ReadAsync()
{
using var reader = new StreamReader("sample.txt");
var text = await reader.ReadToEndAsync();
Console.WriteLine(text);
}
非同期メソッドだからawait using、というわけではありません。対象の型がIDisposableなのかIAsyncDisposableなのかを確認することが大切です。
10-4. チーム開発でのコーディングルール
チーム開発では、using varを使うかどうかをルール化しておくとコードの統一感が出ます。
たとえば、次のような方針が考えられます。
・短いメソッドではusing varを優先する
・Dispose範囲を明確にしたい場合はusing文を使う
・型がわかりにくい場合はusing varではなくusing 型名を使う
・IAsyncDisposableにはawait usingを使う
using varは便利ですが、チーム内で使い方がバラバラだと、逆に読みづらくなることがあります。
特に、Disposeのタイミングに関する認識は統一しておくべきです。
C#using var reader = new StreamReader("sample.txt");
このコードを見たときに、「スコープの終わりでDisposeされる」と全員が理解していることが重要です。
また、コードレビューでは、単にusing varを使っているかどうかだけでなく、Disposeのタイミングが適切かを確認しましょう。
11. using varに関するよくある質問
11-1. using varとvarは同じ意味ですか?
同じ意味ではありません。
varは型推論による通常の変数宣言です。
C#var reader = new StreamReader("sample.txt");
一方、using varは、スコープの終わりで自動的にDisposeする変数宣言です。
C#using var reader = new StreamReader("sample.txt");
varだけではDisposeは自動実行されません。using varにすることで、リソース管理の対象になります。
つまり、using varは「型推論された変数を、スコープ終了時にDisposeする」という意味です。
11-2. using varは必ず使ったほうがいいですか?
必ず使う必要はありません。
using varは便利ですが、従来のusing文のほうが適している場面もあります。
リソースをすぐに解放したい場合は、using文のほうがわかりやすいことがあります。
C#using (var reader = new StreamReader("sample.txt"))
{
var text = reader.ReadToEnd();
}
一方、メソッドやブロックの終わりまでリソースを使う場合は、using varが簡潔です。
C#using var reader = new StreamReader("sample.txt");
var text = reader.ReadToEnd();
大切なのは、コードの短さだけでなく、Disposeされるタイミングが自然かどうかです。
11-3. using varはメモリ解放をしてくれますか?
using varは、直接メモリを解放するための構文ではありません。
using varが行うのは、Disposeを呼び出すことです。
C#using var resource = new MyResource();
スコープの終わりで、概念的には次のような処理が行われます。
C#resource.Dispose();
Disposeの中で、ファイルハンドル、DB接続、ストリームなどのアンマネージリソースや関連リソースが解放されます。
一方、通常のマネージメモリの回収は、.NETのガベージコレクションが担当します。
そのため、using varは「メモリを直接解放する」というより、「Disposeが必要なリソースを適切に解放する」と理解するとよいです。
11-4. using varはtry-finallyと同じですか?
完全に同じコードを書くわけではありませんが、考え方としてはtry-finallyに近いです。
たとえば、次のコードがあります。
C#using var reader = new StreamReader("sample.txt");
var text = reader.ReadToEnd();
これは概念的には、次のようにfinallyでDisposeする処理に近い動きになります。
C#var reader = new StreamReader("sample.txt");
try
{
var text = reader.ReadToEnd();
}
finally
{
if (reader != null)
{
reader.Dispose();
}
}
つまり、例外が発生した場合でも、スコープを抜けるときにDisposeされます。
手動でtry-finallyを書くよりも、using varを使ったほうが簡潔でミスが少なくなります。
11-5. using varで複数のリソースを管理できますか?
はい、できます。
複数のusing varを同じスコープ内で宣言できます。
C#using var stream = File.OpenRead("sample.txt");
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
Console.WriteLine(text);
この場合、Disposeは宣言と逆順に行われます。
C#reader.Dispose();
stream.Dispose();
DB接続でも同じように使えます。
C#using var connection = new SqlConnection(connectionString);
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Users";
var count = command.ExecuteScalar();
Console.WriteLine(count);
複数のリソースを扱う場合、using varを使うとネストを増やさずに書けるため、コードが読みやすくなります。
まとめ
using varは、C# 8.0で導入されたusing宣言の書き方で、IDisposableを実装したオブジェクトをスコープ終了時に自動でDisposeするための構文です。
従来のusing文と比べると、ブロックを書かずに済むため、コードを短くし、ネストを減らせるというメリットがあります。
C#using var reader = new StreamReader("sample.txt");
var text = reader.ReadToEnd();
ただし、using varで宣言した変数は、宣言されたスコープの終わりまでDisposeされません。そのため、従来のusing文よりDisposeのタイミングが遅くなる場合があります。
リソースを短い範囲で解放したい場合は、従来のusing文が向いています。
C#using (var reader = new StreamReader("sample.txt"))
{
var text = reader.ReadToEnd();
}
一方、メソッドやブロックの終わりまでリソースを使う場合は、using varが便利です。
非同期でDisposeする必要がある場合は、await usingを使います。
C#await using var resource = new MyAsyncResource();
using var、using文、await usingは、それぞれ役割が異なります。大切なのは、構文の短さだけで選ぶのではなく、Disposeのタイミングとコードの読みやすさを考えて使い分けることです。
C#でファイル、ストリーム、DB接続などを扱うなら、using varを理解しておくことで、安全で読みやすいリソース管理ができるようになります。

