Partager via


« déclarations using » et « déclarations using basées sur un modèle »

Remarque

Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Elle inclut les changements de spécification proposés, ainsi que les informations nécessaires à la conception et au développement de la fonctionnalité. Ces articles sont publiés jusqu'à ce que les changements proposés soient finalisés et incorporés dans la spécification ECMA actuelle.

Il peut y avoir des divergences entre la spécification de la fonctionnalité et l'implémentation réalisée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).

Pour en savoir plus sur le processus d'adoption des speclets de fonctionnalité dans la norme du langage C#, consultez l'article sur les spécifications.

Problème de champion : https://github.com/dotnet/csharplang/issues/114

Récapitulatif

Le langage ajoute deux nouvelles fonctionnalités autour de l’instruction using afin de simplifier la gestion des ressources : using doit reconnaître un modèle supprimable en plus de IDisposable et ajouter une déclaration using au langage.

Motivation

La déclaration using est un outil efficace pour la gestion des ressources aujourd’hui, mais elle nécessite un peu de préparation. Les méthodes qui ont plusieurs ressources à gérer peuvent être encombrées syntaxiquement avec une série d’instructions using. Cette charge syntaxique est suffisamment importante pour que la plupart des lignes directrices relatives au style de codage prévoient explicitement une exception concernant les accolades pour ce scénario.

La déclaration using élimine une grande partie de la formalité ici et met C# au niveau des autres langages qui incluent des blocs de gestion des ressources. En outre, le using basé sur des modèles permet aux développeurs d’étendre l’ensemble des types qui peuvent contribuer. Dans de nombreux cas, cela supprime la nécessité de créer des types de wrapper qui existent uniquement pour autoriser une utilisation de valeurs dans une instruction using.

L’ensemble de ces fonctionnalités permet aux développeurs de simplifier et d’étendre les scénarios où using peut être appliqué.

Conception détaillée

utilisation d'une déclaration

Le langage permet d’ajouter using à la déclaration d’une variable locale. Une telle déclaration aura le même effet que la déclaration de la variable dans une instruction using au même emplacement.

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

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

La durée de vie d'un local using s'étendra jusqu'à la fin de l'étendue dans laquelle il est déclaré. Les variables locales using seront ensuite supprimées dans l'ordre inverse dans lequel elles sont déclarées.

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

Il n'y a pas de restrictions autour de goto, ou de toute autre construction de flux de contrôle face à une déclaration using. Au lieu de cela, le code agit comme il le ferait pour l’instruction using équivalente :

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

Une variable locale déclarée dans une déclaration locale using sera implicitement en lecture seule. Cela correspond au comportement des locales déclarées dans une instruction using.

La grammaire du langage pour les déclarations using est la suivante :

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

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

Restrictions relatives à la déclaration using :

  • Peut ne pas apparaître directement à l’intérieur d’une étiquette case, mais doit se trouver dans un bloc à l’intérieur de l’étiquette case.
  • Peut ne pas apparaître en tant que partie d’une déclaration de variable out.
  • Doit avoir un initialiseur pour chaque déclarateur.
  • Le type local doit être implicitement convertible en IDisposable ou satisfaire au modèle using.

utilisation basée sur un modèle

Le langage ajoute la notion d’un modèle supprimable pour les types ref struct : il s’agit d’une ref struct qui a une méthode d’instance Dispose accessible. Les types qui correspondent au modèle jetable peuvent participer à une instruction ou à une déclaration using sans être obligés d'implémenter IDisposable.

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

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

Cela permettra aux développeurs de tirer parti de using pour les types ref struct. Ces types ne peuvent pas implémenter d’interfaces en C# 8 et ne peuvent donc pas participer à des instructions using.

Les mêmes restrictions d’une instruction using traditionnelle s’appliquent ici: les variables locales déclarées dans using sont en lecture seule, une valeur null n’entraîne pas la levée d’une exception, etc. La génération de code sera différente uniquement dans le cas où il n’y aura pas de conversion en IDisposable avant d’appeler Dispose :

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

Pour ajuster le modèle supprimable, la méthode Dispose doit être un membre d’instance accessible, sans paramètres et avoir un type de retour void. Il ne peut pas s’agir d’une méthode d’extension.

Considérations

Aucune de ces considérations n’a été mise en œuvre en C# 8

étiquettes de cas sans blocs

Un using declaration est illégal directement à l'intérieur d'une étiquette case en raison des complications liées à sa durée de vie réelle. Une solution potentielle consiste simplement à lui donner la même durée de vie qu’une out var dans le même emplacement. Il a été jugé que la complexité supplémentaire de l'implémentation de la fonctionnalité et la facilité de contournement (il suffit d'ajouter un bloc à l'étiquette case) ne justifient pas de suivre cette voie.

Extensions futures

Locales fixes

Une instruction fixed possède toutes les propriétés des instructions using qui ont motivé la possibilité d'avoir des éléments locaux using. Il conviendrait d'envisager d'étendre cette fonctionnalité aux locals fixed. Les règles de durée de vie et de classement doivent s’appliquer de la même manière pour using et fixed ici.