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.
En este tutorial se da por supuesto que está familiarizado con la API de sintaxis. El artículo introducción al análisis de sintaxis proporciona una introducción suficiente.
En este tutorial, explorará la API de Símbolo y Binding. Estas API proporcionan información sobre el significado semántico de un programa. Le permiten formular y responder preguntas sobre los tipos representados por cualquier símbolo del programa.
Deberá instalar el SDK de .NET Compiler Platform:
Instrucciones de instalación: Instalador de Visual Studio
Hay dos maneras diferentes de encontrar el SDK de .NET Compiler Platform en el Instalador de Visual Studio:
Instalación mediante el Instalador de Visual Studio: vista Cargas de trabajo
El SDK de .NET Compiler Platform no se selecciona automáticamente como parte de la carga de trabajo de desarrollo de extensiones de Visual Studio. Deba seleccionarlo como un componente opcional.
- Ejecución del instalador de Visual Studio
- Seleccione Modificar.
- Active la carga de trabajo Desarrollo de extensiones de Visual Studio.
- Abra el nodo desarrollo de extensiones de Visual Studio en el árbol de resumen.
- Asegúrese de que la casilla del SDK de la plataforma del compilador de .NET esté activada.
- Seleccione Modificar.
Opcionalmente, también querrá que el editor DGML muestre gráficos en el visualizador:
- Abra el nodo Componentes individuales en el árbol de resumen.
- Active la casilla para el editor DGML.
Instalación mediante la pestaña Instalador de Visual Studio: componentes individuales
- Ejecución del instalador de Visual Studio
- Seleccione Modificar.
- Seleccione la pestaña Componentes individuales.
- Marque la casilla para .NET Compiler Platform SDK. Lo encontrará en la parte superior de la sección Compiladores, herramientas de compilación y entornos de ejecución .
- Seleccione Modificar.
Opcionalmente, también querrá que el editor DGML muestre gráficos en el visualizador:
- Active la casilla Editor de DGML. La encontrará en la sección Herramientas de código .
Comprensión de compilaciones y símbolos
A medida que trabaja más con el SDK del compilador de .NET, se familiariza con las diferencias entre la API de sintaxis y la API semántica. La API de sintaxis permite examinar la estructura de un programa. Sin embargo, a menudo desea obtener información más completa sobre la semántica o el significado de un programa. Aunque un archivo de código flexible o un fragmento de código de Visual Basic o C# se puede analizar sintácticamente de forma aislada, no es significativo formular preguntas como "cuál es el tipo de esta variable" en un vacío. El significado de un nombre de tipo puede depender de referencias de ensamblado, importaciones de espacio de nombres u otros archivos de código. Estas preguntas se responden mediante la API semántica, específicamente la Microsoft.CodeAnalysis.Compilation clase .
Una instancia de Compilation es análoga a un único proyecto tal como lo ve el compilador y representa todo lo necesario para compilar un programa de Visual Basic o C#. La compilación incluye el conjunto de archivos de origen que se van a compilar, las referencias de ensamblado y las opciones del compilador. Puede razonar sobre el significado del código mediante todas las demás informaciones de este contexto. Un Compilation permite buscar símbolos : entidades como tipos, espacios de nombres, miembros y variables a las que hacen referencia los nombres y otras expresiones. El proceso de asociar nombres y expresiones con símbolos se denomina Vinculación.
Al igual que Microsoft.CodeAnalysis.SyntaxTree, Compilation es una clase abstracta con derivados específicos del lenguaje. Al crear una instancia de Compilación, debe invocar un método de fábrica en la Microsoft.CodeAnalysis.CSharp.CSharpCompilation clase (o Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation).
Consulta de símbolos
En este tutorial, verá de nuevo el programa "Hola mundo". Esta vez, se consultan los símbolos del programa para comprender qué tipos representan esos símbolos. Puede consultar los tipos de un espacio de nombres y aprender a buscar los métodos disponibles en un tipo.
Puede ver el código terminado de este ejemplo en nuestro repositorio de GitHub.
Nota:
Los tipos de árbol de sintaxis usan herencia para describir los distintos elementos de sintaxis que son válidos en diferentes ubicaciones del programa. El uso de estas API suele significar convertir propiedades o miembros de colección en tipos derivados específicos. En los ejemplos siguientes, la asignación y las conversiones son instrucciones independientes, usando variables con tipo explícito. Puede leer el código para ver los tipos de devolución de la API y el tipo en tiempo de ejecución de los objetos devueltos. En la práctica, es más común usar variables con tipo implícito y confiar en nombres de API para describir el tipo de objetos que se examinan.
Cree un nuevo proyecto de C# para una herramienta de análisis de código independiente.
- En Visual Studio, elija Archivo>nuevo>proyecto para mostrar el cuadro de diálogo Nuevo proyecto.
- En Visual C#>Extensibilidad, seleccione Herramienta autónoma de análisis de código.
- Asigne al proyecto el nombre "SemanticQuickStart" y haga clic en Aceptar.
Va a analizar el programa básico "Hola mundo!" que se mostró anteriormente.
Agregue el texto del programa Hello World como una constante en su Program clase:
const string programText =
@"using System;
using System.Collections.Generic;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
A continuación, agregue el código siguiente para compilar el árbol de sintaxis para el texto del código en la programText constante. Agregue la siguiente línea al Main método :
SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
A continuación, compile un CSharpCompilation elemento a partir del árbol que ya ha creado. El ejemplo "Hello World" se basa en los tipos String y Console. Debe hacer referencia al ensamblaje que declara esos dos tipos en su compilación. Agregue la siguiente línea al Main método para crear una compilación del árbol de sintaxis, incluida la referencia al ensamblado adecuado:
var compilation = CSharpCompilation.Create("HelloWorld")
.AddReferences(MetadataReference.CreateFromFile(
typeof(string).Assembly.Location))
.AddSyntaxTrees(tree);
El CSharpCompilation.AddReferences método agrega referencias a la compilación. El MetadataReference.CreateFromFile método carga un ensamblado como referencia.
Consulta del modelo semántico
Una vez que tenga un Compilation, puede solicitar un SemanticModel para cualquier SyntaxTree contenido en ese Compilation. Puede considerar el modelo semántico como origen de toda la información que normalmente obtendría de IntelliSense. Puede SemanticModel responder a preguntas como "¿Qué nombres están en el ámbito en esta ubicación?", "¿Qué miembros son accesibles desde este método?", "¿Qué variables se usan en este bloque de texto?" y "¿Qué identifica este nombre/expresión?". Agregue esta declaración para establecer el modelo semántico.
SemanticModel model = compilation.GetSemanticModel(tree);
Enlazar un nombre
Compilation crea SemanticModel a partir de SyntaxTree. Después de crear el modelo, puede consultarlo para buscar la primera using directiva y recuperar la información de símbolos del System espacio de nombres. Agregue estas dos líneas al Main método para crear el modelo semántico y recuperar el símbolo de la primera using directiva:
// Use the syntax tree to find "using System;"
UsingDirectiveSyntax usingSystem = root.Usings[0];
NameSyntax systemName = usingSystem.Name;
// Use the semantic model for symbol information:
SymbolInfo nameInfo = model.GetSymbolInfo(systemName);
El código anterior muestra cómo enlazar el nombre en la primera using directiva para obtener un Microsoft.CodeAnalysis.SymbolInfo para el espacio de nombres System. El código anterior también muestra que se usa el modelo de sintaxis para buscar la estructura del código; se usa el modelo semántico para comprender su significado. El modelo de sintaxis busca la cadena System en la using directiva . El modelo semántico tiene toda la información sobre los tipos definidos en el System espacio de nombres.
Desde el SymbolInfo objeto se puede obtener Microsoft.CodeAnalysis.ISymbol mediante la SymbolInfo.Symbol propiedad . Esta propiedad devuelve el símbolo al que hace referencia esta expresión. Para las expresiones que no hacen referencia a nada (como literales numéricos), esta propiedad es null. Cuando SymbolInfo.Symbol no es nulo, ISymbol.Kind denota el tipo del símbolo. En este ejemplo, la propiedad ISymbol.Kind es un SymbolKind.Namespace. Agregue el código siguiente al método Main. Recupera el símbolo del System espacio de nombres y, a continuación, muestra todos los espacios de nombres secundarios declarados en el System espacio de nombres:
var systemSymbol = (INamespaceSymbol?)nameInfo.Symbol;
if (systemSymbol?.GetNamespaceMembers() is not null)
{
foreach (INamespaceSymbol ns in systemSymbol?.GetNamespaceMembers()!)
{
Console.WriteLine(ns);
}
}
Ejecute el programa y debería ver la siguiente salida:
System.Collections
System.Configuration
System.Deployment
System.Diagnostics
System.Globalization
System.IO
System.Numerics
System.Reflection
System.Resources
System.Runtime
System.Security
System.StubHelpers
System.Text
System.Threading
Press any key to continue . . .
Nota:
La salida no incluye todos los espacios de nombres que son secundarios del espacio de nombres System. Muestra todos los espacios de nombres que están presentes en esta compilación, que solo hace referencia al ensamblado donde System.String se declara. Los espacios de nombres declarados en otros ensamblados son desconocidos para esta compilación.
Vincular una expresión
El código anterior muestra cómo buscar un símbolo enlazando a un nombre. Hay otras expresiones en un programa de C# que se pueden enlazar que no son nombres. Para demostrar esta funcionalidad, vamos a acceder a la vinculación de un literal de cadena simple.
El programa "Hola mundo" contiene una Microsoft.CodeAnalysis.CSharp.Syntax.LiteralExpressionSyntaxcadena "Hola, Mundo!" que se muestra en la consola.
Encontrará la cadena "Hello, World!" al buscar la cadena literal única en el programa. Después, una vez que haya localizado el nodo de sintaxis, obtenga la información de tipo de ese nodo desde el modelo semántico. Agregue el código siguiente al Main método :
// Use the syntax model to find the literal string:
LiteralExpressionSyntax helloWorldString = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.Single();
// Use the semantic model for type information:
TypeInfo literalInfo = model.GetTypeInfo(helloWorldString);
La Microsoft.CodeAnalysis.TypeInfo estructura incluye una TypeInfo.Type propiedad que permite el acceso a la información semántica sobre el tipo del literal. En este ejemplo, es el tipo string. Agregue una declaración que asigne esta propiedad a una variable local:
var stringTypeSymbol = (INamedTypeSymbol?)literalInfo.Type;
Para finalizar este tutorial, vamos a crear una consulta LINQ que cree una secuencia de todos los métodos públicos declarados en el string tipo que devuelve un string. Esta consulta es compleja, por lo que vamos a compilarla línea por línea y, a continuación, reconstruirla como una sola consulta. El origen de esta consulta es la secuencia de todos los miembros declarados en el string tipo:
var allMembers = stringTypeSymbol?.GetMembers();
Esa secuencia de origen contiene todos los miembros, incluidas las propiedades y los campos, por lo que filtre mediante el ImmutableArray<T>.OfType método para buscar elementos que son Microsoft.CodeAnalysis.IMethodSymbol objetos:
var methods = allMembers?.OfType<IMethodSymbol>();
A continuación, agregue otro filtro para devolver solo los métodos que son públicos y devuelven un string:
var publicStringReturningMethods = methods?
.Where(m => SymbolEqualityComparer.Default.Equals(m.ReturnType, stringTypeSymbol) &&
m.DeclaredAccessibility == Accessibility.Public);
Seleccione solo la propiedad "nombre" y solo los nombres distintos eliminando las sobrecargas.
var distinctMethods = publicStringReturningMethods?.Select(m => m.Name).Distinct();
También puede compilar la consulta completa mediante la sintaxis de consulta LINQ y, a continuación, mostrar todos los nombres de método en la consola:
foreach (string name in (from method in stringTypeSymbol?
.GetMembers().OfType<IMethodSymbol>()
where SymbolEqualityComparer.Default.Equals(method.ReturnType, stringTypeSymbol) &&
method.DeclaredAccessibility == Accessibility.Public
select method.Name).Distinct())
{
Console.WriteLine(name);
}
Compile y ejecute el programa. Debería ver los siguientes resultados:
Join
Substring
Trim
TrimStart
TrimEnd
Normalize
PadLeft
PadRight
ToLower
ToLowerInvariant
ToUpper
ToUpperInvariant
ToString
Insert
Replace
Remove
Format
Copy
Concat
Intern
IsInterned
Press any key to continue . . .
Ha usado la API semántica para buscar y mostrar información sobre los símbolos que forman parte de este programa.