„Musterbasierte Verwendung” und „Verwendung von Deklarationen”
Anmerkung
Dieser Artikel ist eine Featurespezifikation. Die Spezifikation dient als Designdokument für das Feature. Es enthält vorgeschlagene Spezifikationsänderungen sowie Informationen, die während des Entwurfs und der Entwicklung des Features erforderlich sind. Diese Artikel werden veröffentlicht, bis die vorgeschlagenen Spezifikationsänderungen abgeschlossen und in die aktuelle ECMA-Spezifikation aufgenommen werden.
Es kann einige Abweichungen zwischen der Featurespezifikation und der abgeschlossenen Implementierung geben. Diese Unterschiede werden in den entsprechenden Hinweisen zum Language Design Meeting (LDM) erfasst.
Weitere Informationen zur Einführung von Funktionen in den C#-Sprachstandard finden Sie im Artikel zu den Spezifikationen.
Zusammenfassung
Die Sprache fügt zwei neue Funktionen um die using
Anweisung hinzu, um die Ressourcenverwaltung zu vereinfachen: using
sollte zusätzlich zu IDisposable
ein verfügbares Muster erkennen und der Sprache eine using
-Deklaration hinzufügen.
Motivation
Die using
-Aussage ist heute ein effektives Instrument für die Ressourcenverwaltung, erfordert aber zusätzlichen Aufwand. Methoden, die mehrere Ressourcen zu verwalten haben, können syntaktisch in eine Vielzahl von using
-Anweisungen verstrickt werden. Diese Syntaxlast reicht aus, dass die meisten Codierungsstilrichtlinien explizit eine Ausnahme in geschweiften Klammern für dieses Szenario aufweisen.
Die using
-Deklaration entfernt hier einen Großteil der Zeremonie und bringt C# auf eine Stufe mit anderen Sprachen, die Ressourcenverwaltungsblöcke enthalten. Darüber hinaus können Entwickler mit der musterbasierten using
die Gruppe von Typen erweitern, die hier teilnehmen können. In vielen Fällen wird die Notwendigkeit entfernt, Wrappertypen zu erstellen, die nur vorhanden sind, um die Verwendung von Werten in einer using
-Anweisung zu ermöglichen.
Zusammen mit diesen Features können Entwickler die Szenarien vereinfachen und erweitern, in denen using
angewendet werden können.
Detailliertes Design
using-Deklaration
Die Sprache ermöglicht es, using
einer lokalen Variablendeklaration hinzuzufügen. Eine solche Deklaration hat dieselbe Wirkung wie das Deklarieren der Variablen in einer using
-Anweisung an derselben Position.
if (...)
{
using FileStream f = new FileStream(@"C:\users\jaredpar\using.md");
// statements
}
// Equivalent to
if (...)
{
using (FileStream f = new FileStream(@"C:\users\jaredpar\using.md"))
{
// statements
}
}
Die Lebensdauer einer lokalen using
-Variable wird bis zum Ende des Bereichs verlängert, in dem sie deklariert wird. Die lokalen using
-Variablen werden dann in umgekehrter Reihenfolge verworfen, in der sie deklariert werden.
{
using var f1 = new FileStream("...");
using var f2 = new FileStream("...");
using var f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
}
Es gibt keine Einschränkungen im Bereich goto
oder eines anderen Steuerungsflusskonstrukts im Hinblick auf eine using
-Deklaration. Stattdessen verhält sich der Code genauso wie bei der entsprechenden using
-Anweisung.
{
using var f1 = new FileStream("...");
target:
using var f2 = new FileStream("...");
if (someCondition)
{
// Causes f2 to be disposed but has no effect on f1
goto target;
}
}
Eine in einer using
lokalen Deklaration deklarierte Variable ist implizit schreibgeschützt. Dies entspricht dem Verhalten von lokalen Variablen, die in einer using
-Anweisung deklariert sind.
Die Sprachgrammatik für using
-Deklarationen lautet wie folgt:
local-using-declaration:
'using' type using-declarators
using-declarators:
using-declarator
using-declarators , using-declarator
using-declarator:
identifier = expression
Einschränkungen im Hinblick auf using
-Deklaration:
- Kann nicht direkt in einem
case
-Label erscheinen, sondern muss innerhalb eines Blocks imcase
-Label sein. - Darf nicht als Teil einer
out
-Variablendeklaration erscheinen. - Es muss ein Initialisierer für jeden Deklarator vorhanden sein.
- Der lokale Typ muss implizit in
IDisposable
konvertierbar sein oder dasusing
Muster erfüllen.
musterbasierte Verwendung
Die Sprache fügt den Begriff eines verwerfbaren Musters für Typen der Klasse ref struct
hinzu: das ist ein ref struct
, das über eine zugängliche Dispose
-Instanzmethode verfügt. Typen, die in das verwerfbare Muster passen, können in einer using
-Anweisung oder Deklaration verwendet werden, ohne IDisposable
implementieren zu müssen.
ref struct Resource
{
public void Dispose() { ... }
}
using (var r = new Resource())
{
// statements
}
Auf diese Weise können Entwickler using
für Typen von ref struct
nutzen. Diese Typen können keine Interfaces in C# 8 implementieren und nehmen daher nicht an using
-Anweisungen teil.
Die gleichen Einschränkungen einer herkömmlichen using
-Anweisung gelten auch hier: Lokale Variablen, die in der using
deklariert sind, sind schreibgeschützt; ein null
-Wert führt nicht dazu, dass eine Ausnahme ausgelöst wird usw. ... Die Codegenerierung unterscheidet sich nur darin, dass keine Umwandlung in IDisposable
erfolgt, bevor Dispose aufgerufen wird:
{
Resource r = new Resource();
try {
// statements
}
finally {
if (r != null) r.Dispose();
}
}
Damit das Entwurfsmuster für verwertbare Objekte passt, muss die Dispose
-Methode eine zugängliche Instanzmethode sein, parameterlos und einen void
-Rückgabetyp besitzen. Es kann keine Erweiterungsmethode sein.
Überlegungen
Keine dieser Überlegungen wurde in C# 8 implementiert.
Fallbeschriftungen ohne Blöcke
Ein using declaration
ist aufgrund von Komplikationen um seine tatsächliche Lebensdauer direkt innerhalb einer case
-Beschriftung illegal. Eine mögliche Lösung besteht darin, es einfach die gleiche Lebensdauer wie ein out var
an demselben Ort zu geben. Es wurde entschieden, dass die zusätzliche Komplexität bei der Implementierung der Funktion und die Einfachheit des Workarounds (einfach einen Block zum case
-Label hinzufügen) diesen Weg nicht rechtfertigten.
Zukünftige Erweiterungen
feste Variablen
Eine fixed
-Anweisung verfügt über alle Eigenschaften von using
-Anweisungen, die die Möglichkeit motiviert haben, lokale using
-Variablen zu verwenden. Es sollte berücksichtigt werden, dieses Feature auch auf lokale fixed
-Variablen zu erweitern. Die Lebensdauer- und Sortierregeln sollten für using
und fixed
hier gleichermaßen gut gelten.
C# feature specifications