Ressourcenverwaltung: Das use-Schlüsselwort

In diesem Thema werden das Schlüsselwort use und die using-Funktion beschrieben, die zum Steuern der Initialisierung und Freigabe von Ressourcen verwendet werden können.

Ressourcen

Der Begriff Ressource wird auf mehrere Arten verwendet. Ressourcen können zwar auch Daten sein, die von einer Anwendung verwendet werden (also beispielsweise Zeichenfolgen, Grafiken usw.), in diesem Kontext bezieht sich Ressourcen allerdings auf Software- oder Betriebssystemressourcen wie Grafikgerätekontexte, Dateihandles, Netzwerk- und Datenbankverbindungen, Parallelitätsobjekte (beispielsweise Wartehandles) und Ähnliches. Die Verwendung dieser Ressourcen durch Anwendungen umfasst den Bezug der Ressource vom Betriebssystem oder einem anderen Ressourcenanbieter, gefolgt von der späteren Freigabe der Ressource für den Pool, sodass sie für eine andere Anwendung bereitgestellt werden kann. Probleme treten auf, wenn Anwendungen Ressourcen nicht wieder für den allgemeinen Pool freigeben.

Verwalten von Ressourcen

Um Ressourcen in einer Anwendung effizient und verantwortungsvoll verwalten zu können, müssen Ressourcen umgehend und planbar freigegeben werden. .NET Framework stellt dazu die System.IDisposable-Schnittstelle bereit. Ein Typ, der System.IDisposable implementiert, verfügt über die System.IDisposable.Dispose-Methode, die Ressourcen ordnungsgemäß freigibt. Bei gut geschriebenen Anwendungen wird sichergestellt, dass System.IDisposable.Dispose sofort aufgerufen wird, wenn ein Objekt, das eine begrenzte Ressource für sich beansprucht, nicht mehr benötigt wird. Glücklicherweise wird dies in den meisten .NET-Programmiersprachen vereinfacht – auch in F#. Es gibt zwei praktische Sprachkonstrukte, die das Dispose-Muster unterstützen: die use-Bindung und die using-Funktion.

use-Bindung

Das Format des use-Schlüsselworts ähnelt dem der let-Bindung:

use Wert = Ausdruck

Das Schlüsselwort bietet die gleichen Funktionen wie eine let-Bindung, fügt jedoch einen Aufruf von Dispose für den Wert hinzu, wenn sich der Wert nicht mehr innerhalb des Gültigkeitsbereichs befindet. Beachten Sie, dass der Compiler eine NULL-Überprüfung für den Wert einfügt. Das bedeutet: Ist der Wert null, wird nicht versucht, Dispose aufzurufen.

Im folgenden Beispiel wird gezeigt, wie eine Datei mithilfe des use-Schlüsselworts automatisch geschlossen wird:

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."

Mehrere Instanzen von use werden in umgekehrter Reihenfolge verworfen, in der sie deklariert werden. Das heißt, die erste use wird die letzte veröffentlicht werden.

Hinweis

Sie können use in Berechnungsausdrücken verwenden. In diesem Fall wird eine angepasste Version des use-Ausdrucks verwendet. Weitere Informationen finden Sie unter Sequenzen, Asynchrone Ausdrücke, Taskausdrücke und Berechnungsausdrücke.

using-Funktion

Die using-Funktion hat folgendes Format:

using (Ausdruck1) Funktion oder Lambdaausdruck

In einem using-Ausdruck erstellt Ausdruck1 das Objekt, das verworfen werden muss. Das Ergebnis von Ausdruck1 (das zu verwerfende Objekt) wird zu einem Argument (Wert) für Funktion oder Lambdaausdruck. Hierbei handelt es sich entweder um eine Funktion, die ein einzelnes verbleibendes Argument eines Typs erwartet, das dem von Ausdruck1 generierten Wert entspricht, oder um einen Lambdaausdruck, der ein Argument dieses Typs erwartet. Am Ende der Ausführung der Funktion ruft die Runtime Dispose auf und gibt die Ressourcen frei (es sei denn, der Wert ist null; dann wird nicht versucht, „Dispose“ aufzurufen).

Im folgenden Beispiel wird die Verwendung des using-Ausdrucks mit einem Lambdaausdruck veranschaulicht:

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."

Das nächste Beispiel zeigt die Verwendung des using-Ausdrucks mit einer Funktion:

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

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

Beachten Sie, dass die Funktion eine Funktion sein kann, für die bereits einige Argumente angewendet wurden. Dies wird im folgenden Codebeispiel veranschaulicht. Hier wird eine Datei erstellt, die die Zeichenfolge XYZenthält:

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

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

Mit der using-Funktion und der use-Bindung können Sie auf nahezu identische Weise das gleiche Ergebnis erreichen. Das using-Schlüsselwort bietet mehr Kontrolle darüber, wann Dispose aufgerufen wird. Bei Verwendung von using wird Dispose am Ende der Funktion oder des Lambdaausdrucks aufgerufen. Bei Verwendung des use-Schlüsselworts wird Dispose am Ende des enthaltenden Codeblocks aufgerufen. Im Allgemeinen empfiehlt sich die Verwendung von use anstelle der using-Funktion.

Weitere Informationen