Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La plataforma de compiladores .NET ("Roslyn") le ayuda a crear bibliotecas conscientes del código. Una biblioteca compatible con código proporciona funcionalidad que puede usar y herramientas (analizadores de Roslyn) para ayudarle a usar la biblioteca de la mejor manera o para evitar errores. En este tema se muestra cómo crear un analizador de Roslyn real para detectar errores comunes al usar el paquete NuGet System.Collections.Immutable. En el ejemplo también se muestra cómo proporcionar una corrección de código para un problema de código encontrado por el analizador. Los usuarios ven correcciones de código en la interfaz de usuario de bombilla de Visual Studio y pueden aplicar una corrección para el código automáticamente.
Comenzar
Necesita lo siguiente para compilar este ejemplo:
- Visual Studio 2015 (no una edición Express) o una versión posterior. Puede usar la versión gratuita de la edición Visual Studio Community.
- SDK de Visual Studio. También puede, al instalar Visual Studio, marcar Herramientas de extensibilidad de Visual Studio en Herramientas comunes para instalar el SDK al mismo tiempo. Si ya ha instalado Visual Studio, también puede instalar este SDK, para ello, vaya al menú principal Archivo>Nuevo>Proyecto, elija C# en el panel de navegación izquierdo y, después, elija Extensibilidad. Al elegir la plantilla de proyecto de ruta de navegación "Instalar las herramientas de extensibilidad de Visual Studio", le pedirá que descargue e instale el SDK.
- SDK de .NET Compiler Platform ("Roslyn"). También puede instalar este SDK; para ello, vaya al menú principal Archivo>Nuevo>Proyecto, elija C# en el panel de navegación izquierdo y, después, elija Extensibilidad. Al elegir la plantilla de proyecto de breadcrumb "Descargar el SDK de .NET Compiler Platform", se le pedirá que descargue e instale el SDK. Este SDK incluye el visualizador de sintaxis de Roslyn . Esta herramienta útil le ayuda a averiguar qué tipos de modelo de código debe buscar en el analizador. La infraestructura del analizador llama al código para tipos de modelo de código específicos, por lo que el código solo se ejecuta cuando es necesario y solo puede centrarse en analizar el código pertinente.
¿Cuál es el problema?
Imagínate que ofreces una biblioteca compatible con ImmutableArray (por ejemplo, System.Collections.Immutable.ImmutableArray<T>). Los desarrolladores de C# tienen mucha experiencia con las matrices de .NET. Sin embargo, debido a la naturaleza de los ImmutableArrays y las técnicas de optimización usadas en la implementación, la intuición que tienen los desarrolladores de C# hace que los usuarios de tu biblioteca escriban código defectuoso, como se explica a continuación. Además, los usuarios no ven sus errores hasta el tiempo de ejecución, lo cual no es la experiencia de calidad a la que están acostumbrados en Visual Studio con .NET.
Los usuarios están familiarizados con la escritura de código como el siguiente:
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 creación de matrices vacías para completar con líneas de código posteriores y el uso de la sintaxis del inicializador de colección resultan familiares para los desarrolladores de C#. Sin embargo, escribir el mismo código para un ImmutableArray se bloquea en tiempo de ejecución.
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);
El primer error se debe a que la implementación de ImmutableArray usa una estructura para encapsular el almacenamiento de datos subyacente. Las estructuras deben tener constructores sin parámetros para que las expresiones default(T) puedan devolver estructuras con todos los miembros en cero o null. Cuando el código accede a b1.Length, hay un error de desreferenciación null en tiempo de ejecución porque no hay ninguna matriz de almacenamiento subyacente en la estructura ImmutableArray. La manera correcta de crear un ImmutableArray vacío es ImmutableArray<int>.Empty.
El error con inicializadores de colección se produce porque el método ImmutableArray.Add devuelve nuevas instancias cada vez que se llama. Dado que ImmutableArrays nunca cambia, cuando se agrega un nuevo elemento, se devuelve un nuevo objeto ImmutableArray (que puede compartir almacenamiento por motivos de rendimiento con una immutableArray existente previamente). Dado que b2 apunta a la primera ImmutableArray antes de llamar a Add() cinco veces, b2 es una ImmutableArray predeterminada. Al llamar a Length también se produce un error de desreferencia nula. La manera correcta de inicializar un ImmutableArray sin tener que llamar manualmente a Add es usar ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).
Búsqueda de tipos de nodo de sintaxis relevantes para desencadenar el analizador
Para empezar a compilar el analizador, primero averigüe qué tipo de nodo de sintaxis necesita buscarlo. Inicie el Visualizador de sintaxis desde el menú Ver>Otras ventanas>Visualizador de sintaxis de Roslyn.
Coloque el cursor del editor en la línea que declara b1. Verá que el visualizador de sintaxis muestra que está en un nodo LocalDeclarationStatement del árbol de sintaxis. Este nodo tiene un VariableDeclaration, que a su vez tiene un VariableDeclarator, que a su vez tiene un EqualsValueClausey, por último, hay un ObjectCreationExpression. Al hacer clic en el árbol del visualizador de sintaxis de nodos, la sintaxis de la ventana del editor resalta para mostrar el código representado por ese nodo. Los nombres de los subtipos SyntaxNode coinciden con los nombres usados en la gramática de C#.
Creación del proyecto del analizador
En el menú principal, elija Archivo>Nuevo>Proyecto. En el cuadro de diálogo Nuevo Proyecto, en la barra de navegación izquierda, elija Proyectos de C#, luego seleccione Extensibilidady, en el panel derecho, elija la plantilla de proyecto Analizador con Corrección de Código. Escriba un nombre y confirme el cuadro de diálogo.
La plantilla abre un archivo DiagnosticAnalyzer.cs. Elija esa pestaña del búfer del editor. Este archivo tiene una clase de analizador (formada a partir del nombre que proporcionó el proyecto) que deriva de DiagnosticAnalyzer (un tipo de API de Roslyn). La nueva clase tiene un DiagnosticAnalyzerAttribute que declara que el analizador es relevante para el lenguaje C#, de modo que el compilador lo detecte y lo cargue.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzer : DiagnosticAnalyzer
{}
Puede implementar un analizador mediante Visual Basic que tenga como destino código de C# y viceversa. Es más importante en DiagnosticAnalyzerAttribute elegir si el analizador tiene como destino un idioma o ambos. Los analizadores más sofisticados que requieren modelado detallado del lenguaje solo pueden tener como destino un único lenguaje. Si el analizador, por ejemplo, solo comprueba los nombres de tipo o los nombres de miembros públicos, puede ser posible usar el modelo de lenguaje común que Roslyn ofrece en Visual Basic y C#. Por ejemplo, FxCop advierte de que una clase implementa ISerializable, pero la clase no tiene el atributo SerializableAttribute es independiente del lenguaje y funciona para código de Visual Basic y C#.
Inicialización del analizador
Desplácese hacia abajo un poco en la clase DiagnosticAnalyzer para ver el método Initialize. El compilador llama a este método al activar un analizador. El método toma un objeto AnalysisContext que permite a su analizador obtener información de contexto y registrar devoluciones de llamada para eventos correspondientes a los tipos de código que desea analizar.
public override void Initialize(AnalysisContext context) {}
Abra una nueva línea en este método y escriba "context" para ver una lista de finalización de IntelliSense. Puede ver que en la lista de finalización hay muchos métodos Register... para controlar varios tipos de eventos. Por ejemplo, la primera, RegisterCodeBlockAction, hace referencia a un bloque de código, que suele ser código entre llaves. El registro de un bloque también solicita al código el inicializador de un campo, el valor dado a un atributo o el valor de un parámetro opcional.
Como otro ejemplo, RegisterCompilationStartActionrealiza una llamada de retorno a tu código al principio de una compilación, lo que resulta útil cuando necesitas recopilar el estado en muchos lugares. Puede crear una estructura de datos, por ejemplo, para recopilar todos los símbolos usados y cada vez que se llame al analizador para alguna sintaxis o símbolo, puede guardar información sobre cada ubicación de la estructura de datos. Cuando se le solicita nuevamente después de que termine la compilación, puede examinar todas las ubicaciones que ha guardado, por ejemplo, para informar sobre qué símbolos utiliza el código de cada declaración using.
Usando Syntax Visualizer, desea ser notificado cuando el compilador procesa una expresión de creación de objetos. Use este código para configurar la devolución de llamada:
context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
SyntaxKind.ObjectCreationExpression);
Se registra un nodo de sintaxis y se filtran solo los nodos de sintaxis de creación de objetos. Por convención, los autores del analizador usan una expresión lambda al registrar acciones, lo que ayuda a mantener los analizadores sin estado. Puede usar la característica de Visual Studio Generar a partir del uso para crear el método AnalyzeObjectCreation. Esto también genera el tipo correcto de parámetro de contexto.
Establecimiento de propiedades para los usuarios del analizador
Para que el analizador aparezca correctamente en la interfaz de usuario de Visual Studio, busque y modifique la siguiente línea de código para identificar el analizador:
internal const string Category = "Naming";
Cambie "Naming" a "API Guidance".
A continuación, busque y abra el archivo Resources.resx mediante el explorador de soluciones en su proyecto. Puede incluir una descripción para su analizador, el título, etc. Puede cambiar el valor de todos ellos a "Don't use ImmutableArray<T> constructor" por ahora. Puede colocar argumentos de formato de cadena en su cadena ({0}, {1}, etc.), y luego, cuando llame a Diagnostic.Create(), puede proporcionar una matriz de argumentos params que se va a pasar.
Análisis de una expresión de creación de objetos
El método AnalyzeObjectCreation toma un tipo de contexto diferente proporcionado por el marco del analizador de código. El método Initialize, a través de su AnalysisContext, permite registrar devoluciones de llamadas de acción para configurar el analizador. El SyntaxNodeAnalysisContext, por ejemplo, tiene un CancellationToken que puede pasar. Si un usuario empieza a escribir en el editor, Roslyn cancelará la ejecución de analizadores para guardar el trabajo y mejorar el rendimiento. Como otro ejemplo, este contexto tiene una propiedad Node que devuelve el nodo de sintaxis de creación de objetos.
Obtenga el nodo, que puede suponer que es del tipo para el cual filtró la acción del nodo de sintaxis.
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
Inicie Visual Studio con el analizador la primera vez.
Inicie Visual Studio mediante la compilación y ejecución del analizador (presione F5). Dado que el proyecto de inicio en el Solution Explorer de es el proyecto VSIX, al ejecutar el código se compila el código y un VSIX. A continuación, se inicia Visual Studio con ese VSIX instalado. Al iniciar Visual Studio de esta manera, se inicia con un subárbol del registro distinto para que el uso principal de Visual Studio no se vea afectado por las instancias de prueba al compilar analizadores. La primera vez que se inicia de esta manera, Visual Studio realiza varias inicializaciones similares a cuando se inicia Visual Studio por primera vez después de instalarlo.
Cree un proyecto de consola y escriba el código de matriz en el método Main de las aplicaciones de consola:
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);
Las líneas de código con ImmutableArray tienen un subrayado ondulado porque necesita descargar el paquete NuGet inmutable y agregar una instrucción using al código. Presione el botón derecho del ratón sobre el nodo del proyecto en el Explorador de soluciones de y elija Administrar paquetes NuGet . En el administrador de NuGet, escriba "Inmutable" en el cuadro de búsqueda y elija el elemento System.Collections.Immutable (no elija Microsoft.Bcl.Immutable) en el panel izquierdo y presione el botón Instalar en el panel derecho. Al instalar el paquete se agrega una referencia a las referencias del proyecto.
Todavía ve líneas de subrayado rojas en ImmutableArray, así que coloque el cursor en ese identificador y presione Ctrl+. (punto) para abrir el menú de corrección sugerida y seleccionar agregar la instrucción using adecuada.
Guarda todo y Cierra la segunda instancia de Visual Studio por el momento para dejarlo en un estado limpio y poder continuar.
Finalizar el analizador mediante editar y continuar
En la primera instancia de Visual Studio, establezca un punto de interrupción al principio del método AnalyzeObjectCreation presionando F9 con el cursor en la primera línea.
Vuelva a iniciar el analizador con F5y, en la segunda instancia de Visual Studio, vuelva a abrir la aplicación de consola que creó la última vez.
Vuelva a la primera instancia de Visual Studio en el punto de interrupción porque el compilador de Roslyn vio una expresión de creación de objetos y llamó al analizador.
Obtenga el nodo de creación de objetos. Recorra paso a paso por la línea que establece la variable de objectCreation presionando F10y, en la ventana inmediata , evalúe la expresión "objectCreation.ToString()". Verá que el nodo de sintaxis al que apunta la variable es el código "new ImmutableArray<int>()", solo lo que está buscando.
Obtenga el objeto ImmutableArray<T> Type. Debe comprobar si el tipo que se va a crear es ImmutableArray. En primer lugar, obtendrá el objeto que representa este tipo. Compruebe los tipos mediante el modelo semántico para asegurarse de que tiene exactamente el tipo correcto y no compara la cadena de ToString(). Escriba la siguiente línea de código al final de la función:
var immutableArrayOfTType =
context.SemanticModel
.Compilation
.GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");
Los tipos genéricos se designan en metadatos con acentos inversos (') y el número de parámetros genéricos. Por eso no ves "... ImmutableArray<T>" en el nombre de los metadatos.
El modelo semántico tiene muchas cosas útiles que le permiten formular preguntas sobre símbolos, flujo de datos, duración variable, etc. Roslyn separa los nodos de sintaxis del modelo semántico por diversos motivos de ingeniería (rendimiento, modelado de código erróneo, etc.). Quiere que el modelo de compilación busque información contenida en referencias para obtener una comparación precisa.
Puede arrastrar el puntero de ejecución amarillo en el lado izquierdo de la ventana del editor. Arrástrelo hasta la línea que establece la variable objectCreation y recorra la nueva línea de código mediante F10. Si mantiene el puntero del mouse sobre la variable immutableArrayOfType, verá que encontramos el tipo exacto en el modelo semántico.
Obtenga el tipo de la expresión de creación del objeto. "Type" se usa de varias maneras en este artículo, pero esto significa que si tiene una expresión "foo nueva", debe obtener un modelo de Foo. Debe obtener el tipo de la expresión de creación de objetos para ver si es del tipo ImmutableArray<T>. Use de nuevo el modelo semántico para obtener información de símbolos del símbolo de tipo (ImmutableArray) en la expresión de creación de objetos. Escriba la siguiente línea de código al final de la función:
var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;
Dado que el analizador debe controlar el código incompleto o incorrecto en los búferes del editor (por ejemplo, falta una instrucción using), debe comprobar si symbolInfo está siendo null. Debe obtener un tipo con nombre (INamedTypeSymbol) del objeto de información de símbolos para finalizar el análisis.
Compare los tipos. Dado que hay un tipo genérico abierto de T que estamos buscando y el tipo en el código es un tipo genérico concreto, se consulta la información de símbolos para lo que el tipo se construye a partir de (un tipo genérico abierto) y compara ese resultado con immutableArrayOfTType. Escriba lo siguiente al final del método :
if (symbolInfo != null &&
symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}
Informe del diagnóstico. Notificar el diagnóstico es bastante fácil. Use la regla creada automáticamente en la plantilla de proyecto, que se define antes del método Initialize. Dado que esta situación en el código es un error, puede cambiar la línea que inicializa Regla para reemplazar DiagnosticSeverity.Warning (subrayado ondulado verde) por DiagnosticSeverity.Error (subrayado ondulado rojo). El resto de la regla se inicializa desde los recursos que editó cerca del principio del tutorial. También necesita reportar la ubicación del subrayado rojo, que es donde se especifica el tipo de la expresión de creación de objetos. Escriba este código en el bloque if:
context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
La función debe tener un aspecto similar al siguiente (quizás formateado de forma diferente):
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()));
}
}
Quite el punto de interrupción para que pueda ver que el analizador funciona (y deje de volver a la primera instancia de Visual Studio). Arrastre el puntero de ejecución al principio del método y presione F5 para continuar con la ejecución. Al volver a la segunda instancia de Visual Studio, el compilador comenzará a examinar el código de nuevo y llamará al analizador. Puede ver un subrayado rojo debajo de ImmutableType<int>.
Agregar una "corrección de código" para el problema de código
Antes de comenzar, cierra la segunda instancia de Visual Studio y detenga la depuración en la primera instancia de Visual Studio (donde está desarrollando el analizador).
Agregue una nueva clase. Use el menú contextual (botón derecho del ratón) en el nodo del proyecto en el Explorador de soluciones y elija agregar un nuevo elemento. Agregue una clase denominada BuildCodeFixProvider. Esta clase debe derivar de CodeFixProvider, y deberá usar Ctrl+. (punto) para invocar la corrección de código que agrega la instrucción correcta using. Esta clase también debe anotarse con el atributo ExportCodeFixProvider, y deberá agregar una sentencia using para resolver la enumeración LanguageNames. Debe tener un archivo de clase con el código siguiente en él:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
namespace ImmutableArrayAnalyzer
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
class BuildCodeFixProvider : CodeFixProvider
{}
Esquema los miembros derivados. Ahora, coloque el símbolo de intercalación del editor en el identificador CodeFixProvider y presione Ctrl+. (punto) para extraer el código auxiliar de la implementación de esta clase base abstracta. Esto genera una propiedad y un método para ti.
Implementar la propiedad. Rellene el cuerpo FixableDiagnosticIds de la propiedad get con el código siguiente:
return ImmutableArray.Create(ImmutableArrayAnalyzer.DiagnosticId);
Roslyn reúne diagnósticos y correcciones mediante la coincidencia de estos identificadores, que son solo cadenas. La plantilla de proyecto generó un identificador de diagnóstico para usted, y puede cambiarlo. El código en la propiedad simplemente devuelve el identificador de la clase de analizador.
El método RegisterCodeFixAsync toma un contexto. El contexto es importante porque una corrección de código se puede aplicar a varios diagnósticos o podría haber más de un problema en una línea de código. Si escribe "context." en el cuerpo del método, la lista de finalización de IntelliSense le mostrará algunos miembros útiles. Hay un miembro CancellationToken que puede comprobar si algo quiere cancelar la corrección. Hay un miembro Document que posee muchos miembros útiles y le permite acceder a los objetos del modelo de proyecto y solución. Hay un miembro Span que marca el inicio y el final de la ubicación de código especificada cuando notificó el diagnóstico.
Haga que el método sea asincrónico. Lo primero que debe hacer es corregir la declaración del método generado para que sea un método async. La corrección de código para sustituir provisionalmente la implementación de una clase abstracta no incluye la palabra clave async aunque el método devuelva un Task.
Obtenga la raíz del árbol de sintaxis. Para modificar el código, debe generar un nuevo árbol de sintaxis con los cambios que realiza la corrección de código. Necesita el Document del contexto para llamar a GetSyntaxRootAsync. Este es un método asincrónico porque hay un trabajo desconocido para obtener el árbol de sintaxis, posiblemente, obtener el archivo del disco, analizarlo y compilar el modelo de código de Roslyn para él. La interfaz de usuario de Visual Studio debe responder durante este tiempo, lo que permite el uso de async. Reemplace la línea de código del método por lo siguiente:
var root = await context.Document
.GetSyntaxRootAsync(context.CancellationToken);
Busque el nodo con el problema. Pase el intervalo del contexto, pero es posible que el nodo que encuentre no sea el código que tiene que cambiar. El diagnóstico informado solo proporcionó el rango del identificador de tipo (donde pertenecía el subrayado rojo), pero debe reemplazar toda la expresión de creación de objetos, incluida la palabra clave new al principio y los paréntesis al final. Agregue el código siguiente al método (y use Ctrl+. para agregar una instrucción using para ObjectCreationExpressionSyntax):
var objectCreation = root.FindNode(context.Span)
.FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();
Registra la corrección de errores en el código para la interfaz de la bombilla. Al registrar la corrección de código, Roslyn se conecta automáticamente a la interfaz de usuario de bombilla de Visual Studio. Los usuarios finales verán que pueden usar Ctrl+. (punto) cuando el analizador activa un constructor de ImmutableArray<T> incorrecto. Dado que el proveedor de corrección de código solo se ejecuta cuando hay un problema, puede suponer que tiene la expresión de creación de objetos que estaba buscando. Desde el parámetro de contexto, puede registrar la nueva corrección de código agregando el código siguiente al final del método RegisterCodeFixAsync:
context.RegisterCodeFix(
CodeAction.Create("Use ImmutableArray<T>.Empty",
c => ChangeToImmutableArrayEmpty(objectCreation,
context.Document,
c)),
context.Diagnostics[0]);
Debe colocar el cursor del editor en el identificador, CodeAction, y luego usar Ctrl+. (punto) para agregar la instrucción using adecuada para este tipo.
A continuación, coloque el cursor del editor en el identificador de ChangeToImmutableArrayEmpty y use Ctrl+. de nuevo para generar el esqueleto del método.
Este último fragmento de código que agregó registra la corrección de código pasando un CodeAction y el identificador de diagnóstico para el tipo de problema encontrado. En este ejemplo, solo hay un identificador de diagnóstico para el que este código proporciona correcciones, por lo que solo puede pasar el primer elemento de la matriz de identificadores de diagnóstico. Al crear el CodeAction, se pasa el texto que la interfaz de usuario de bombilla debe usar como descripción de la corrección de código. También se pasa una función que toma un CancellationToken y devuelve un nuevo Documento. El nuevo documento tiene un nuevo árbol de sintaxis que incluye el código revisado que llama a ImmutableArray.Empty. Este fragmento de código usa una expresión lambda para que pueda cerrarse sobre el nodo objectCreation y el documento del contexto.
Construya el nuevo árbol de sintaxis. En el método ChangeToImmutableArrayEmpty cuyo stub generó anteriormente, escriba la línea de código: ImmutableArray<int>.Empty;. Si vuelve a ver la ventana de herramienta de Syntax Visualizer, puede ver que esta sintaxis es un nodo SimpleMemberAccessExpression. Eso es lo que este método necesita para construir y devolver en un nuevo Documento.
El primer cambio a ChangeToImmutableArrayEmpty consiste en agregar async antes de Task<Document> porque los generadores de código no pueden suponer que el método debe ser asincrónico.
Rellene el cuerpo con el código siguiente para que el método tenga un aspecto similar al siguiente:
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);
}
Deberá colocar el cursor del editor en el identificador de SyntaxGenerator y usar Ctrl+. (punto) para agregar la instrucción using adecuada para este tipo.
Este código usa SyntaxGenerator, que es un tipo útil para construir código nuevo. Después de obtener un generador para el documento que tiene el problema de código, ChangeToImmutableArrayEmpty llama a MemberAccessExpression, pasando el tipo que tiene el miembro al que queremos acceder y pasando el nombre del miembro como cadena.
A continuación, el método captura la raíz del documento y, dado que esto puede implicar trabajo arbitrario en el caso general, el código espera esta llamada y pasa el token de cancelación. Los modelos de código roslyn son inmutables, como trabajar con una cadena de .NET; cuando se actualiza la cadena, se obtiene un nuevo objeto de cadena a cambio. Al llamar a ReplaceNode, se devuelve un nuevo nodo raíz. La mayoría del árbol de sintaxis se comparte (porque es inmutable), pero el nodo objectCreation se reemplaza por el nodo memberAccess, así como todos los nodos primarios hasta la raíz del árbol de sintaxis.
Prueba tu corrección de código
Ahora puede presionar F5 para ejecutar el analizador en una segunda instancia de Visual Studio. Abra el proyecto de consola que usó antes. Ahora debería ver aparecer la bombilla donde está su nueva expresión de creación de objetos para ImmutableArray<int>. Si presiona Ctrl+. (punto), verá la corrección de código y verá una vista previa de diferencias de código generada automáticamente en la interfaz de usuario de bombilla. Roslyn crea esto para ti.
Consejo profesional: Si inicia la segunda instancia de Visual Studio y no ve la bombilla con la corrección de código, puede que necesite borrar la caché de componentes de Visual Studio. Al borrar la memoria caché, Visual Studio hace que se reexaminen los componentes, por lo que debería detectar el componente más reciente. En primer lugar, apague la segunda instancia de Visual Studio. A continuación, en Explorador de Windows, vaya a %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\. (La versión "16.0" cambia de la versión a la versión con Visual Studio). Elimine el subdirectorio ComponentModelCache.
Vídeo de conversación y finalización del proyecto de código
Puede ver todo el código terminado aquí. Las subcarpetas DoNotUseImmutableArrayCollectionInitializer y DoNotUseImmutableArrayCtor cada uno tiene un archivo C# para buscar problemas y un archivo de C# que implementa las correcciones de código que se muestran en la interfaz de usuario de la bombilla de Visual Studio. Tenga en cuenta que el código terminado tiene un poco más de abstracción para evitar obtener el objeto de tipo ImmutableArray<T> una y otra vez. Usa acciones registradas anidadas para guardar el objeto de tipo en un contexto que está disponible siempre que se ejecuten las subacciones (analizar la creación de objetos y analizar inicializaciones de colección).