先决条件

Visual Studio 2017

.NET Compiler Platform SDK

Rosyln入门(一)-C#语法分析

简介

今天,Visual Basic和C#编译器是黑盒子:输入文本然后输出字节,编译管道的中间阶段没有透明性。使用.NET编译器平台(以前称为“Roslyn”),工具和开发人员可以利用编译器使用的完全相同的数据结构和算法来分析和理解代码。

本篇文章,我们将探索Symbol和BindingAPI。通过语法API来查看解析器,语法树,用于推理和构造它们的实用程序。

理解编译和符号

这个语法API能让你看程序的结构。但是,通常您需要有关程序语义或含义的更丰富信息。虽然松散的代码片段可以单独进行语法分析,但孤立的提出诸如“这个变量的类型是什么”之类的问题并不是很有意义。类型名称的含义可能取决于程序集引用,命名空间导入或其他代码文件。这就是Compilation类的用武之地。

编译类似于编译器看到的单个项目,表示编译Visual Basic或C#程序所需的所有内容,例如程序集引用,编译器选项和要编译的源文件集。 通过此上下文,您可以推断出代码的含义。 编译允许您查找符号 - 名称和其他表达式引用的类型,名称空间,成员和变量等实体。 将名称和表达式与符号(Symbols)相关联的过程称为Binding。

与SyntaxTree一样,Compilation是一个具有特定语言派生类的抽象类。创建Compilation实例时,必须在CSharpCompilation(或VisualBasicCompilation)类上调用工厂方法。

演示-创建编译

  • 引入Nuget
  Microsoft.CodeAnalysis.CSharp

  Microsoft.CodeAnalysis.CSharp.Workspaces
  • 上节提到的演示Main代码
  class Program
{
static void Main(string[] args)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(
@"using System;
using System.Collections.Generic;
using System.Text; namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}"); var root = (CompilationUnitSyntax)tree.GetRoot();
}
}
  • 接下来,在Main方法的末尾创建CSharpCompilation对象
var compilation = CSharpCompilation.Create("HelloWorld")
.AddReferences(
MetadataReference.CreateFromFile(
typeof(object).Assembly.Location))
.AddSyntaxTrees(tree);
  • 设置断点,启动调试,在compilation处查看提示。

语义模型SemanticModel

一旦你有了编译,你可以要求它为该编译中包含的任何SyntaxTree提供SemanticModel。你可以查询SemanticModel来回答诸如“这个位置的范围是什么名称?”,“从这种方法可以获得哪些成员?” ,“在这个文本块中使用了哪些变量?”和“这个名字/表达是指什么?”之类的问题。

示例-绑定名称

此示例显示如何为HelloWorld SyntaxTree获取SemanticModel对象。获得SemanticModel后,第一个using指令中的名称绑定为System命名空间的符号。

  • 将下段代码放到Main的末尾。

var model = compilation.GetSemanticModel(tree); var nameInfo = model.GetSymbolInfo(root.Usings[0].Name); var systemSymbol = (INamespaceSymbol)nameInfo.Symbol;

*追加以下代码,枚举System命名空间的子命名空间并将其名称打印到控制台:

 foreach (var ns in systemSymbol.GetNamespaceMembers())
{
Console.WriteLine(ns.Name);
}
  • Debug进入调试,查看每个节点的值。Console输出结果如下:
Buffers
Collections
ComponentModel
Configuration
Diagnostics
Globalization
IO
Numerics
Reflection
Resources
Runtime
Security
StubHelpers
Text
Threading

示例--绑定表达式

前面的示例显示了如何绑定name去查找Symbol。但是,在C#程序中还有其他不是Name的表达式可以绑定。此示例显示绑定如何与其他表达式类型一起使用 - 在本例中为简单的字符串文字。

var helloWorldString = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.First(); var literalInfo = model.GetTypeInfo(helloWorldString); var stringTypeSymbol = (INamedTypeSymbol)literalInfo.Type; Console.Clear(); foreach (var name in (from method in stringTypeSymbol.GetMembers()
.OfType<IMethodSymbol>()
where method.ReturnType.Equals(stringTypeSymbol) &&
method.DeclaredAccessibility ==
Accessibility.Public
select method.Name).Distinct())
{
Console.WriteLine(name);
}
  • 运行Debug,查看相关节点的值,Console输出结果如下
Intern
IsInterned
Create
Copy
ToString
Normalize
Concat
Format
Insert
Join
PadLeft
PadRight
Remove
Replace
Substring
ToLower
ToLowerInvariant
ToUpper
ToUpperInvariant
Trim
TrimStart
TrimEnd

总结

本篇文章演示了语义分析,通过两个示例分别演示绑定Name查找Symbol和绑定表达式 我们可以获得以下几个知识点:

获取语法树的根节点:(CompilationUnitSyntax)tree.GetRoot()

用于创建CSharpCompilation对象:CSharpCompilation.Create("HelloWorld").AddReferences( MetadataReference.CreateFromFile( typeof(object).Assembly.Location)) .AddSyntaxTrees(tree)

用于获取模型:compilation.GetSemanticModel(tree);

获取Name的Symbol信息: model.GetSymbolInfo(root.Usings[0].Name);

可获取具体命名空间名:(INamespaceSymbol)nameInfo.Symbol;

获取当前命名空间下所有成员:systemSymbol.GetNamespaceMembers()

获取文字表达式:root.DescendantNodes().OfType<LiteralExpressionSyntax>()

获取Type信息 model.GetTypeInfo(helloWorldString)

获取具体的Type类型 (INamedTypeSymbol)literalInfo.Type;

获取返回值是string,公开类型的方法: from method in stringTypeSymbol.GetMembers().OfType<IMethodSymbol>() where method.ReturnType.Equals(stringTypeSymbol) && method.DeclaredAccessibility == Accessibility.Public select method.Name

源码

csharpfandemo

参考链接

Getting Started C# Semantic Analysis

Roslyn入门(二)-C#语义的更多相关文章

  1. 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

    前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...

  2. Swift语法基础入门二(数组, 字典, 字符串)

    Swift语法基础入门二(数组, 字典, 字符串) 数组(有序数据的集) *格式 : [] / Int / Array() let 不可变数组 var 可变数组 注意: 不需要改变集合的时候创建不可变 ...

  3. Thinkphp入门 二 —空操作、空模块、模块分组、前置操作、后置操作、跨模块调用(46)

    原文:Thinkphp入门 二 -空操作.空模块.模块分组.前置操作.后置操作.跨模块调用(46) [空操作处理] 看下列图: 实际情况:我们的User控制器没有hello()这个方法 一个对象去访问 ...

  4. DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表

    原文:DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用的, ...

  5. css入门二-常用样式

    css入门二-常用样式总结 基本标签样式 背景色background-color 高度height; 宽度width; 边框对齐以及详细设定举例 width/*宽度*/: 80%; height/*高 ...

  6. 微服务(入门二):netcore通过consul注册服务

    基础准备 1.创建asp.net core Web 应用程序选择Api 2.appsettings.json 配置consul服务器地址,以及本机ip和端口号信息 { "Logging&qu ...

  7. IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)

    1.系列文章引言 1.1 适合谁来阅读? 本系列文章尽量使用最浅显易懂的文字.图片来组织内容,力求通信技术零基础的人群也能看懂.但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获.如果您大 ...

  8. 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...

  9. 2.Python爬虫入门二之爬虫基础了解

    1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.想抓取什么?这个由你来控制它咯. ...

  10. [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?     http://www.52im.net/thread-1732-1-1.html   1.引言 本文接上篇<脑残式网 ...

随机推荐

  1. Linux中对逻辑卷的移除

    移除前先df -mT 看一下:(在上一篇的基础上:Linux中对逻辑卷进行扩容) 1.取消挂载同时删除/etc/fstab下的记录 取消挂载 umount /dev/zhi/lv-zhi 删除记录 v ...

  2. spring4笔记----“零配置”:spring提供的几个Annotation标注

    @Component  :标注一个普通的Spring Bean类 @Controller    :标注一个控制器组件器 @Service        :标注一个业务逻辑组件器 @Repository ...

  3. c/c++ 通用的(泛型)算法 之 只读算法,写算法,排序算法

    通用的(泛型)算法 之 只读算法,写算法,排序算法 只读算法: 函数名 功能描述 accumulate 求容器里元素的和 equal 比较2个容器里的元素 写算法 函数名 功能描述 fill 用给定值 ...

  4. mysql 数据库 命令行的操作——对库的操作

    1.查看所有数据库 show databaese; 2.查看当前所用的数据库 show databases(): 3.切换数据库 use(数据库名): 4.创建数据库 create database ...

  5. miui10 傻瓜式安装google框架方法

    miui10,打开自带的小米应用商店,搜索youtube,然后往下看选择百度的搜索源,点进去选择第一个下载就可以了. 下载完成之后会就提醒你安装google框架了, 点确定后自动就装好了,就是这么简单 ...

  6. GitHub-暂存区与版本回退

    参考博文:廖雪峰Git教程 1. 工作区和暂存区 Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 1.1. 工作区(Working Directory) 就是你在电脑里能看到的目录 ...

  7. puppet 和 apache passenger的配置

    目录 1. 概要 2. apache passenger 安装测试 2.1. 什么是 apache passenger 2.2. 安装 apache passenger 2.3. 配置 apache ...

  8. 枚举应用demo

    package com.xx; public enum PositionEM{ ONE(1,"领导"), TWO(2,"员工"); private Long v ...

  9. [福大软工] Z班 团队作业——系统设计 作业成绩

    团队作业--系统设计 作业链接 http://www.cnblogs.com/easteast/p/7709763.html 作业情况 这次作业大家完成度都很高,大家的团队分工,任务布置都安排得很到位 ...

  10. Unittest框架小结

    在日常的自动化测试过程中,Python里有一个自带的单元测试框架是unittest模块,简单易用,这里简单介绍下其主要的用法. Unittest测试框架主要包含四个部分 TestCase 也就是测试用 ...