用过的语言按时序算:c++、lua(粗浅脚本)、erlang、c#(unity)、lua、go。印象最深的是erlang,因为特别吃亏,嗯。
那会连多线程、多进程都不怎么了解,虽然看了erlang的进程通信模型,mail_box、gen_server原理,但不理解什么时候要它们。还有递归为主的编码方式,也不大习惯。
……
以上不是本是要说的重点:技术上的东西,时间够,多用用就熟络了,且项目组对新人也有足够的宽容度。
更紧要的是那些“安全性”上的东西,尤其对已发布的项目而言。
自己以往编码,把几乎全部精力放在把握“逻辑脉络”上,切换到新语言后连犯好几回错。一直在想为什么,以为细心、严谨方面的理由不够充分(当然确有些高手真的很细致,能考虑到方方面面~)。
近半年后,某次看着项目里一片片的代码,大家写了那么多,忽然意识到:把业务逻辑写对并不是难事(写好另当别论),那么多人写了那么多……业务逻辑,理清之后在编码中算是比较粗枝大叶的了。可算人人都能写,效率差别而已,且这类逻辑上的错误、疏漏也更容易发现。据实际项目经历看来,外网损失极少是业务逻辑疏漏造成的。什么最多?边边角角的东西——临界值、空指针、参数检验、下标越界、析构回收不充分……这些家伙还比较难在测试中覆盖到,稍不留神就放出去了。
知乎上见到一大神,职称:首席铲屎官。说了段非常精彩的评论,“所谓的出师其实就是把该犯的错误都犯一遍,有人出师快其实是他试错快。如果有人说他没有犯错误,只能说他还没学到家”。(PS:他把重构排为程序员的第一技能,再是代码设计、风格,English)
还碰到客户端同事,自测试一个合作功能老报错,下狠心单步到c++底层,发现构造函数里指针没初始化。致电合作方开发者,沟通不是很顺畅,只听他爆了一句:“你在质疑我的专业性吗?”
霸气十足,当场心动!
很希望自己往后也有资格讲这样的话。
调度回重点:各个语言都有些自己的坑,了解语法后第一该作死的就它们( ̄﹁ ̄)~
比如c++的空指针,STL迭代器的实效,容器遍历it在某个分枝里木有++it,new/delete的恒久匹配,内存操作的越界防范,丢进函数算出个小标再访问数组……
比如看到erlang的并发,就要想到ets里的数据是共享的,大伙都能读写,所以彼此交互时,得gen_server套一层;它函数的多分枝式递归写法,提供了新的死循环形式——同个函数分枝间无限蹦跶(~ ̄▽ ̄~) 啊哈
作为动态语言,变量可匹配任意类型,许多函数的返回带有错误码,但无法从声明中看出。常常顺手一用就忘记判断了。尤其取数据接口,不似c++用指针作返回值,无脑判断空指针即可,形式统一(留个小心眼,所有Get、Find方法都是可能失败的,凡是此语义的函数,都检查返回值)。
再比如lua的table引用传递,当参数传来传去,指不定哪哪改下,原始数据就变了,搞不好还是配置数据。一样有erlang动态语义,检查nil返回的问题……关键是不方便判定哪些函数要检查哇~
c#,变量都是引用,连续使用闭包,触发回调时,很可能捕获的是同一value,上代码
for (int i = 1; i <= 7; ++i) { EventDelegate.Add(uiBtn.onClick, () => SelectOne(i)); //回调时传入SelectOne的参数全是7 }
了解c++11的Lambda表达式,知晓其有“[=]、[&]”两种捕获方式便可理解其原理。修补也就有了针对性:循环体内加一句“int idx = i”,重新生产变量给闭包即可。
最近用的go,发现的一个大坑,range循环,值传递,比lua的ipair危险好多。尤其涉及状态改变时候(比如用框架代码中),循环体内的更改可能都是无效的,出了bug藏得还深( ̄^ ̄),跟同事商议后决定禁止用它了。
最后:各个语言函数库的坑点,彼此差别很大,需额外关注,常用接口自己写代码试试边界情况,无论它多么简单。