Roslyn入门(3): 修改本程序的源码

3 篇文章 0 订阅

本例子依然来自 https://github.com/dotnet/roslyn/wiki/Getting-Started-C%23-Syntax-Transformation , 看过的大神跳过. 

修改源码的功能一般常见于各种插件,比如大名鼎鼎的VAX,  本程序的作用也能修改源码, 而且就是修改自己刚打入的代码,  本project里包含两个cs文件, 运行之后会把这两个cs里的部分变量定义改为var. 比如Main函数的这几行, 修改前:

        private static void Main()
        {
                SemanticModel model = test.GetSemanticModel(sourceTree);
                TypeInferenceRewriter rewriter = new TypeInferenceRewriter(model);
                SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
        }

修改后

        private static void Main()
        {
                var model = test.GetSemanticModel(sourceTree);
                var rewriter = new TypeInferenceRewriter(model);
                var newSource = rewriter.Visit(sourceTree.GetRoot());
        }

 好吧, 把完整源码贴出来, 操作步骤跟例子1一样, 在vs2019里先建立 Stand-Alone Code Analysis Tool 工程,再删除只留下空的Main, 确保空Main函数运行不出错的前提下做修改. 建立工程时,项目名称叫TransformationCS,  Main函数所在文件为Program.cs(默认),再建立一个新类名字是: TypeInferenceRewriter , 类文件名字TypeInferenceRewriter.cs,  这两个文件名字程序会用到, 所以要提前说清楚不要修改, 先贴出Program.cs完整内容

using System;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace TransformationCS
{
    internal class Program
    {
        private static void Main()
        {
            Compilation test = CreateTestCompilation();
            foreach (SyntaxTree sourceTree in test.SyntaxTrees)
            {
                SemanticModel model = test.GetSemanticModel(sourceTree);
                TypeInferenceRewriter rewriter = new TypeInferenceRewriter(model);
                SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
                if (newSource != sourceTree.GetRoot())
                {
                    File.WriteAllText(sourceTree.FilePath, newSource.ToFullString());
                }
            }
        }

        private static Compilation CreateTestCompilation()
        {
            String programPath = @"..\..\..\Program.cs";
            String programText = File.ReadAllText(programPath);
            SyntaxTree programTree =
                           CSharpSyntaxTree.ParseText(programText)
                                           .WithFilePath(programPath);

            String rewriterPath = @"..\..\..\TypeInferenceRewriter.cs";
            String rewriterText = File.ReadAllText(rewriterPath);
            SyntaxTree rewriterTree =
                           CSharpSyntaxTree.ParseText(rewriterText)
                                           .WithFilePath(rewriterPath);

            SyntaxTree[] sourceTrees = { programTree, rewriterTree };

            MetadataReference mscorlib =
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            MetadataReference codeAnalysis =
                    MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
            MetadataReference csharpCodeAnalysis =
                    MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);

            MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis };

            return CSharpCompilation.Create("TransformationCS",
                                            sourceTrees,
                                            references,
                                            new CSharpCompilationOptions(
                                                    OutputKind.ConsoleApplication));
        }
    }
}

TypeInferenceRewriter.cs 完整内容

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace TransformationCS
{
    public class TypeInferenceRewriter : CSharpSyntaxRewriter
    {
        private readonly SemanticModel SemanticModel;
 
        public TypeInferenceRewriter(SemanticModel semanticModel)
        {
            this.SemanticModel = semanticModel;
        }
 
        public override SyntaxNode VisitLocalDeclarationStatement(
                                       LocalDeclarationStatementSyntax node)
        {
            if (node.Declaration.Variables.Count > 1) 
            {
                return node;
            }
            if (node.Declaration.Variables[0].Initializer == null)
            {
                return node;
            }
 
            VariableDeclaratorSyntax declarator = node.Declaration.Variables.First();
            TypeSyntax variableTypeName = node.Declaration.Type;
            
            ITypeSymbol variableType = 
                           (ITypeSymbol)SemanticModel.GetSymbolInfo(variableTypeName)
                                                    .Symbol;
            
            TypeInfo initializerInfo = 
                         SemanticModel.GetTypeInfo(declarator
                                                   .Initializer
                                                   .Value);
            
            if (variableType == initializerInfo.Type)
            {
                TypeSyntax varTypeName = 
                               IdentifierName("var")
                                     .WithLeadingTrivia(
                                          variableTypeName.GetLeadingTrivia())
                                     .WithTrailingTrivia(
                                          variableTypeName.GetTrailingTrivia());
 
                return node.ReplaceNode(variableTypeName, varTypeName);
            }
            else
            {
                return node;
            }
        }
    }
}

运行时要留意文件路径问题, 否则提示找不到 , 就是这两行,

            String programPath = @"..\..\..\Program.cs";
            String rewriterPath = @"..\..\..\TypeInferenceRewriter.cs";

我把它从 ..\..\ 改成 ..\..\..\ , 因为我的exe文件在debug目录下还有一级, 实际是 \TransformationCS\TransformationCS\bin\Debug\net472 , 所以要做修改, 可能vs2015里没有net472这一级(我没测试过), 所以原作者用了2级目录.  如果实在搞不清, 建议修改为绝对路径.  程序运行完毕后, 就会发现刚写的代码里面有部分变量类型被改为var.

可以用ctrl+z / ctrl+y 来回切换对比看结果.  修改的核心功能就在TypeInferenceRewriter类的VisitLocalDeclarationStatement 里面,

在第一篇Roslyn入门(1):一个简单修改C#代码的例子说过, 这个VisitLocalDeclarationStatement 重载是关于变量声明语句的, 所以传进来的node全是变量声明语句, 变量个数超过1个和没初始化的不管, 其他酌情处理. 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值