c# - significado - ¿Cómo modificar el código antes de la compilación?
roslyn new york (3)
Aquí hay una manera rápida y sucia de hacer lo que quieras. Se basa en uno de los comentarios anteriores, que apunta a sebbylive.com/articles/programming/… . Es solo una prueba de concepto, no trataría de usarlo en producción.
La idea básica es que cambies el compilador del proyecto que deseas modificar. Y este compilador cambiado hará la inyección de código. Por lo tanto, necesitaría escribir un nuevo compilador (AopCompiler.exe) y establecerlo como la herramienta de compilación en su proyecto.
Configurar AopCompiler.exe como la herramienta de compilación es fácil, en su archivo de proyecto, debe agregar las dos líneas siguientes:
<CscToolPath>$(SolutionDir)AopCompiler/bin/Debug</CscToolPath>
<CscToolExe>AopCompiler.exe</CscToolExe>
AopCompiler debe ser una aplicación de consola simple. Esto está haciendo la modificación del código y la compilación también. Si no desea modificar el código fuente, simplemente créelo, entonces la forma más fácil es llamar al csc.exe usted mismo:
static void Main(string[] args)
{
var p = Process.Start(@"C:/Program Files (x86)/MSBuild/14.0/Bin/csc.exe",
string.Join(" ", args));
p.WaitForExit();
}
Entonces, si configuraste esto hasta ahora, tendrías un proceso de compilación normal, sin que el aspecto se tejiera.
En este punto, si revisa qué hay en los args
, verá que hay una ruta de archivo a un archivo .RSP, que contiene todos los parámetros de la línea de comandos para csc.exe. Naturalmente, estos parámetros contienen todos los nombres de archivo .CS también. Así que podría analizar este archivo .RSP y encontrar todos los archivos .CS, que son parte de la compilación.
Con los archivos C # en la mano, la reescritura se puede hacer con Roslyn. Hay muchos tutoriales sobre CSharpSyntaxRewriter
, por ejemplo here , y sebbylive.com/articles/programming/… . CSharpSyntaxRewriter
que escribir su CSharpSyntaxRewriter
personalizado, que busca el atributo dado, y luego agregar el registro al comienzo de los métodos encontrados. Agregar el registro al final de cada método es un poco más complicado, porque puede haber múltiples puntos de salida. Para encontrarlos, puede usar el análisis de flujo de control. El análisis de flujo de control incorporado de Roslyn puede brindarle exactamente lo que ExitPoints
, la propiedad ExitPoints
contiene el conjunto de declaraciones dentro de una región que salta a ubicaciones fuera de la región.
Para obtener el modelo semántico (y luego hacer el análisis CFG) puede hacer algo como esto:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree);
// semanticModel.AnalyzeControlFlow(node.Block)
return node;
}
Finalmente, para procesar cada uno de los archivos de entrada, su AopCompiler, simplemente tiene que llamar al método de Visit
su reescritor en la raíz del árbol. Eso producirá el árbol modificado, que puede escribir en un archivo. (O puede modificar el archivo original, o escribir el resultado en uno nuevo, y cambiar el archivo .RSP en consecuencia).
Perdón por no proporcionar una solución completa, pero espero que esto sea suficiente para comenzar.
Usando Roslyn, me gustaría modificar mi código C # antes de la compilación real. Por ahora, solo necesitaré algo como:
[MyAnotatedMethod]
public void MyMethod()
{
// method-body
}
Y en base a la anotación, me gustaría insertar algún código al comienzo del método y al final del método.
Conozco PostSharp, pero eso no es lo que me gustaría.
¿Es esto posible con Roslyn? Y si es así, ¿podría darme un ejemplo?
Como señalé en mi comentario, actualmente no está disponible. Aunque podría juntar algo utilizando las técnicas de AOP que se muestran here con la API de Roslyn Scripting para proporcionar una solución muy flexible.
La respuesta correcta en este momento es que el equipo de Roslyn tiene una propuesta / problema abierto para apoyarlo. Gracias a .Net Foundation y Microsoft que participan en Open Source, puede leer sobre el desarrollo de estas características aquí:
Es un poco más sucio que usar una Anotación, pero bloquear el código usando Directivas de preprocesador le permitirá configurar cómo se compila el código basado en indicadores.
http://www.codeproject.com/Articles/304175/Preprocessor-Directives-in-Csharp
El código en el #ifMyFlag en el siguiente listado solo se compilará si se define MyFlag
#define MyFlag
public void MyMethod()
{
#if MyFlag
// Flag conditional code
#endif
// method-body
}