Partage via


« utilisation basée sur des modèles » et « utilisation de déclarations »

Remarque

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

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

Vous pouvez en savoir plus sur le processus d’adoption des speclets de fonctionnalités dans la norme de langage C# dans l’article sur les spécifications .

Résumé

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 jetable en plus de IDisposable et ajouter une déclaration de using à la langue.

Motivation

La déclaration using est un outil efficace pour la gestion des ressources aujourd’hui, mais elle nécessite un peu de cérémonie. 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 participer ici. Dans de nombreux cas, la suppression de la nécessité de créer des types wrapper qui existent uniquement pour autoriser une utilisation de valeurs dans une instruction using.

Ensemble, ces fonctionnalités permettent aux développeurs de simplifier et d’étendre les scénarios où using peuvent être appliqués.

Conception détaillée

utilisation d'une déclaration

La langue permet d’ajouter using à une déclaration de 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:\users\jaredpar\using.md");
   // statements
}

// Equivalent to 
if (...) 
{ 
   using (FileStream f = new FileStream(@"C:\users\jaredpar\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 de case, mais doit se trouver dans un bloc à l’intérieur de l’étiquette case.
  • Peut ne pas apparaître dans le cadre 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 remplir le modèle de using.

utilisation basée sur un modèle

Le langage ajoute la notion d’un modèle jetable pour les types ref struct : il s’agit d’un 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 également : les variables locales déclarées dans l'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 cast en IDisposable avant d’appeler Dispose :

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

Pour ajuster le modèle jetable, la méthode Dispose doit être un membre d’instance accessible, sans paramètre 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.