网上其他例子都写得有点难懂, 这里有个简单的, 首先你要装好 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/