Udostępnij za pośrednictwem


"wzorcowe użycie" i "deklaracje dotyczące użycia"

Notatka

Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.

Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).

Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .

Problem z liderem: https://github.com/dotnet/csharplang/issues/114

Streszczenie

Język doda dwie nowe funkcje wokół instrukcji using, aby ułatwić zarządzanie zasobami: using powinien rozpoznawać wzorzec jednorazowego użytku oprócz IDisposable oraz dodać deklarację using do języka.

Motywacja

Oświadczenie using jest skutecznym narzędziem do zarządzania zasobami obecnie, ale wymaga sporo formalności. Metody, które mają wiele zasobów do zarządzania, mogą się stać składniowo skomplikowane przez serię instrukcji using. To obciążenie składniowe jest na tyle duże, że większość wytycznych dotyczących stylu kodowania wyraźnie przewiduje wyjątek dotyczący nawiasów klamrowych dla tego scenariusza.

Deklaracja using usuwa dużą część zbędnych formalności i stawia język C# na równi z innymi językami, które zawierają bloki zarządzania zasobami. Ponadto oparty na wzorcu using umożliwia deweloperom rozwijanie zestawu typów, które mogą uczestniczyć tutaj. W wielu przypadkach oznacza to usunięcie konieczności tworzenia typów otoki, które istnieją wyłącznie po to, aby umożliwić użycie wartości w instrukcji using.

Te funkcje pozwalają deweloperom uprościć i rozszerzyć scenariusze, w których można zastosować using.

Szczegółowy projekt

przy użyciu deklaracji

Język umożliwi dodanie using do deklaracji zmiennej lokalnej. Taka deklaracja będzie miała taki sam efekt jak deklarowanie zmiennej w instrukcji using w tej samej lokalizacji.

if (...) 
{ 
   using FileStream f = new FileStream(@"C:\source\using.md");
   // statements
}

// Equivalent to 
if (...) 
{ 
   using (FileStream f = new FileStream(@"C:\source\using.md")) 
   {
    // statements
   }
}

Okres istnienia using lokalnego będzie rozciągać się do końca zakresu, w którym jest zadeklarowany. using lokalne zmienne zostaną następnie usunięte w odwrotnej kolejności, w której je zadeklarowano.

{ 
    using var f1 = new FileStream("...");
    using var f2 = new FileStream("...");
    using var f3 = new FileStream("...");
    ...
    // Dispose f3
    // Dispose f2 
    // Dispose f1
}

Nie ma żadnych ograniczeń dotyczących gotoani żadnej innej konstrukcji przepływu sterowania w obliczu deklaracji using. Zamiast tego kod działa tak samo jak w przypadku równoważnej instrukcji using:

{
    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;
    }
}

Lokalna zmienna zadeklarowana w deklaracji lokalnej using stanie się niejawnie tylko do odczytu. Jest to zgodne z zachowaniem zmiennych lokalnych określonych w instrukcji using.

Gramatyka języka dla deklaracji using będzie następująca:

local-using-declaration:
  'using' type using-declarators

using-declarators:
  using-declarator
  using-declarators , using-declarator
  
using-declarator:
  identifier = expression

Ograniczenia dotyczące deklaracji using:

  • Może nie pojawiać się bezpośrednio wewnątrz etykiety case, ale zamiast tego musi znajdować się w bloku wewnątrz etykiety case.
  • Może nie być wyświetlana jako część deklaracji zmiennej out.
  • Musi mieć inicjalizator dla każdego deklaratora.
  • Typ lokalny musi być niejawnie konwertowany na IDisposable lub spełniać wzorzec using.

oparte na wzorcu przy użyciu

Język doda pojęcie jednorazowego wzorca dla typów ref struct: jest to ref struct, który ma dostępną metodę wystąpienia Dispose. Typy pasujące do wzorca jednorazowego mogą brać udział w deklaracji lub instrukcji using bez potrzeby implementacji IDisposable.

ref struct Resource
{ 
    public void Dispose() { ... }
}

using (var r = new Resource())
{
    // statements
}

Umożliwi to programistom wykorzystanie using dla typów ref struct. Te typy nie mogą implementować interfejsów w języku C# 8 i dlatego nie mogą uczestniczyć w instrukcjach using.

Te same ograniczenia dotyczące tradycyjnej instrukcji using mają zastosowanie również: zmienne lokalne zadeklarowane w using są tylko do odczytu, wartość null nie spowoduje zgłoszenia wyjątku itp. Generowanie kodu będzie inne tylko w tym, że nie będzie rzutowania do IDisposable przed wywołaniem Dispose:

{
	  Resource r = new Resource();
	  try {
		    // statements
	  }
	  finally {
		    if (r != null) r.Dispose();
	  }
}

Aby dopasować wzorzec jednorazowego użycia, metoda Dispose musi być dostępnym składowym obiektem, bezparametrowa i mieć typ zwracany void. Nie może to być metoda rozszerzenia.

Zagadnienia dotyczące

Żadna z tych kwestii nie została zaimplementowana w języku C# 8

etykiety przypadku bez bloków

using declaration jest nielegalne bezpośrednio wewnątrz etykiety case ze względu na komplikacje związane z jego faktycznym czasem trwania. Jednym z potencjalnych rozwiązań jest po prostu nadanie mu tego samego okresu istnienia co out var w tej samej lokalizacji. Uznano, że dodatkowa złożoność implementacji tej funkcji i prostota obejścia problemu (wystarczy dodać blok do etykiety case) nie uzasadniają wybrania tej ścieżki.

Przyszłe rozszerzenia

ustalone lokalne ustawienia

Instrukcja fixed ma wszystkie właściwości instrukcji using, które motywowały możliwość posiadania lokalnych using. Należy również rozważyć rozszerzenie tej funkcji na fixed lokalne. Reguły okresu istnienia i porządkowania powinny być stosowane równie dobrze dla using i fixed tutaj.