Freigeben über


„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 im case-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 das using 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.