如何高效地查看开源项目源码?

C++中的拷贝构造函数

标签: 源码学习方法

我们为什么要看源码?

这个小标题好像有点扯淡,不过我感觉还是有必要聊一聊。
最近搞 Blazor,手边常备 AspNetCore 源码,遇到问题了就翻源码。
然后有同样关注 Blazor 的同学会一起讨论一些问题,我知道的问题会直接分享,我不知道的问题,我就,甩一句,“看源码”
然后有的同学炸了,说,“不是每个人都可以像你一样看源码,源码不是每个人都能看的,不是每个人都想看源码”
当然原话不是这样,后两句是我添油加醋的,不过这两句想必是大部分同学的心声,心里害怕看源码,觉得看源码都是大神才会看的。

看源码的正确姿势

st=>start: 遇到问题
search=>operation: 百度谷歌
isOk=>condition: 能解决吗?
donotusecode=>end: 好了你可以不用继续看了
usecode=>end: 分析源码解决
st->search->isOk
isOk(yes)->donotusecode
isOk(no)->usecode

看源码的错误姿势

st=>start: 听说看源码能学到很多东西,能通过面试,能XXXXX
usecode=>end: 看源码
st->usecode

感觉第二个流程图像是在扯犊子,其实第二个图也并非完全错误,只是在强调你不能为了看而看,你要带着问题去看,但如果这样的话,那其实又变成了第一张流程图了

看源码的方法

很多同学在满足第一个流程图的条件之后,可能看源码仍然感觉累,没有头绪,这是没掌握看源码的技巧,可能存在以下几种情况

  • 拿着 github 在线看源码,这和拿记事本看源码没好到哪去
  • 真的就拿着记事本看源码,或者拿着 VSCode 看源码(我不是黑它)
  • 不擅于使用 VS 的相关功能

看源码一定先编译源码

不用编译直接打开 VS 一把梭,不是很好吗?
不好,确实是可以打开的,但是这个时候往往因为没有编译过导致 VS 的很多功能不可用,例如转到定义、查找引用。
对我而言,编译源码是个艰难的过程,看源码反而简单。特别是 AspNetCore 的源码,编译起来要了半条命。
关于怎么编译,每个开源项目都不一样,有的项目简单,打开 VS 直接就能编译,但是像 AspNetCore 这种,必须严格按照官方给的编译步骤文档,还得保证网络畅通,编译过程中很容易被劝退。

五大板斧不可少

有很多同学看源码的过程让我比较着急,咋看的呢?
CTRL+F,输入搜索词,全解决方案搜索,不管是怎样的情况全是这样搞,解决方案比较大的话一搜一大堆出来。
也许只是我周围的同学是这样的吧。
我看源码的过程中常使用这些方法

  • 转到定义,F12
  • 转到实现,CTRL + F12
  • 查找引用
  • 调用堆栈
  • 解决方案管理器搜索

这五大板斧中,除了调用堆栈,其他的几乎都是静态分析代码的必备方法,调用堆栈一般属于动态分析代码的办法。
这五大板斧用好了,只要源码的变量命名不要太坑爹,基本上是不需要看注释的,我看开源项目的源码从来不看注释,开源项目的源码的命名一般都是比较好的。

下面我将举例来分享我看源码的过程,把上面这几个方法用上

问题实例:怎么根据路由获取对应的组件?

这是我在编写 BlazAdmin 时遇到的问题,我需要根据当前路由得到这个路由对应的那个组件,然后做一些操作,我甚至不知道如何搜索,因为关键词不好搞,随便搜了几个也没找到答案。

下面开始分享我针对这个问题的解决思路,下载编译 AspNetCore 源码的步骤我就不写了

第一个问题,从哪儿开始?

万事开头难,刚开始看源码更难,无头苍蝇的感觉。
我们的需求是,怎么根据路由获取对应的组件,这里面有两个关键词,一个是路由,一个是组件
我们换位思考一下,假如由我们来开发这个功能,我们会写哪两个类?
答案应当很明显,RouterComponent,当然也许不是这么命名的,都有可能。但好像知道这两个名字了还是没啥用?根本问题在于 AspNetCore 解决方案太多,我们压根不知道应该打开哪个解决方案,也就无从搜索这两个类名。
我的办法是,也许这两个类我们是可以直接使用的,那么如果可以直接用的话,我们应该可以在代码的任意地方调用这两个类,那么我们就试试。
在我们自己项目的 Program.cs 文件中,随便找个地方,我们首先尝试 Router 关键字。
如何高效地查看开源项目源码?

好像有戏,我们已经看到命名空间了,那么根据这个命名空间,我们尝试找到对应的解决方案,这个命名空间的关键词是什么呢?Microsoft?AspNetCore?这两个关键词太大众化了,那么只剩下两个,Components 和 Routing,我们一个一个来

如何高效地查看开源项目源码?
如何高效地查看开源项目源码?

没啥好说的,既然找到了,管它是不是,先打开看看再说。

如何高效地查看开源项目源码?

我们直接在解决方案管理器中搜索这 Router 这个类。

如何高效地查看开源项目源码?

我们的入口点已经显而易见了

如何高效地查看开源项目源码?

第二个问题,这个类是如何找到 Component 的?

这个类中有这么些方法

使用python实现归并排序、快速排序、堆排序

如何高效地查看开源项目源码?

根据方法猜功能,没错,看源码全靠猜,猜错了换条路继续

OnAfterRenderAsync 这个应该不是
OnLocationChanged 这里面都有些啥?

如何高效地查看开源项目源码?

这里面调用了 Refresh 方法,F12 跟进去

如何高效地查看开源项目源码?

开始发晕了,咋这么多呢,而且还不明显,我们一行行看,一行行猜
看第五第六行,这两行有点意思,跟当前路由有关,也许是我们想要的
F12 进 RouteContext,看看里面有啥

如何高效地查看开源项目源码?

好吧啥也没有,我们继续看第六行,F12 进 Route 方法

如何高效地查看开源项目源码?

越来越像了,Match 方法里面又是什么呢?F12 进去

如何高效地查看开源项目源码?

这个方法中看起来也不像那么回事,不过还是有点有用信息,最后一句代码将 Handler 赋值了一下,但问题是 Handler 是啥?看这命名有点像当前路由的处理器,那么这个处理器可能就是我们要找的 Component,从构造方法中可以看出这个 Handler 是由构造方法传过来的,构造方法可以看到还有一个引用,意味着有地方在显示调用,那么我们通过查找引用跳过去看看

如何高效地查看开源项目源码?

看来这就是我们要找的了,越来越近了,这里应该是在分析路由模板,那么路由模板应该是调用这个方法的地方传过来的,再次查找引用跳过去

如何高效地查看开源项目源码?

两个地方在调,咋办?明显可以看出第一个地方是单元测试调的,所以其实只有第二个地方在调

如何高效地查看开源项目源码?

Template 就是路由的模板,而这个模板是由 RouteAttribute 特性来得到的,而这个特性是标记在每一个组件的 Type 上的,也就是说,只要获取所有组件的 Type,就可以拿到所有组件对应的路由,这样一来,我的问题解决了

总结

  • 大胆猜测,小心求证
  • 换位思考,如果是我,我会怎么命名,这个常用于寻找分析入口点
  • 英语别太差,不过一般来说你坚持看源码英语会提升的
  • 不要 CTRL + F,不要 CTRL + F,不要 CTRL + F,多用 F12,查找引用
  • 在某些特殊情况下,例如别人都是通过反射调用的,那么就只能 CTRL + F 了

可以看到,解决这个问题的过程几乎全是靠猜,猜错了就回过头去猜下一个,猜对了那就对了。所以如果说某个开源项目的命名太糟糕,那就确实不好找了。当你看源码看多了之后,你就不需要猜了,因为你已经知道命名是啥了。

在这个示例中,我们没有使用转到实现,因为其实这个示例所涉及到的代码还是简单的,没有涉及到太多的多态,因此不需要转到实现。如果涉及到多态,那么过程会复杂很多,因为要看的路径太多,但总归是能看完的,相比用谷歌去搜索半天压根就没有头绪,花点时间猜源码还是值的。

工程师应该学点算法——图论2

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享