热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Roslyn入门(二)C#语义

1|0先决条件VisualStudio2017.NETCompilerPlatformSDKRosyln入门(一)-C#语法分析2|0简介今天,VisualBasic和C#编译器是


1|0先决条件

Visual Studio 2017

.NET Compiler Platform SDK

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

2|0简介

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

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

3|0理解编译和符号

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

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

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

4|0演示-创建编译



  • 引入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处查看提示。


5|0语义模型SemanticModel

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

5|1示例-绑定名称



此示例显示如何为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

5|2示例--绑定表达式



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

var helloWorldString = root.DescendantNodes()
.OfType()
.First();

var literalInfo = model.GetTypeInfo(helloWorldString);
var stringTypeSymbol = (INamedTypeSymbol)literalInfo.Type;
Console.Clear();

foreach (var name in (from method in stringTypeSymbol.GetMembers()
.OfType()
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

6|0总结

本篇文章演示了语义分析,通过两个示例分别演示绑定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()


获取Type信息 model.GetTypeInfo(helloWorldString)


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


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


7|0源码

csharpfandemo

8|0参考链接

Getting Started C# Semantic Analysis

 

本文原文:https://www.cnblogs.com/fancunwei/p/9855834.html


推荐阅读
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 在project.properties添加#Projecttarget.targetandroid-19android.library.reference.1..Sliding ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
author-avatar
纠结不停的孩子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有