看了金旭亮老师的《在.NET程序中正确使用String类型》,不敢赞同其看法。原因如下:
1. String使用不当影响程序性能这一说法并不准确,准确地说是对象分配不当会影响程序性能。
2. 文中没有仔细分析String的属性,随随便便使用一些简单和模糊的程序片断,并不能说明事实,反而混淆读者对概念的理解。
写这篇文章,并不是要诋毁金旭亮老师,而是让他知道自己在教学中可能出现的错误,提醒其及时改正以避免再犯同样错误。是好意不是恶意。我不敢保证篇文章中的技术上的正确性,如有错误或不准确之处敬请指正。
首先说说《在.NET程序中正确使用String类型》的第一节,我并不赞同金旭亮老师所说的,在定义String时内存分配不当会造成程序运行速度的减 慢。过份的内存分配是会造成程序运行速度的减慢,这是正确的观点。金旭亮老师所作的不恰当的是两个例子举得不恰当。这两个例子的运行结果基本上是一样快。 因为两个例子所用的String常量的分配在现在的PC上运行,最多相差几个处理器周期(CPU Cycle),两者的快慢几乎是同样的。这样细微的变化,根本不能影响程序运行性能。所以论据无法有力地证明论点。
在什么情况下,会造成程序运行的减慢?如果一个程序在一次操作中试图给一百万个独特的String常量分配内存,这样的情况下就会产生可能的程序运行速度 减慢。如果一个程序在一次操作中试图给一个50MB甚至100MB的String常量分配内存,假设这一操作可以成功,那么这一操作就会造成程序运行的减 慢。如果一个程序在一次操作中不停地分配新内存然后再遗弃最后造成大量遗弃内存被垃圾收集器收回,这种情况下,程序运行会暂停。我觉得第一种情况和第三种 情况常常会混合在一起(管理级内存堆不断的内存配置和遗弃一定会造成垃圾收集器试探性的回收),这才是一个程序员所要注意到的问题。金旭亮老师在第一节中 举的例子没有说明性,那么读者不必太注意。金旭亮老师在第一节中举的例子还有一个隐性问题,如果是一个QA工程师这样挑拣边边角角的细节问题,和他合作的 开发工程师肯定会感到讨厌,这样细小的问题根本不值得所谓的优化,只有严重的效率问题甚至内存泄漏才是真正需要测试发现的问题。
再看看第二节的内容,我觉得金旭亮老师提供的第二个例子也没有什么说明意义,看第一个代码,三个String(“ab”,“cd”,“abcd”)被创 建,并收入String内存池。这只能说我们进行了三次内存分配。而第二个例子,只有一个String被创建,如果第二个例子需要进行如下操作:
if (s1.indexOf("ab") != -1)
{
...
}
if (s1.indexOf("cd") != -1)
{
...
}
那么这两个程序的内存分配是一样的。也就是说:
(1)String s1 = "ab";
s1+="cd";
if (s1.IndexOf("ab") != -1)
{
}
if (s1.IndexOf("cd") != -1)
{
}
(2)String s1="ab"+"cd";
if (s1.IndexOf("ab") != -1)
{
}
if (s1.IndexOf("cd") != -1)
{
}
两者的效率是一样的,或是两者之间的相差是微不足道的。这说明了少分配两个String常量并不能说明效率的不一样。
说说String常量池,我猜想(我没有看完过任何一本.NET的书,平时只是看看MSDN的帮助文档).NET有个内存池,我知道.NET中的 String类型是恒定数值类型,也就是说,一个String如“Hello World”被创建后,其数值是不可改变的,同时也有一个内存池专门用来装这些已建立的String。当新的变量要指向一个String,先要在这个内存 池里查找是否已经有了同样值的String,然后要么让变量指向这个值,要么新建String值再塞进内存池,最后让变量指向这个新建String值。我 个人的感觉是,使用String并不会太大影响程序运行速度。最终决定某个操作对程序运行速度影响应该是QA工程师的职责,查找并锁定程序运行速度影响的 操作则是开发者的职责。如果一个简单的操作,优化后和优化前的差距细微,这样的优化是浪费时间,是捡芝麻丢西瓜,第一和第个例就是这种问题。
我对金旭亮老师提供的第三个例子是没有异议的,这个例子很好地说明了进箱,出箱的问题。进箱,出箱的问题也是这样,几个小的操作是没有什么大不了的。但是重复成千上万次的代价是非常高的,这里同样也是两个东西:
1. 内存分配;
2. 垃圾回收器对程序运行的影响。
最后一个例子我不说什么了,这个例子没有说清解决的问题,我觉得使用StringBuilder是在避免一个问题,当String + String这样的代码出现时,每一个“+”操作,就会产生一个新的String对象,这样不停地制造对象然后马上在使用完后遗弃可能会造成垃圾回收器工 作,导致程序运行速度减慢。StringBuilder可以进行内存扩大缩小,这样可以减少不必要的对象的建立和回收。但是这是要按照情况来 确定什么时候用StringBuilder和String的"+"操作。金旭亮老师没有特别指出这些情况,那么就等于没有说。
说说代码优化,我见过的所有开发高手(5-15年开发经验,有高级开发工程师,建筑师,高级QA工程师,还有MVP)都对我建议代码只要工作,就不需要进 行代码优化。代码优化有一定的难度,而且很容易引进新的虫子。只要程序运行的速度可以接受,就不需要进行代码优化。要进行代码只有在万不得已的状况下进 行,而且要和其他有资深经历的开发者,和QA一起合作下进行。
我觉得金旭亮老师对.NET Framework的了解还不是很深,所以给出的例子没有什么说服力。既然这样,为何不推荐学员去读一读"Effective C#"?我觉得这本书中写的案例比较有说服力,开发者和QA都该看看。这个例子也可以看出国内教育界的实践和国外的对比有一定距离。现在市面上教人程序设 计的好书大多是国外开发者写的书。少数国外开发者写的书是垃圾这也是现实,但是,大多数国外开发者写的书更容易和现实中的开发联系起来,给读者更好的解决 实际问题的方案。
推荐阅读
- C#调用耗时函数时显示进度条浅探
- 程序设计|IT业比较好的几个社区论坛
- 程序设计思维与实践 Week13 实验
- 完美的开发语言--现在只是梦想
- 中国程序员什么时候才能走出大师崇拜的阴影()
- 程序设计|很想写以下这本书(C#2.0学习心得),就是没有时间
- javascript|在Egret项目中使用protobuf
- javascript|使用TypeScript积累自己的类库