闲聊
因为刚开始写公号,不熟练效率不高,我给自己定的时间是,每天抽出三小时来完成公号的工作,不能再多,因为还有工作要做。所以你可能会发现,咦?对比一般的公号又臭又长不一样,你怎么这么短小?短是有原因的,时间有限,我写不了那么长,这是原因之一;原因之二是,这是学习型文章,我翻看大量的技术博客,发现要研究透一篇文章,读者耗费的时间是很长的,需要几个小时,这知识负载太高了,并不是碎片时间阅读能完成的,你也可能就很难坚持。因此篇幅要短,我们从简单的开始做起,逐步形成微习惯。
初期我计划的推送频率是每两天一更,因为是学习型博客,每天学习压力会不会比较大?这是要观察的,另外我自己也还不熟练,产出率不高。
另外,好像前3篇不能打开留言,后面的文章我都会打开留言,你阅读中有任何想法,都可以留言跟我沟通。有个技术的朋友跟我说,你这文章初学者看不懂。我觉得也有可能,因为我们学成者,当走过了学成之路,往往已经淡忘了我们是如何走过来的,所以写的东西初学者可能会看不明白。但我坚持解读“高级经典”的观点还是会坚持,因为入门经典的内容很朴实,其实没什么可解读的,你也就提高不了。学习是困难的,理解不了就多思考,我也会逐步学习用更有穿透性的“第一性原理”来朴实的阐述看起来艰深的道理。
01
中间语言(IL)
.Net中间语言(IL)的特性,很大程度上来自于要支持多语言互操作性。要支持多语言互操作性,是因为微软想搞一个大事情,将它的老产品线VB和VC++,VJ++都装入.Net架构中。要支持多语言互操作,就需要实现这些基本功能:
1)继承:一种语言的类能继承另一种语言编写的类。
2)组合:一种语言的类可以包含另外一种语言的类的实例。
3)调用:一个对象能够调用其他语言编写的对象的方法。
4)传递:值和对象应该能在不同类的方法之间传递(传递数据)。
5)调试:调试器应能支持跨语言调试。
这里特别说明下:继承和组合。我们在学习所谓的“面向对象”的编程的时候,往往会用力过度,觉得对象化编程关键是“继承”,然后就设计出一堆继承关系。然而你要提醒自己,“组合”才是你应该多用的,因为继承观念虽好,但它有限制且复杂,因为.Net,Java是单一继承,而C++的多继承又特别复杂,概念虽美,但我们的目标是:更高效的写代码。举个例子,当你考虑B类继承A类时,考虑一下:B类包含A类,如何?这例子可以类比为:当你要继承你爸爸时,你考虑下,你包含你爸爸和你妈妈?我觉得后者更合适。因为你还要继承你妈妈,所以单一继承你爸爸不合适,而且你还会有你爸爸没有的技能,所以用包含挺好。
回到话题,上述如此宏伟的规划,.Net实现了。这就需要中间语言要提供强力支持。很显然我们可以想到,中间语言必须有相当程度的“对象化”特性,才能很好的支持这一点。我们来看看它的特征:
1)面对对象和使用接口
面对对象相当于为不同语言建立了底层的“基本对象”,使不同语言有了相同的底层基础。使用接口使得不同语言可以遵循统一的“契约”(接口即契约)。
2)区分值类型和引用类型
与其他编程语言一样,中间语言提供了预定义的基本数据类型。它分为值类型和引用类型,值类型存储在堆上,引用类型的实例则存储在堆栈上,堆和堆栈是两种不同的内存区域。
3)强数据类型化
强数据类型化非常重要,它使得在不同语言间的数据传递变得简单而高效。中间语言通过“通用类型系统(CTS)”来预定义通用类型,通过“公共语言规范(CLS)”来确保语言的互操作性。
强数据类型化也使得中间语言可以实现对象化管理,从而实现了.Net的垃圾回收机制。中间语言通过“托管堆”存储对象,当托管堆满时调用垃圾回收器清理对象。
“应用程序域”也是个.Net的一个重要特性,传统的应用程序通过COM进行组件调用,此时应用程序和组件使用的是同一块内存,这容易因为COM组件的Bug而导致整个应用程序崩溃。而传统的通过比如CGI进程的方式来隔离内存,又存在进程开销比较大,进程间数据交换效率低的问题。而应用程序域很好的解决了这个问题。这依赖于中间语言的“强数据类型化”,强数据类型化隔绝了对内存的跨域访问,以及通过强类型化,使得数据可以以“对象”的方式传输,极大的提高了数据交换性能。当然应用程序域很少被应用,我自己几乎没用到,一般只在做类似开发工具Eclipse之类的插件式应用时才需要考虑。
4)使用异常来处理错误
因为运行在中间语言之上的高级语言都使用了异常处理机制,所以中间语言需要支持异常的基础结构,使得异常能够在不同语言间传递必要的调试数据信息。
同Java一样,异常在C#中非常重要。因为历史原因,程序员们看到太多的代码都是使用更老旧的语言编写的,比如C,C++等,在这些传统语言中,一般使用方法的返回值来表示错误,比如返回-1表示错误类型1,返回-2表示错误类型2。这在.Net时代已经过时,我看到太多的人在错误的使用异常机制,还在走老旧的错误处理方式。这真是埋没了.Net优秀的异常机制,也是给自己编程挖坑的典范。记住一句话:你压根儿不应该在.Net框架下,考虑用返回值来表示错误!如果你发现在这么想,那么应该立即反省和思考。.Net下的返回值,只需要用于真正要返回的值,如果你的方法没有想要返回的值,那么方法的返回值就应该是void。
5)使用特性(attribute)
中间语言也支持特性。在传统的C++编写COM时会使用特性,它一般是用于给编译器提供一些额外的信息。中间语言对特性进行了功能的拓展。我们可以自定义特性,可以使用反射来应用特性。这在ASP.Net MVC中体现的非常多,它实现了“面向切面编程”,使得代码能得到更好的复用。
后面将继续讲述“程序集”,“名称空间”等中间语言特性。
我们下回分解。
也欢迎关注本人公众号 “产品技术知与行” ,除了分享以上技术文章,还致力于打造辅助开发的结构化知识库,包括原创文章、免费课程(C#,Java,Js)、技术专题、视野知识、源码下载等内容。