如何在编译之前修改代码?

使用Roslyn,我想在实际编译之前修改我的C#代码。 现在,我只需要像这样的东西:

[MyAnotatedMethod] public void MyMethod() { // method-body } 

并且基于注解,我想在方法的开始处和方法的末尾注入一些代码。

我知道PostSharp,但这不是我想要的。

这可能与Roslyn有关吗? 如果是的话,你能举个例子吗?

这是一个快速和肮脏的方式做你想做的事情。 它基于上面的评论之一,指向SebbyLive 。 这只是一个概念的certificate,我不会尝试在生产中使用它。

基本的想法是你改变你想修改的项目的编译器。 而这个改变的编译器将会执行代码注入。 所以你需要编写一个新的编译器(AopCompiler.exe),并将其设置为项目中的构build工具。

将AopCompiler.exe设置为构build工具很容易,在您的项目文件中,您需要添加以下两行:

 <CscToolPath>$(SolutionDir)AopCompiler\bin\Debug</CscToolPath> <CscToolExe>AopCompiler.exe</CscToolExe> 

AopCompiler应该是一个简单的控制台应用程序。 这是在做代码修改和编译。 如果您不想修改源代码,只需构build它,那么最简单的方法就是自己调用csc.exe:

 static void Main(string[] args) { var p = Process.Start(@"C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe", string.Join(" ", args)); p.WaitForExit(); } 

所以,如果你设置这个到目前为止,你会有一个正常的构build过程,没有方面编织。

此时,如果检查args ,就会看到有一个.RSP文件的文件path,其中包含csc.exe的所有命令行参数。 自然,这些参数也包含所有的.CS文件名。 所以你可以parsing这个.RSP文件,并find所有的.CS文件,这是编译的一部分。

有了C#文件,重写可以用Roslyn来完成。 CSharpSyntaxRewriter上有许多教程,例如这里和这里 。 您需要编写自定义的CSharpSyntaxRewriter ,它将检查给定的属性,然后将日志logging添加到find的方法的开头。 将日志logging添加到每个方法的末尾有点棘手,因为可能有多个退出点。 要find这些,你可以使用控制stream分析。 内置的Roslyn控制stream分析可以准确地告诉你后面的内容, ExitPoints属性包含跳转到区域之外的区域内的一组语句。

要获得语义模型(然后进行CFG分析),可以执行如下操作:

 public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) { var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree); // semanticModel.AnalyzeControlFlow(node.Block) return node; } 

最后,为了处理每个input文件,你的AopCompiler,你只需要在树的根上调用你的重写器的Visit方法。 这将产生修改后的树,你可以写出一个文件。 (您可以修改原始文件,也可以将结果写入新文件,并相应地更改.RSP文件。)

对不起,没有提供完整的工作解决scheme,但我希望,这足以让你开始。

正如我在我的评论中指出的那样,目前还没有。 尽pipe您可以使用此处显示的AOP技术与Roslyn Scripting API一起使用,以提供非常灵活的解决scheme。

在这个时候,正确的答案是Roslyn团队有一个公开的提议/问题来支持它。 感谢.NET基金会和微软参与Open Source,你可以在这里阅读这个function开发:

https://github.com/dotnet/roslyn/issues/5561

它比使用Annotation稍微有些脏,但是使用Preprocessor指令阻塞代码将允许您configuration基于标志编译代码的方式。

http://www.codeproject.com/Articles/304175/Preprocessor-Directives-in-Csharp

下面的#ifMyFlag中的代码只有在定义了MyFlag的情况下才会被编译

 #define MyFlag public void MyMethod() { #if MyFlag // Flag conditional code #endif // method-body }