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 XYZ
enthä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.