Roslyn入门(1):一个简单修改C#代码的例子

网上其他例子都写得有点难懂, 这里有个简单的, 首先你要装好 roslyn,  如果没有安装,打开vs2019的安装程序,点修改,选择如下图进行安装:

我已经在vs2019里面装了, 所以创建项目时出现下图:

选择如图Stand-Alone Code Analysis Tool, 创建项目, 框架选.Net 4.7.2, 但是不知道什么原因,创建出来的项目运行不了,对初学者来说太麻烦,也懒得找原因.

于是删除原来的代码, 只保留空的Main函数. 保留原来的using , 如下所示:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;

namespace TestMyRewriter
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("Hello world");
        }
    }
}

保证这个Main能正常运行后, 把程序完整填写:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;

namespace TestMyRewriter
{
    class Program
    {
        static async Task Main(string[] args)
        {   //原始代码
            string strCode = @"
                  public class Foo
                  {
                      public string _bar = ""baz"";  
                      public string strHello = ""heloo world"";
                  }";
            var tree = CSharpSyntaxTree.ParseText(strCode);
            var Mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            var compilation = CSharpCompilation.Create("MyCompilation",
                syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
            // 获得语义模型
            var model = compilation.GetSemanticModel(tree);
            var root = model.SyntaxTree.GetRoot();
            // 用Visit重写代码
            var rw = new LiteralRewriter();
            var newRoot = rw.Visit(root);
            // 新生成代码
            string strNewCode = newRoot.GetText().ToString();
            Console.WriteLine(strNewCode);
            Console.ReadLine();
        }
    }
    
    class LiteralRewriter : CSharpSyntaxRewriter      // 继承CSharpSyntaxRewriter
    {
        public override SyntaxNode VisitLiteralExpression(LiteralExpressionSyntax node) // 重载 VisitLiteralExpression 方法, 输入节点是 文字表达式
        {   
            if (!node.IsKind(SyntaxKind.StringLiteralExpression))
            {  return base.VisitLiteralExpression(node);        }
            // 重新构造一个字符串表达式
            var retVal = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,
                                                         SyntaxFactory.Literal("NotBaz"));
            return retVal;
        }
    }
 }

运行结果如下:

这段代码简要解释就是: 用roslyn的语法树工具, 构造一个根节点, 用CSharpSyntaxReWriter重写里面的部分代码, 原始代码就是Main函数里strCode字符串,  重写的办法是自己写一个类继承CSharpSyntaxReWriter, 里面所有以Visit开头的函数都可以重载, 比如VisitLiteralExpression解决字符串表达式, 入口参数node里面只有字符串, 且有多少个字符串就调用这个函数多少次. 现在我重载里非常简单粗暴, 只要是字符串我就改成"NotBaz" . 

但是我们还想知道其他的重载函数做什么的, 几十个重载函数, 文档根本没写清楚, 好多生僻词, 这些计算机文法语法分析的专业词, 一般字典查不清楚,只能知道大概, 或者说吃不准确切含义. 简单办法就是重载,用Console.WriteLine大法. 不断修改strCode里的代码内容,会看到各个重载里显示不同的东西, 当然有些内容你的代码块里没有,重载也不会调用到,  代码示例:

        public override SyntaxNode VisitArgument(ArgumentSyntax node)
        {
            Console.WriteLine("ArgumentSyntax:{0}", node.GetText());
            return base.VisitArgument(node);
        }
        public override SyntaxNode VisitArgumentList(ArgumentListSyntax node)
        {
            Console.WriteLine("ArgumentListSyntax:{0}", node.GetText());
            return base.VisitArgumentList(node);
        }

        public override SyntaxNode VisitVariableDeclaration(VariableDeclarationSyntax node)
        {
            Console.WriteLine(node.Variables.First().Initializer.Value );
            return base.VisitVariableDeclaration(node);
        }

现在稍微搞清楚的有: VisitArgument, 参数,  VisitArgumentList 参数列表  VisitLiteralExpression  文字表达式(还不清楚和StringLiteralExpression的确切差别),  VisitIdentifierName 标识符名字(包括各种关键字和变量名)  VisitVariableDeclaration 变量声明表达式VisitVariableDeclarator 变量本身

这一行里:    Console.WriteLine(node.Variables.First().Initializer.Value ); 

  Initializer.Value就是变量初始化的值, 如果没初始化Initializer会是Null,直接调用Value会导致异常. (说人话就是 int a=10; 初始化了就ok, 只写个 int a; 它的Initializer就会是null)

我也是初学,很多东西还在摸索中,先记录下来做个笔记. 

参考链接: https://johnkoerner.com/csharp/using-a-csharp-syntax-rewriter/

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值