リソースの管理: use キーワード

このトピックでは、リソースの初期化と解放を制御できるキーワード use および using 関数について説明します。

リソース

"リソース" という用語は、さまざまな形で使用されます。 リソースは、アプリケーションで使用される文字列やグラフィックスなどのデータを指す場合がありますが、このコンテキストでは、 "リソース" は、グラフィックス デバイス コンテキスト、ファイル ハンドル、ネットワークとデータベース接続、待機ハンドルなどの同時実行オブジェクトなど、ソフトウェアまたはオペレーティング システムのリソースを指します。 これらのリソースのアプリケーションでの使用には、オペレーティング システムまたは他のリソース プロバイダーからリソースを取得し、その後、リソースをプールに解放することにより、別のアプリケーションに提供できるようにすることが含まれます。 アプリケーションでリソースが共通プールに解放されないと、問題が発生します。

リソースの管理

アプリケーションでリソースを効率的かつ確実に管理するには、リソースを迅速かつ予測可能な方法で解放する必要があります。 .NET Framework を使用すると System.IDisposable インターフェイスが提供され、行いやすくなります。 System.IDisposable を実装する型には、リソースを正しく解放する System.IDisposable.Dispose メソッドがあります。 適切に作成されたアプリケーションを使用すると、制限されているリソースを保持するオブジェクトが不要になったときに System.IDisposable.Dispose が迅速かつ確実に呼び出されます。 幸いなことに、ほとんどの .NET 言語では、これを容易にするためのサポートが提供されており、F# も例外ではありません。 破棄パターンをサポートする便利な言語構成要素として、use バインディングと using 関数の 2 つがあります。

use バインディング

use キーワードには、let バインディングのそれと似た以下の形式のものがあります。

use value = expression

これは let バインディングと同じ機能を提供しますが、値がスコープ外になったときに Dispose への呼び出しを値に追加します。 コンパイラにより値に null チェックが挿入されるため、値が null の場合、Dispose への呼び出しは試行されないことにご注意ください。

次の例は、use キーワードを使用してファイルを自動的に閉じる方法を示しています。

open System.IO

let writetofile filename obj =
   use file1 = File.CreateText(filename)
   file1.WriteLine("{0}", obj.ToString() )
   // file1.Dispose() is called implicitly here.

writetofile "abc.txt" "Humpty Dumpty sat on a wall."

use の複数のインスタンスは、宣言された順序とは逆の順序で破棄されます。 つまり、最初の use が最後にリリースされます。

Note

use は、コンピュテーション式で使用することができます。その場合は、use 式のカスタマイズされたバージョンを使用します。 詳細については、シーケンス非同期式タスク式およびコンピュテーション式に関するページを参照してください。

using 関数

using 関数の形式は次のとおりです。

using (expression1) function-or-lambda

using 式では、expression1 により、破棄する必要のあるオブジェクトが作成されます。 expression1 (破棄する必要のあるオブジェクト) の結果は、function-or-lambda に対する引数 value になります。これは、expression1 によって生成された値と一致する型の残りの 1 つの引数を予期する関数、またはその型の引数を予期するラムダ式のいずれかになります。 関数の実行が終了すると、ランタイムにより Dispose が呼び出され、リソースが解放されます (値が null の場合を除きます。この場合、Dispose への呼び出しは試行されません)。

次の例は、ラムダ式を含む using 式を示しています。

open System.IO

let writetofile2 filename obj =
    using (System.IO.File.CreateText(filename)) ( fun file1 ->
        file1.WriteLine("{0}", obj.ToString() )
    )

writetofile2 "abc2.txt" "The quick sly fox jumps over the lazy brown dog."

次の例は、関数を含む using 式を示しています。

let printToFile (file1 : System.IO.StreamWriter) =
    file1.WriteLine("Test output");

using (System.IO.File.CreateText("test.txt")) printToFile

関数は、いくつかの引数が既に適用されている関数である可能性があることに注意してください。 次のコード例はこの処理方法を示しています。 文字列 XYZ を含むファイルが作成されます。

let printToFile2 obj (file1 : System.IO.StreamWriter) =
    file1.WriteLine(obj.ToString())

using (System.IO.File.CreateText("test.txt")) (printToFile2 "XYZ")

using 関数と use バインディングは、同じことを実現するためのほぼ同じ手法です。 using キーワードを使用すると、Dispose が呼び出されるタイミングをより細かく制御できます。 using を使用すると、関数またはラムダ式の末尾で Dispose が呼び出されます。use キーワードを使用すると、含まれているコード ブロックの末尾で Dispose が呼び出されます。 一般に、using 関数ではなく、use を使用することをお勧めします。

関連項目