Roslyn 的 API 是非常易用的。即便如此复杂的 C# 语法,建立的复杂的 C# 语法树,还有其复杂的树遍历和修改过程,也都被其 API 包装得干净简洁。

而这背后是它的重要设计思路 —— 红绿树。


红绿树的影子

如果你是通过搜索找到这篇文章的,那么至少证明你调试过 Roslyn API 的使用,或者阅读过 Roslyn 的源码。因为正常使用 Roslyn 的 API 时你是看不到红绿树的,这是 Roslyn 的实现细节。但你在调试的时候可能会看到 Green 属性,或者在阅读源码时看到 GetRed 方法。


▲ 调试时看到的绿树

protected T GetRed<T>(ref T field, int slot) where T : SyntaxNode
{var result = field;if (result == null){var green = this.Green.GetSlot(slot);if (green != null){Interlocked.CompareExchange(ref field, (T)green.CreateRed(this, this.GetChildPosition(slot)), null);result = field;}}return result;
}

▲ Roslyn 中获取红树的源代码

源代码摘抄自:roslyn/SyntaxNode.cs at master · dotnet/roslyn。

Roslyn 的设计理念

Roslyn 一开始就将漂亮的 API 作为目标的一部分,同时还要非常高的性能;所以 Roslyn 的开发团队需要找到一种特殊的数据结构来描述语言(如 C#)的语法。这种数据结构要满足这些期望的要求:

  • 不可变(Immutable)
  • 树的形式
  • 可以容易地访问父节点和子节点
  • 可以非常容易地将任何一个节点对应到源代码文件的一段文本区间
  • 可重用(Persistent)

最后一个的英文说法是 Persistent,单词的原本意思是“可持久的,连续的”,我把它翻译为“可重用”(Reusable)。Roslyn 的设计中有一个重要的业务需求,希望能够分析源代码文件并在开发者编辑的过程中不断提供建议。也就是说,当我们连续不断地去修改源代码中的文本内容时,Roslyn 也需要具备很高的性能。如果每次编辑代码都去重新解析一次整份源代码,然后全部重新生成整个数据结构,那将是大量的性能浪费;更不可能实时去分析开发者编辑的源码。所以,在 Roslyn 的设计中,希望源代码文本改变时,整棵树中的大多数节点都是能够重复使用的(无需重新生成)。

而如果将数据结构设计成不可变的(Immutable),那么重用这些节点将会非常容易。当然不止对于 Roslyn,对其它数据来说,不可变也一样有各种好处;比如可以随时重用这份数据的实例而不用担心可能被各个不同的业务模块意外修改,比如天然是线程安全的。

那么问题来了,到底什么样的数据结构能够在同时满足以上所有的特点的前提下,同时还能设计出简单易用的 API 呢?

  • 既然要容易地访问到父节点和子节点,那么我们是先构造父节点还是子节点呢?如果先构造父节点,那子节点还没有创建出来;而先构造子节点,那父节点就没构造出来。我们要求这样的数据结构具有不可变性,所以我们不可能先把它们都构造出来再去修改它们的父子关系。
  • 还有,我们也不能随意地去为任何子节点指定新的父节点,因为子节点是不可变的。然而我们同时有希望能够在连续修改的情况下具备较高的性能,如果连修改父节点都不能办到,那也很难重用之前的节点,最终不得不再次重新生成所有的子节点。
  • 另外,如果你在源代码文件中插入了一个字符,那么这个字符后面的每一个节点对应的源代码区间都需要改变。然而这非常不利于连续修改,因为随便一个字符的插入都将导致更新大量节点中的文本区间信息。而由于不可变性,我们只能重新生成这些节点而没法儿重用它们。

于是 Roslyn 团队就折腾出了“红绿树”(Red-Green Trees)。

红绿树

红绿树并不是一棵树,而是两棵树。

绿树(the green tree)是不可变的,可重用的,没有父节点的引用。绿树的构建是自下而上的,每一个节点都保存它在文本区间中的字符个数(说通用点是宽度)。如果源代码的内容被编辑,我们只需要重新创建受编辑影响的绿树的部分;相比于重新分析整棵树,其时间复杂度只有 O(log n)。

树(the red tree)也是不可变的,是围绕绿树而建的外观(参见 外觀模式)。红树的构建是自上而下的,但红树只在需要时才会创建,而一旦编辑了源代码文件,红树就直接丢弃不用了。如果有需要,红树就会开始创建;它会根据绿树自上而下计算最新的父节点引用,计算节点最新对应的文本区间。

这两棵树设计起来协同工作,前者负责解决 Roslyn 语法分析的性能问题,后者负责对开发人员提供友好的 API 调用。由于最开始 Roslyn 团队的大佬们在会议室讨论时,前者是用红笔画的,后者是用绿笔画的,于是就合在一起称作“红绿树”。

自此,Roslyn 团队设计出的这种数据结构满足了以上所有的要求。不过,如果红树太大,每次重新生成依然会耗费比较多的性能。


参考资料

  • Persistence, Facades and Roslyn’s Red-Green Trees – Fabulous Adventures In Coding
  • 外觀模式 - 维基百科,自由的百科全书

理解 Roslyn 中的红绿树(Red-Green Trees)相关推荐

  1. 万字长文深入理解java中的集合-附PDF下载

    文章目录 1. 前言 2. List 2.1 fail-safe fail-fast知多少 2.1.1 Fail-fast Iterator 2.1.2 Fail-fast 的原理 2.1.3 Fai ...

  2. 【拼爹坑爹不比爹】深入理解css中position属性及z-index属性

    总结:拼爹坑爹不比爹 1,定位移动:距左上角left+top;(移动后对于移动前:如果值为负数,则直接换成整数:如果值为整数,则直接改变相对方向.) 2,单一父元素不设值,子z-index才奏效:&q ...

  3. 理解设计模式中的工厂模式

    在理解设计模式中的单例模式一文中介绍了创建型模式中最为简单的一种:单例模式,并推荐了几种可以在实际生产中使用的线程安全的形式.本文将继续介绍创建型模式中的工厂方法模式和抽象工厂模式,同样使用代码和类图 ...

  4. HashMap中的红黑树

    转载自:http://blog.csdn.net/u011240877/article/details/53358305 张拭心 读完本文你将了解到: 点击查看 Java 集合框架深入理解 系列 - ...

  5. 理解CSS3中的background-size(对响应性图片等比例缩放)

    理解CSS3中的background-size(对响应性图片等比例缩放) 阅读目录 background-size的基本属性 给图片设置固定的宽度和高度的 固定宽度400px和高度200px-使用ba ...

  6. 从编译器层面理解C#中的闭包的这个坑!

    前言 在公众号上看到一篇文章<正确使用和理解C#中的闭包>,里面提到了闭包的一个坑: 当捕获的外部变量为for循环的迭代变量时,C#认为变量i是定义在循环体外的.所以,当添加委托集合的fo ...

  7. 高光谱数据集_文献选读|从地面和空间高光谱数据中提取红边位置参数,以估算水稻冠层叶氮含量...

    点击蓝字 关注我们 为了实现水稻施肥的精确管理,对作物氮素状态进行无损实时监测,我们从地面和空间数据中对红边区域的反射光谱进行了表征,并对红边位置之间的关系进行了量化(REP)来自不同算法和田间水稻( ...

  8. 理解NLP中的卷积神经网络(CNN)

    此篇文章是Denny Britz关于CNN在NLP中应用的理解,他本人也曾在Google Brain项目中参与多项关于NLP的项目.  ·  翻译不周到的地方请大家见谅. 阅读完本文大概需要7分钟左右 ...

  9. 【计算视觉】理解图像中基本概念:色调、色相、饱和度、对比度、亮度

    理解图像中基本概念:色调.色相.饱和度.对比度.亮度 对比度: 对比度指不同颜色之间的差别.对比度越大,不同颜色之间的反差越大,即所谓黑白分明,对比度过大,图像就会显得很刺眼.对比度越小,不同颜色之间 ...

  10. c语言捕鱼达人源码,用捕鱼达人去理解C中的多线程.doc

    用<捕鱼达人>去理解C 中的多线程 线程是进程中某个单一顺序的控制流,是程序运行中的调度单位,是程序执行流的最小单位,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成. ...

最新文章

  1. 腾讯优图+厦门大学发布!2021十大人工智能趋势
  2. WGCNA加权基因共表达网络分析(1)简介、原理
  3. 程序员,你是选择25k的996还是18k的八小时工作日
  4. 小程序中使用threejs
  5. 《Javascript高级程序设计》读书笔记之bind函数详解
  6. SARscape_5.2.0和SARscape_5.2.1安装包下载
  7. c++ primer文本查询程序 自编加强版(c++primer5th 练习12.32-33)
  8. Error-Project facet Java version 1.8 is not supported
  9. freemarker 生成java_半自动化Java代码生成器[利用freemarker模板生成]
  10. UI实用素材|扁平化UI设计模板,UI设计师都要会!
  11. openSSH离线升级(6.6->7.9),解决Linux安全漏洞(CVE-2018-15473)
  12. dotNet基于office实现word转pdf
  13. chrome Axure插件(Mac版)
  14. windows录屏_电脑是怎么录屏的呢?推荐三个录屏实用方法!
  15. Linux下安装vim编辑器
  16. GCD,快速GCD,扩展GCD
  17. Linux GDB分析死锁
  18. Revit版本对应的.NET框架版本
  19. 树莓派挂载硬盘以及播放视频
  20. JAVA模板引擎velocity语法讲解

热门文章

  1. 给马云、马化腾订制旅行,这家公司想做旅游行业的“得到”
  2. 为什么人需要一个人静静--《孤独力》的读后感
  3. 书单 电影单 电视剧单
  4. 【跨境电商】如何创建吸引人的免费送货电子邮件营销
  5. Android 系统 Bar 沉浸式完美兼容方案
  6. 给交换机console接口设置密码
  7. 寻找心灵深处的菩提树
  8. linux memery dump
  9. 瑞星发布可防未知勒索病毒工具 将逐月公布更多漏洞
  10. 计算机毕设存档袋子,关于做好2018届毕业论文(设计)材料整理存档的通知