【CSharp Roslyn教程】【末】应用篇

工具与准备

使用的IDE是Visual Studio 2017,之前试过用VS2013,在下载依赖项的时候出现了一些问题,可能是版本太老。

新建一个C#控制台程序,在项目管理器那里选择NuGet,进入界面之后搜索第三方包Microsoft.CodeAnalysis.CSharp并下载安装。

第一次尝试

首先来尝试获取一下某一份的类名。

先上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
String codeStr = System.IO.File.ReadAllText(System.IO.Path.GetFullPath("..\\..\\Program.cs"));
Console.WriteLine(codeStr);

SyntaxTree tree = CSharpSyntaxTree.ParseText(codeStr);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
foreach(var element in root.Members)
{
foreach(var childNode in element.ChildNodes())
{
Type nodeType = childNode.GetType();
if(nodeType.Equals(typeof(ClassDeclarationSyntax)))
{
ClassDeclarationSyntax childNodeClass = childNode as ClassDeclarationSyntax;
Console.WriteLine(childNodeClass.Identifier.Text);
}
}
}
Console.ReadLine();
}
}
}

首先我们把整份代码用System.IO.File.ReadAllText读进来。然后需要把代码文本进行分析,读成一份语法树。

1
SyntaxTree tree = CSharpSyntaxTree.ParseText(codeStr);

得到语法树之后,我们就可以遍历它的成员了。一般来说成员只会有一个。

接下来遍历所有成员的节点,节点可能会有三个:

  1. using定义
  2. namespace定义
  3. class定义

三者都继承于基类MemberDeclarationSyntax

现在我们的目的是取到这份代码里面所有的类的类名。于是判断每个节点的类型,如果是ClassDeclarationSyntax类型就进行强转。

强转之后得到的childNodeClass,我们就可以通过

1
string theClassName = childNodeClass.Identifier.Text;

来得到对应的类名了。

好,本期到此结束(误,只是留个坑啦

解决获取不到document的问题

1
Install-Package Microsoft.Build.Tasks.Core -Version 16.0.461

遍历语法树并输出所有方法和字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
public static Int32 StaticIntVal;
public Int32 MemberIntVal;

static void Main(string[] args)
{
RunAsync().Wait();
}

private static async Task RunAsync()
{
string slnPath = Path.GetFullPath("../../../ConsoleApp1.sln");
Console.WriteLine(slnPath);
var solution = await MSBuildWorkspace.Create().OpenSolutionAsync(slnPath);
var project = solution.Projects.First(x => x.Name == "ConsoleApp1");
var document = project.Documents.First(x => x.Name.Equals("Program.cs"));

var tree = await document.GetSyntaxTreeAsync();
var syntaxTree = tree.GetCompilationUnitRoot();

foreach(var member in syntaxTree.Members)
{
if(member.GetType() == typeof(NamespaceDeclarationSyntax))
{
var namespaceDeclaration = member as NamespaceDeclarationSyntax;
Console.WriteLine("Found a Namespace: " + namespaceDeclaration.Name);
foreach (var memberInNS in namespaceDeclaration.Members)
{
if(memberInNS.GetType() == typeof(ClassDeclarationSyntax))
{
var classDeclaration = memberInNS as ClassDeclarationSyntax;
Console.WriteLine("\tFound a Class: " + classDeclaration.Identifier.Text);
foreach(var memberInClass in classDeclaration.Members)
{
if(memberInClass.GetType() == typeof(MethodDeclarationSyntax))
{
var methodDeclaration = memberInClass as MethodDeclarationSyntax;
Console.WriteLine("\t\tFound a Method: " + methodDeclaration.Identifier.Text);
}
if (memberInClass.GetType() == typeof(FieldDeclarationSyntax))
{
var fieldDeclaration = memberInClass as FieldDeclarationSyntax;
Console.WriteLine("\t\tFound a Field: " + fieldDeclaration.Declaration.Variables.First().Identifier.Text);
}
}
}
}
}
}
Console.ReadLine();
}
}
}

现在针对一些常用的模式进行分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A
{
public Int32 value;
public A Func(A a) { return null; }
}

class B
{
public A a;
}

class Program
{
static void Main(string[] args)
{
A a = new A();
B b = new B();
b.a.value = 2;
b.a.Func(a);
Console.WriteLine();
Console.ReadLine();
}
}

看一下上面代码中这句代码的结构

1
b.a.value = 2;
  • ExpressionStatement b.a.value = 2;
    • SimpleAssignmentExpression b.a.value = 2
      • SimpleAssignmentExpression b.a.value
        • SimpleMemberAccessExpression b.a
          • IdentifierName b
          • DotToken .
          • IdentifierName a
        • DotToken .
        • IdentifierName value
      • EqualsToken =
      • NumericLiteralExpression 2
    • SemicolonToken ;

再看方法调用

1
b.a.Func();
  • ExpressionStatement b.a.Func(a);
    • InvocationExpression b.a.Func(a)
      • SimpleMemberAccessExpression b.a.Func
        • SimpleMemberAccessExpression b.a
          • IdentifierName b
          • DotToken .
          • IdentifierName a
        • DotToken ‘.’
        • IdentifierName Func
      • ArgumentList (a)
        • OpenParenToken (
        • Argument a
          • IdentifierName a
        • CloseParenToken )
    • SemicolonToken ;

接下来看函数的定义

Buy Me A Coffee / 捐一杯咖啡的钱
分享这篇文章~
0%
//