Partager via


Analyseurs Roslyn et bibliothèque prenant en charge le code pour ImmutableArrays

La plateforme du compilateur .NET (« Roslyn ») vous aide à créer des bibliothèques prenant en charge le code. Une bibliothèque prenant en charge le code fournit des fonctionnalités que vous pouvez utiliser et outils (analyseurs Roslyn) pour vous aider à utiliser la bibliothèque de la meilleure façon ou pour éviter les erreurs. Cette rubrique vous montre comment créer un analyseur Roslyn réel pour détecter les erreurs courantes lors de l’utilisation du package NuGet System.Collections.Immutable . L’exemple montre également comment fournir un correctif de code pour un problème de code trouvé par l’analyseur. Les utilisateurs voient les correctifs de code dans l’interface utilisateur de l’ampoule Visual Studio et peuvent appliquer automatiquement un correctif pour le code.

Bien démarrer

Vous avez besoin des éléments suivants pour générer cet exemple :

  • Visual Studio 2015 (pas une édition Express) ou une version ultérieure. Vous pouvez utiliser gratuitement Visual Studio Community Edition
  • Visual Studio SDK. Vous pouvez également, lors de l’installation de Visual Studio, case activée Outils d’extensibilité Visual Studio sous Common Tools pour installer le Kit de développement logiciel (SDK) en même temps. Si vous avez déjà installé Visual Studio, vous pouvez également installer ce Kit de développement logiciel (SDK) en accédant au nouveau>projet de fichier de menu> principal, en choisissant C# dans le volet de navigation gauche, puis en choisissant Extensibilité. Lorsque vous choisissez le modèle de projet de navigation « Installer les outils d’extensibilité Visual Studio », il vous invite à télécharger et installer le Kit de développement logiciel (SDK).
  • Kit de développement logiciel (SDK) de la plateforme du compilateur .NET (« Roslyn »). Vous pouvez également installer ce SDK en accédant au nouveau>projet de fichier de menu> principal, en choisissant C# dans le volet de navigation gauche, puis en choisissant Extensibilité. Lorsque vous choisissez le modèle de projet de navigation « Télécharger le Kit de développement logiciel (SDK) de la plateforme du compilateur .NET », il vous invite à télécharger et installer le Kit de développement logiciel (SDK). Ce KIT SDK inclut le visualiseur de syntaxe Roslyn. Cet outil utile vous aide à déterminer les types de modèles de code que vous devez rechercher dans votre analyseur. L’infrastructure de l’analyseur appelle votre code pour des types de modèles de code spécifiques, de sorte que votre code s’exécute uniquement si nécessaire et ne peut se concentrer que sur l’analyse du code approprié.

Quel est le problème ?

Imaginez que vous fournissez une bibliothèque avec la prise en charge d’ImmutableArray (par exemple). System.Collections.Immutable.ImmutableArray<T> Les développeurs C# ont beaucoup d’expérience avec les tableaux .NET. Toutefois, en raison de la nature des techniques ImmutableArrays et d’optimisation utilisées dans l’implémentation, les intuitions des développeurs C# entraînent l’écriture de code rompu par les utilisateurs de votre bibliothèque, comme expliqué ci-dessous. En outre, les utilisateurs ne voient pas leurs erreurs tant que l’exécution n’est pas l’expérience de qualité utilisée dans Visual Studio avec .NET.

Les utilisateurs sont familiarisés avec l’écriture de code comme suit :

var a1 = new int[0];
Console.WriteLine("a1.Length = { 0}", a1.Length);
var a2 = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("a2.Length = { 0}", a2.Length);

La création de tableaux vides pour remplir les lignes de code suivantes et l’utilisation de la syntaxe d’initialiseur de collection sont familières aux développeurs C#. Toutefois, l’écriture du même code pour un ImmutableArray se bloque au moment de l’exécution :

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = { 0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = { 0}", b2.Length);

La première erreur est due à l’implémentation ImmutableArray à l’aide d’un struct pour encapsuler le stockage de données sous-jacent. Les structs doivent avoir des constructeurs sans paramètre afin que default(T) les expressions puissent retourner des structs avec tous les membres zéro ou Null. Lorsque le code accède b1.Length, il existe une erreur de déréférencement null au moment de l’exécution, car il n’existe aucun tableau de stockage sous-jacent dans le struct ImmutableArray. La bonne façon de créer un ImmutableArray vide est ImmutableArray<int>.Empty.

L’erreur avec les initialiseurs de collection se produit, car la ImmutableArray.Add méthode retourne de nouvelles instances chaque fois que vous l’appelez. Étant donné que ImmutableArrays ne change jamais, lorsque vous ajoutez un nouvel élément, vous récupérez un nouvel objet ImmutableArray (qui peut partager le stockage pour des raisons de performances avec un ImmutableArray existant précédemment). Étant donné que b2 pointe vers le premier ImmutableArray avant d’appeler Add() cinq fois, b2 est un ImmutableArray par défaut. L’appel de length sur celui-ci se bloque également avec une erreur de déréférencement null. La bonne façon d’initialiser un ImmutableArray sans appeler manuellement Add consiste à utiliser ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).

Rechercher des types de nœuds de syntaxe pertinents pour déclencher votre analyseur

Pour commencer à générer l’analyseur, commencez par déterminer le type de SyntaxNode que vous devez rechercher. Lancez le visualiseur de syntaxe à partir du menu Afficher>d’autres visualiseurs de syntaxe Roslyn Windows.>

Placez le caret de l’éditeur sur la ligne qui déclare b1. Le visualiseur de syntaxe s’affiche dans un LocalDeclarationStatement nœud de l’arborescence de syntaxe. Ce nœud a un VariableDeclaration, qui à son tour a un VariableDeclarator, qui à son tour a un EqualsValueClause, et enfin il y a un ObjectCreationExpression. Lorsque vous cliquez dans l’arborescence du visualiseur de syntaxe des nœuds, la syntaxe dans la fenêtre de l’éditeur met en surbrillance pour afficher le code représenté par ce nœud. Les noms des sous-types SyntaxNode correspondent aux noms utilisés dans la grammaire C#.

Créer le projet d’analyseur

Dans le menu principal, choisissez Fichier>nouveau>projet. Dans la boîte de dialogue Nouveau projet , sous Projets C# dans la barre de navigation gauche, choisissez Extensibilité et, dans le volet droit, choisissez Analyseur avec le modèle de projet Correctif de code. Entrez un nom et confirmez la boîte de dialogue.

Le modèle ouvre un fichier DiagnosticAnalyzer.cs . Choisissez cet onglet de mémoire tampon de l’éditeur. Ce fichier possède une classe d’analyseur (formée à partir du nom que vous avez donné au projet) qui dérive d’un DiagnosticAnalyzer (type d’API Roslyn). Votre nouvelle classe a une DiagnosticAnalyzerAttribute déclaration de votre analyseur est pertinente pour le langage C# afin que le compilateur découvre et charge votre analyseur.

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzerAnalyzer : DiagnosticAnalyzer
{}

Vous pouvez implémenter un analyseur à l’aide de Visual Basic qui cible le code C#, et vice versa. Il est plus important dans DiagnosticAnalyzerAttribute de choisir si votre analyseur cible un langage ou les deux. Les analyseurs plus sophistiqués qui nécessitent une modélisation détaillée du langage ne peuvent cibler qu’une seule langue. Si votre analyseur, par exemple, n’case activée s noms de types ou noms de membres publics, il peut être possible d’utiliser les offres Roslyn du modèle de langage commun dans Visual Basic et C#. Par exemple, FxCop avertit qu’une classe implémente ISerializable, mais que la classe n’a pas l’attribut SerializableAttribute est indépendante du langage et fonctionne pour le code Visual Basic et C#.

Initialiser l’analyseur

Faites défiler vers le bas un peu dans la DiagnosticAnalyzer classe pour voir la Initialize méthode. Le compilateur appelle cette méthode lors de l’activation d’un analyseur. La méthode prend un AnalysisContext objet qui permet à votre analyseur d’obtenir des informations de contexte et d’inscrire des rappels pour les événements pour les types de code que vous souhaitez analyser.

public override void Initialize(AnalysisContext context) {}

Ouvrez une nouvelle ligne dans cette méthode et tapez « context ». Pour afficher une liste d’achèvement IntelliSense. Vous pouvez voir dans la liste de saisie semi-automatique de nombreuses Register... méthodes pour gérer différents types d’événements. Par exemple, le premier, appelle RegisterCodeBlockActionvotre code pour un bloc, qui est généralement du code entre accolades. L’inscription d’un bloc rappelle également votre code pour l’initialiseur d’un champ, la valeur donnée à un attribut ou la valeur d’un paramètre facultatif.

En guise d’autre exemple, RegisterCompilationStartActionrevient à votre code au début d’une compilation, ce qui est utile lorsque vous devez collecter l’état sur de nombreux emplacements. Vous pouvez créer une structure de données, par exemple, pour collecter tous les symboles utilisés, et chaque fois que votre analyseur est rappelé pour une syntaxe ou un symbole, vous pouvez enregistrer des informations sur chaque emplacement de votre structure de données. Lorsque vous êtes rappelé en raison de la fin de la compilation, vous pouvez analyser tous les emplacements que vous avez enregistrés, par exemple, pour signaler les symboles que le code utilise à partir de chaque using instruction.

À l’aide du visualiseur de syntaxe, vous avez appris que vous souhaitez être appelé lorsque le compilateur traite un ObjectCreationExpression. Vous utilisez ce code pour configurer le rappel :

context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
                                 SyntaxKind.ObjectCreationExpression);

Vous vous inscrivez à un nœud de syntaxe et filtrez uniquement pour les nœuds de syntaxe de création d’objets. Par convention, les auteurs d’analyseurs utilisent une lambda lors de l’inscription d’actions, ce qui permet de conserver les analyseurs sans état. Vous pouvez utiliser la fonctionnalité Visual Studio Generate From Usage pour créer la AnalyzeObjectCreation méthode. Cela génère également le type de paramètre de contexte approprié.

Définir des propriétés pour les utilisateurs de votre analyseur

Afin que votre analyseur s’affiche correctement dans l’interface utilisateur de Visual Studio, recherchez et modifiez la ligne de code suivante pour identifier votre analyseur :

internal const string Category = "Naming";

Changez "Naming" en "API Guidance".

Ensuite, recherchez et ouvrez le fichier Resources.resx dans votre projet à l’aide de la Explorateur de solutions. Vous pouvez placer une description pour votre analyseur, votre titre, etc. Vous pouvez modifier la valeur de toutes ces "Don't use ImmutableArray<T> constructor" valeurs pour le moment. Vous pouvez placer des arguments de mise en forme de chaîne dans votre chaîne ({0}, {1}etc.) et ultérieurement lorsque vous appelez Diagnostic.Create(), vous pouvez fournir un params tableau d’arguments à passer.

Analyser une expression de création d’objet

La AnalyzeObjectCreation méthode prend un autre type de contexte fourni par l’infrastructure d’analyseur de code. La Initialize méthode vous permet d’inscrire des rappels d’action AnalysisContext pour configurer votre analyseur. Le SyntaxNodeAnalysisContext, par exemple, a un CancellationToken que vous pouvez passer. Si un utilisateur commence à taper dans l’éditeur, Roslyn annule l’exécution des analyseurs pour enregistrer le travail et améliorer les performances. Comme autre exemple, ce contexte a une propriété Node qui retourne le nœud de syntaxe de création d’objet.

Obtenez le nœud, que vous pouvez supposer est le type pour lequel vous avez filtré l’action de nœud de syntaxe :

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Lancez Visual Studio avec votre analyseur la première fois

Lancez Visual Studio en créant et en exécutant votre analyseur (appuyez sur F5). Étant donné que le projet de démarrage dans le Explorateur de solutions est le projet VSIX, l’exécution de votre code génère votre code et un VSIX, puis lance Visual Studio avec ce VSIX installé. Lorsque vous lancez Visual Studio de cette façon, il démarre avec une ruche de Registre distincte afin que votre utilisation principale de Visual Studio ne soit pas affectée par vos instances de test lors de la génération d’analyseurs. La première fois que vous lancez cette méthode, Visual Studio effectue plusieurs initialisations similaires au moment où vous avez lancé Visual Studio après l’avoir installée.

Créez un projet de console, puis entrez le code de tableau dans la méthode Main de vos applications console :

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);

Les lignes de code avec ImmutableArray des gouilles, car vous devez obtenir le package NuGet immuable et ajouter une using instruction à votre code. Appuyez sur le bouton pointeur droit sur le nœud du projet dans le Explorateur de solutions, puis choisissez Gérer les packages NuGet. Dans le gestionnaire NuGet, tapez « Immuable » dans la zone de recherche, puis choisissez l’élément System.Collections.Immutable (ne choisissez pas Microsoft.Bcl.Immutable) dans le volet gauche, puis appuyez sur le bouton Installer dans le volet droit. L’installation du package ajoute une référence à vos références de projet.

Vous voyez toujours des écouilles rouges sous ImmutableArray, placez le pointeur dans cet identificateur et appuyez sur Ctrl+. (point) pour afficher le menu de correction suggéré et choisir d’ajouter l’instruction appropriée.using

Enregistrez tout et fermez la deuxième instance de Visual Studio pour le moment afin de vous placer dans un état propre pour continuer.

Terminer l’analyseur à l’aide de la modification et continuer

Dans la première instance de Visual Studio, définissez un point d’arrêt au début de votre AnalyzeObjectCreation méthode en appuyant sur F9 avec le pointage sur la première ligne.

Lancez à nouveau votre analyseur avec F5 et, dans la deuxième instance de Visual Studio, rouvrez votre application console que vous avez créée la dernière fois.

Vous revenez à la première instance de Visual Studio au point d’arrêt, car le compilateur Roslyn a vu une expression de création d’objet et appelé dans votre analyseur.

Obtenez le nœud de création d’objet. Parcourez la ligne qui définit la objectCreation variable en appuyant sur F10 et, dans la fenêtre Exécution, évaluez l’expression "objectCreation.ToString()". Vous voyez que le nœud de syntaxe vers lequel la variable pointe est le code "new ImmutableArray<int>()", juste ce que vous recherchez.

Obtenir l’objet ImmutableArray<T> Type. Vous devez case activée si le type créé est ImmutableArray. Tout d’abord, vous obtenez l’objet qui représente ce type. Vous case activée types à l’aide du modèle sémantique pour vous assurer que vous disposez exactement du type approprié et que vous ne comparez pas la chaîne à partir de ToString(). Entrez la ligne de code suivante à la fin de la fonction :

var immutableArrayOfTType =
    context.SemanticModel
           .Compilation
           .GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");

Vous désignez des types génériques dans les métadonnées avec des backticks (') et le nombre de paramètres génériques. C’est pourquoi vous ne voyez pas « ... ImmutableArray<T> » dans le nom des métadonnées.

Le modèle sémantique comporte de nombreuses choses utiles qui vous permettent de poser des questions sur les symboles, le flux de données, la durée de vie des variables, etc. Roslyn sépare les nœuds de syntaxe du modèle sémantique pour différentes raisons d’ingénierie (performances, modélisation de code erroné, etc.). Vous souhaitez que le modèle de compilation recherche des informations contenues dans les références pour une comparaison précise.

Vous pouvez faire glisser le pointeur d’exécution jaune sur le côté gauche de la fenêtre de l’éditeur. Faites-le glisser jusqu’à la ligne qui définit la objectCreation variable et parcourez votre nouvelle ligne de code à l’aide de F10. Si vous pointez le pointeur de la souris sur la variable immutableArrayOfType, vous voyez que nous avons trouvé le type exact dans le modèle sémantique.

Obtenez le type de l’expression de création d’objet. « Type » est utilisé de plusieurs façons dans cet article, mais cela signifie que si vous avez une expression « new Foo », vous devez obtenir un modèle de Foo. Vous devez obtenir le type de l’expression de création d’objet pour voir s’il s’agit du type T> ImmutableArray<. Utilisez à nouveau le modèle sémantique pour obtenir des informations de symbole pour le symbole de type (ImmutableArray) dans l’expression de création d’objet. Entrez la ligne de code suivante à la fin de la fonction :

var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;

Étant donné que votre analyseur doit gérer du code incomplet ou incorrect dans les mémoires tampons de l’éditeur (par exemple, il existe une instruction manquanteusing), vous devez case activée pour symbolInfo être null. Vous devez obtenir un type nommé (INamedTypeSymbol) à partir de l’objet d’informations de symboles pour terminer l’analyse.

Comparez les types. Étant donné qu’il existe un type générique ouvert de T que nous recherchons, et que le type dans le code est un type générique concret, vous interrogez les informations de symbole pour ce que le type est construit à partir (un type générique ouvert) et comparez ce résultat avec immutableArrayOfTType. Entrez les éléments suivants à la fin de la méthode :

if (symbolInfo != null &&
    symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}

Signalez le diagnostic. La création de rapports sur le diagnostic est assez simple. Vous utilisez la règle créée pour vous dans le modèle de projet, qui est défini avant la méthode Initialize. Étant donné que cette situation dans le code est une erreur, vous pouvez modifier la ligne qui a initialisé la règle à remplacer DiagnosticSeverity.Warning (green squiggle) par DiagnosticSeverity.Error (quiggle rouge). Le reste de la règle initialise à partir des ressources que vous avez modifiées près du début de la procédure pas à pas. Vous devez également signaler l’emplacement du bouton bascule, qui est l’emplacement de la spécification de type de l’expression de création d’objet. Entrez ce code dans le if bloc :

context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));

Votre fonction doit ressembler à ceci (peut-être mis en forme différemment) :

private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
    var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
    var immutableArrayOfTType =
        context.SemanticModel
               .Compilation
               .GetTypeByMetadataName(
                   "System.Collections.Immutable.ImmutableArray`1");
    var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as
        INamedTypeSymbol;
    if (symbolInfo != null &&
        symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
    {
        context.ReportDiagnostic(
            Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
    }
}

Supprimez le point d’arrêt pour que vous puissiez voir votre analyseur fonctionner (et arrêter de revenir à la première instance de Visual Studio). Faites glisser le pointeur d’exécution vers le début de votre méthode, puis appuyez sur F5 pour continuer l’exécution. Lorsque vous revenez à la deuxième instance de Visual Studio, le compilateur commence à examiner à nouveau le code, et il appellera votre analyseur. Vous pouvez voir unquiggle sous ImmutableType<int>.

Ajout d’un « correctif de code » pour le problème de code

Avant de commencer, fermez la deuxième instance de Visual Studio et arrêtez le débogage dans la première instance de Visual Studio (où vous développez l’analyseur).

Ajoutez une nouvelle classe. Utilisez le menu contextuel (bouton pointeur droit) sur le nœud de votre projet dans le Explorateur de solutions et choisissez d’ajouter un nouvel élément. Ajoutez une classe appelée BuildCodeFixProvider. Cette classe doit dériver CodeFixProviderde , et vous devez utiliser Ctrl+. (point) pour appeler le correctif de code qui ajoute l’instruction correcte.using Cette classe doit également être annotée avec ExportCodeFixProvider l’attribut, et vous devez ajouter une using instruction pour résoudre l’énumération LanguageNames . Vous devez disposer d’un fichier de classe avec le code suivant :

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;

namespace ImmutableArrayAnalyzer
{
    [ExportCodeFixProvider(LanguageNames.CSharp)]
    class BuildCodeFixProvider : CodeFixProvider
    {}

Stub out membres dérivés. À présent, placez le pointeur de l’éditeur dans l’identificateur CodeFixProvider et appuyez sur Ctrl+. (point) pour extraire l’implémentation de cette classe de base abstraite. Cela génère une propriété et une méthode pour vous.

Implémentez la propriété . Renseignez le corps de get la FixableDiagnosticIds propriété avec le code suivant :

return ImmutableArray.Create(ImmutableArrayAnalyzerAnalyzer.DiagnosticId);

Roslyn rassemble les diagnostics et les correctifs en correspondant à ces identificateurs, qui sont simplement des chaînes. Le modèle de projet a généré un ID de diagnostic pour vous et vous êtes libre de le modifier. Le code de la propriété retourne simplement l’ID de la classe d’analyseur.

La méthode RegisterCodeFixAsync prend un contexte. Le contexte est important, car un correctif de code peut s’appliquer à plusieurs diagnostics, ou il peut y avoir plusieurs problèmes sur une ligne de code. Si vous tapez « context ». Dans le corps de la méthode, la liste de saisie semi-automatique IntelliSense affiche certains membres utiles. Il existe un membre CancellationToken que vous pouvez case activée pour voir si quelque chose souhaite annuler le correctif. Il existe un membre document qui a beaucoup de membres utiles et vous permet d’accéder aux objets de modèle de projet et de solution. Il existe un membre Span qui est le début et la fin de l’emplacement de code spécifié lorsque vous avez signalé le diagnostic.

Faites en sorte que la méthode soit asynchrone. La première chose à faire est de corriger la déclaration de méthode générée pour être une async méthode. Le correctif de code pour le stubbing de l’implémentation d’une classe abstraite n’inclut pas le async mot clé même si la méthode retourne un Task.

Obtenez la racine de l’arborescence de syntaxe. Pour modifier le code, vous devez produire une nouvelle arborescence de syntaxe avec les modifications apportées à votre correctif de code. Vous avez besoin du Document contexte pour appeler GetSyntaxRootAsync. Il s’agit d’une méthode asynchrone, car il existe un travail inconnu pour obtenir l’arborescence de syntaxe, y compris l’obtention du fichier à partir du disque, l’analyse et la création du modèle de code Roslyn pour celui-ci. L’interface utilisateur de Visual Studio doit être réactive pendant cette période, qui utilise async des activations. Remplacez la ligne de code dans la méthode par les éléments suivants :

var root = await context.Document
                        .GetSyntaxRootAsync(context.CancellationToken);

Recherchez le nœud avec le problème. Vous passez l’étendue du contexte, mais le nœud que vous trouvez peut ne pas être le code que vous devez modifier. Le diagnostic signalé fournissait uniquement l’étendue de l’identificateur de type (où l’écuggle appartenait), mais vous devez remplacer l’expression de création d’objet entière, y compris la new mot clé au début et les parenthèses à la fin. Ajoutez le code suivant à votre méthode (et utilisez Ctrl+. pour ajouter une using instruction pour ObjectCreationExpressionSyntax) :

var objectCreation = root.FindNode(context.Span)
                         .FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();

Inscrivez votre correctif de code pour l’interface utilisateur de l’ampoule. Lorsque vous inscrivez votre correctif de code, Roslyn se connecte automatiquement à l’interface utilisateur de l’ampoule Visual Studio. Les utilisateurs finaux voient qu’ils peuvent utiliser Ctrl+. (période) lorsque votre analyseur se gouille un mauvais ImmutableArray<T> constructeur. Étant donné que votre fournisseur de correctif de code s’exécute uniquement en cas de problème, vous pouvez supposer que vous avez l’expression de création d’objet que vous recherchiez. À partir du paramètre de contexte, vous pouvez inscrire le nouveau correctif de code en ajoutant le code suivant à la fin de RegisterCodeFixAsync la méthode :

context.RegisterCodeFix(
            CodeAction.Create("Use ImmutableArray<T>.Empty",
                              c => ChangeToImmutableArrayEmpty(objectCreation,
                                                               context.Document,
                                                               c)),
            context.Diagnostics[0]);

Vous devez placer le pointeur de l’éditeur dans l’identificateur, CodeActionpuis utiliser Ctrl+. (point) pour ajouter l’instruction appropriée using pour ce type.

Placez ensuite le point d’insertion de l’éditeur dans l’identificateur ChangeToImmutableArrayEmpty et utilisez à nouveau Ctrl+ pour générer ce stub de méthode pour vous.

Ce dernier extrait de code que vous avez ajouté enregistre le correctif de code en transmettant un CodeAction ID de diagnostic pour le type de problème trouvé. Dans cet exemple, il n’existe qu’un seul ID de diagnostic pour lequel ce code fournit des correctifs. Vous pouvez donc simplement passer le premier élément du tableau d’ID de diagnostic. Lorsque vous créez le CodeActionfichier , vous transmettez le texte que l’interface utilisateur de l’ampoule doit utiliser comme description du correctif de code. Vous transmettez également une fonction qui prend un CancellationToken et retourne un nouveau document. Le nouveau document a une nouvelle arborescence de syntaxe qui inclut votre code corrigé qui appelle ImmutableArray.Empty. Cet extrait de code utilise une expression lambda afin qu’elle puisse se fermer sur le nœud objectCreation et le document du contexte.

Construisez la nouvelle arborescence de syntaxe. Dans la ChangeToImmutableArrayEmpty méthode dont vous avez généré précédemment le stub, entrez la ligne de code : ImmutableArray<int>.Empty;. Si vous affichez à nouveau la fenêtre de l’outil Visualiseur de syntaxe, vous pouvez voir que cette syntaxe est un nœud SimpleMemberAccessExpression. C’est ce que cette méthode doit construire et retourner dans un nouveau document.

La première modification à ChangeToImmutableArrayEmpty ajouter consiste à ajouter async avant Task<Document> , car les générateurs de code ne peuvent pas supposer que la méthode doit être asynchrone.

Renseignez le corps avec le code suivant pour que votre méthode ressemble à ce qui suit :

private async Task<Document> ChangeToImmutableArrayEmpty(
    ObjectCreationExpressionSyntax objectCreation, Document document,
    CancellationToken c)
{
    var generator = SyntaxGenerator.GetGenerator(document);
    var memberAccess =
        generator.MemberAccessExpression(objectCreation.Type, "Empty");
    var oldRoot = await document.GetSyntaxRootAsync(c);
    var newRoot = oldRoot.ReplaceNode(objectCreation, memberAccess);
    return document.WithSyntaxRoot(newRoot);
}

Vous devez placer le pointeur de l’éditeur dans l’identificateur SyntaxGenerator et utiliser Ctrl+. (point) pour ajouter l’instruction appropriée using pour ce type.

Ce code utilise SyntaxGenerator, qui est un type utile pour construire un nouveau code. Après avoir obtenu un générateur pour le document qui a le problème de code, appelle MemberAccessExpression, ChangeToImmutableArrayEmpty en passant le type qui a le membre auquel nous voulons accéder et en passant le nom du membre en tant que chaîne.

Ensuite, la méthode extrait la racine du document et, étant donné que cela peut impliquer un travail arbitraire dans le cas général, le code attend cet appel et transmet le jeton d’annulation. Les modèles de code Roslyn sont immuables, comme l’utilisation d’une chaîne .NET ; lorsque vous mettez à jour la chaîne, vous obtenez un nouvel objet de chaîne en retour. Lorsque vous appelez, vous récupérez ReplaceNodeun nouveau nœud racine. La plupart de l’arborescence de syntaxe est partagée (car elle est immuable), mais le objectCreation nœud est remplacé par le memberAccess nœud, ainsi que tous les nœuds parents jusqu’à la racine de l’arborescence de syntaxe.

Essayer votre correctif de code

Vous pouvez maintenant appuyer sur F5 pour exécuter votre analyseur dans une deuxième instance de Visual Studio. Ouvrez le projet de console que vous avez utilisé précédemment. Vous devez maintenant voir l’ampoule apparaître où votre nouvelle expression de création d’objet est pour ImmutableArray<int>. Si vous appuyez sur Ctrl+. (période), vous verrez votre correctif de code et vous verrez un aperçu de différence de code généré automatiquement dans l’interface utilisateur de l’ampoule. Roslyn crée cela pour vous.

Conseil pro : si vous lancez la deuxième instance de Visual Studio et que vous ne voyez pas l’ampoule avec votre correctif de code, vous devrez peut-être effacer le cache du composant Visual Studio. L’effacement du cache force Visual Studio à réinscrire les composants. Visual Studio doit donc récupérer votre dernier composant. Tout d’abord, arrêtez la deuxième instance de Visual Studio. Ensuite, dans l’Explorateur Windows, accédez à %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (La version 16.0 passe de la version à la version avec Visual Studio.) Supprimez le sous-répertoire ComponentModelCache.

Parler vidéo et terminer le projet de code

Vous pouvez voir tout le code terminé ici. Les sous-dossiers DoNotUseImmutableArrayCollectionInitializer et DoNotUseImmutableArrayCtor ont chacun un fichier C# pour rechercher des problèmes et un fichier C# qui implémente les correctifs de code qui apparaissent dans l’interface utilisateur de l’ampoule Visual Studio. Notez que le code terminé a un peu plus d’abstraction pour éviter d’extraire l’objet de type ImmutableArray<T> au-dessus et à plusieurs. Il utilise des actions inscrites imbriquées pour enregistrer l’objet de type dans un contexte disponible chaque fois que les sous-actions (analyser la création d’objet et analyser les initialisations de collection) s’exécutent.