﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-老赵点滴 - 追求编程之美</title><link>http://www.cnblogs.com/JeffreyZhao/</link><description>先做人，再做技术人员，最后做程序员。打造国内最好的.NET技术博客。</description><language>zh-cn</language><lastBuildDate>Wed, 10 Feb 2010 08:06:26 GMT</lastBuildDate><pubDate>Wed, 10 Feb 2010 08:06:26 GMT</pubDate><ttl>60</ttl><item><title>总结：史上第一混乱、程序员的爱情、Nobody &amp;amp; Sorry Sorry</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/play-novel-nobody-sorry-sorry.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Sun, 07 Feb 2010 13:19:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/play-novel-nobody-sorry-sorry.html</guid><description><![CDATA[<p>阅读: 4262 评论: 62 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-02-07 21:19 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/play-novel-nobody-sorry-sorry.html" target="_blank">原文链接</a></p><p>星期六是我的休息日，这天一般我不安排自己做什么和工作有关的事情，一般就是去看看电影，出去逛逛，或者在家上上网吹吹牛。昨天<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/0-bug-and-sjtu-acm-champion.html">总结</a>了一部分由<a href="https://twitter.com">推特</a>上的讨论，现在继续剩下的一部分。不过，这次的内容可能就要和技术或产业略远一些了，其中大部分是我自己的一些体会和感想。现在我打算谈三个东西，一是《史上第一混乱》这部话剧，《程序员的爱情》这本小说，以及Nobody和Sorry Sorry（您不知道这是啥？你成奥特曼啦！）。</p>  <h1>史上第一混乱</h1>  <p>上周五公司开年会，其间我侥幸拿下喝啤酒比赛第一，赢得价值300元的电磁炉一个——这还是整场年会中唯一比蛮力的项目，我那个自豪啊。呃，扯远了，其实下一件事才和我要谈的事情有些联系。那就是后来我又在年会上表演节目，唱了两首歌，因此拿到了昨天晚上《史上第一混乱》话剧的门票。至于为啥是这个话剧，后来我才意识到原来这个话剧改编自盛大文学旗下<a href="http://www.qidian.com">起点中文网</a>上的<a href="http://www.qidian.com/Book/174075.aspx">同名小说</a>，如此说来盛大准备几张该话剧的门票自然就顺理成章了。</p> <img class="floatRight" src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/ssdyhl.jpg" />   <p>昨天下午先去看了场电影，锦衣卫，甄子丹演的，我对于此类动作片的兴趣始终不减，不过看完后倒也没有什么留下什么深刻的印象。看完后吃了晚饭，发现时间尚早，想票子不要浪费了吧，于是便去看了这场话剧。座位不太好，第一排，需要一直仰着头。不过由于距离够近，因此可以清晰地看到演员横飞的唾沫星子，还有演女人的mm身材也不错，腿细胸大的。</p>  <p>提起话剧内容，您可以将其理解为是一个非常单纯的，不包含任何思想在内的，完完全全的无厘头闹剧。总之，就是把最近各种流行的玩艺儿，笑话，网络词，广告串在一起。虽说笑果扑面而来，但听得多了倒也会有些疲劳——因此某些情况下其他观众哈哈大笑的时候我却有点无动于衷。不过也有不少地方挺有创意的，虽说都是耳熟能详的东西，但是在令人意想不到的地方跑出来确实也有眼前一亮的感觉。其中也有很多桥段在为80后的童年生活致敬，如蓝精灵，葫芦娃，黑猫警长，北斗神拳，颇为亲切。</p>  <p>按常理说我不太会喜欢这样的东西，不过可能是由于事先已经了解过这部话剧/小说的背景，再加上“童年回忆”的影响，我倒也并不讨厌这部话剧（但也没推荐啊）。事实上，可能的确是由于平时天天山珍海味，忽然来了一顿粗茶淡饭似乎也别有一番风味。前一段时间<a href="http://blog.tianya.cn/blogger/post_show.asp?idWriter=0&amp;Key=0&amp;BlogID=41917&amp;PostID=19055487">发生了这样一个著名的事件</a>：</p>  <blockquote>   <p>天涯的“天涯时代”版块，本月举办了一次“天涯车友会Logo征集大赛”，参赛作品约30件，最后著名的“27号”勇夺桂冠。再总共3227张投票中，27号作品获得1627票的支持，支持率超过50%，而第二名的得票数还不到它的五分之一。绝对的、毫无争议的、毋庸置疑的众望所归。</p> </blockquote>  <p>而这27号参赛作品的设计是这样的：</p> <img src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/tianya-car-logo.jpg" />   <p>有人认为这是一次“反智主义”的胜利：</p>  <blockquote>   <p>回到我们前面谈到这个“27号”，它为什么表现了反智主义？很显然，在众多的设计作品中，有一些是由“知识精英”创作的，那些作品有他们的设计思想、美学理念，这些都可以统称为一种“知识性”或者“智识性”。而粗陋的27号与之相比基本上没有什么知识性可言，但是它获胜了，这说明，广大的投票者普遍地带有一种蔑视、否定设计知识的倾向，具有一种反智主义的态度，“恶意地”支持这样一个很糟糕的作品。</p> </blockquote>  <p>试想，如果您参与到这样一次投票中去，当每个参赛者都在不断展现他们优秀的设计，不断阐述这些设计背后的理念，原理，思考，含义，您的感觉如何？对我来说，我可能会感到不知所措。似乎现代人常常会有选择恐惧症，例如我去餐馆吃饭，在面临一大堆琳琅满目的选择时往往会不断左右摇摆，这也可以那也不错。此时，我最终可能就会选择那些“无需选择”的东西，例如我会每天去吃套餐，因为餐厅每天都会自行为我进行搭配，我要做的选择可能只是从3个荤菜里选择一个就行了。</p> <a href="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/xyyyhtl-hhsw-1.jpg" target="_blank"><img class="floatRight" src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/xyyyhtl-hhsw-1.jpg" width="300" /></a>   <p>其实在知识扑面而来的现在，想要寻求一种纯粹的宁静的时刻也并非那么容易。最近还有一部动画电影非常火爆：“喜羊羊与灰太狼2：虎虎生威”，这部原本纯粹为儿童准备的动画片似乎也吸引了许多成年观众——包括我。在我看来，这部动画片与众不同的地方便是在众多商业大片的包围中显得如此富有童真，如此轻松愉快。它没有“2012”和“阿凡达”的惊人画面，甚至也远不如日式动画片中精美成熟的效果，但是在看这部片子的时候我不会想到玛雅文明与地球毁灭，强拆与钉子户，或是动画水平差距与<a href="http://www.google.com/search?hl=en&amp;source=hp&amp;q=%E8%9C%A1%E7%AC%94%E5%B0%8F%E6%96%B0+%E6%8A%84%E8%A2%AD&amp;aq=f&amp;aqi=&amp;oq=">大嘴巴小新</a>——看过，笑过，忘掉，然后带着轻松的心情继续工作，继续生活，这可能也就够了。</p>  <p>简单点，有时也没什么不好的。</p>  <h1>程序员的爱情</h1>  <p>诚蒙机械工业出版社朋友们的垂青，他们会不定期地，在我丝毫不知情的状况下给我寄一些书来看。惊喜之余，这样做的毛病也就体现出来了——他们把书寄到我上一个公司的地址去了，因此我也才刚拿到这两本书：《<a href="http://www.douban.com/subject/4196673/">程序员的爱情</a>》和《<a href="http://www.douban.com/subject/4188128/">林立立成长记</a>》。</p> <a href="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/programmer-love.jpg" target="_blank"><img class="floatRight" src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/programmer-love.jpg" width="150" /></a>   <p>我昨天出门前捎上了这本书，在路上将这本300页不到的书从头到尾匆匆翻了一遍。总体来说，我觉得这本书没什么价值。</p>  <p>为什么这么说呢？因为在我看来，这本书仅仅是打着的程序员的旗号而已。其中的男主人公的确是一个程序员，但是几乎没有对程序员的工作，程序员的生活有太多描写。换句话说，我们可以很轻易地将主人公改写为其他的职业——“程序员”在书中仅仅是一个标记，一个可有可无的东西，也并没有对小说的内容产生什么影响。在书的封面中写着“告诉你收获爱情的秘密”，但是书中男主人公最终的归宿，是和原本就一直喜欢他的海归漂亮姑娘结婚，而他们在一起的“方式”居然是因为男方失恋酒后乱性……哪儿有什么秘密啊，唯RP耳。</p>  <p>其实有时候我也怀疑作者是否真的是一个程序员（当然作者简介里给出了肯定的答案啦），因为对于程序员生活的描写很少，又基本是停留在表面，因此我觉得写出这些内容并不需要有亲身体会——从一些表面渠道来了解就行了（例如找个程序员朋友聊聊天，听听他们的抱怨）。此外，书中还有一些不符合实际的内容，例如第16章的开始：</p>  <blockquote>   <p>陈旭（男主人公）被提拔为项目组长后，主要负责协调和管理方面的工作，不再需要他编写代码了，但他依旧保持着对学习技术的巨大的热情，坚持要求自己每天写几百行代码。</p> </blockquote>  <p>我看到这“几百行”后立刻就被雷翻了。</p>  <p>这部小说是一个普通的故事，没有华丽的词藻，没有跌宕起伏的剧情，甚至包含一些奇怪的情景——例如男主人公的好友莫名其妙地车祸身亡，这似乎没有任何意义。但也正是这样的“普通”，我很愿意相信这个故事有个真实的蓝本，如果作者的确是一个程序员而不是个职业写手，如果作者并非是为了名利而是为了兴趣进行写作，那么我对于这部小说和作者本人还是非常尊敬的。</p>  <p>虽然我还是不喜欢，如果我是编辑，也不会出版这本书。如果您对它感兴趣，我觉得您可以去书店里捧着这本书看2个小时，或是先在CSDN上<a href="http://blog.csdn.net/theloveofprogrammer">浏览其连载</a>。</p>  <p>不知道什么时候开始流行起程序员小说了，例如之前有比较著名的《疯狂的程序员》，现在又出了这两本书。这两本书看起来还是挺受程序员群体追捧的。但是我读下来其实感觉都不怎么样，难道是因为程序员群体看到“亲切”的内容因此产生了好感吗？对于我在推特上对《程序员的爱情》的看法，图灵出版社的主编刘江老师评论到：</p>  <blockquote>   <p>其实小说比较代码难写多了，是门高超的技术，可惜有些出版社和某些作者就是不信。</p> </blockquote>  <p>我也很同意陈猫（@catchen）的说法：</p>  <blockquote>   <p>向人比較准確無誤地傳達信息，比向電腦准確無誤地傳達信息，要難得多，因為向人傳達嘅信息需要包括情感。程序員習慣無需向電腦傳達情感，所以……</p> </blockquote>  <p>当然，我还是十分建议程序员朋友们总结自己的所做所想，也算是锻炼自己的表达能力。事实上表达能力是优秀程序员的基本素养之一。呃……其实我觉得这句话有些奇怪，因为这本应该是每一个正常的“社会人”所具备的能力，但是慢慢地程序员似乎变成了木讷、不善言辞的群体。当然我遇见过许多不善表达的的程序员，他们可能连一个简单的问题也表达不清楚，但我也认识不少善于表达的程序员。</p>  <p>例如，我现在在盛大创新院的同事们，他们大都是很有想法，并乐于表达自己观点的人——甚至我觉得有时候这样的人太多，聚在一起时反而会有烦恼。上周在进行集体讨论的时候，要不是由主持人进行控制，一个人说着说着就很可能就停不下来了……我这个准话唠真有些插不上话的感觉。</p>  <p>兄弟们一定要锻炼好自己的表达能力（<a href="http://www.programmer.com.cn/1798/">Joel也这么说</a>）——而写写小说可能也是不错的方式。</p>  <h1>Nobody &amp; Sorry Sorry</h1>  <p>最近是各公司的举行年会的日子，每个公司年会上的节目自然大不相同。但是，如果把所有公司年会的节目取一个交集，您会发现必然会涉及到一个节目，那便是一个叫做Nobody的东东。</p> <a href="http://images.cnblogs.com/cnblogs_com/JeffreyZhao/168980/o_wg_nobody_pic1.jpg" target="_blank"><img src="http://images.cnblogs.com/cnblogs_com/JeffreyZhao/168980/r_wg_nobody_pic1.jpg" width="200" class="floatRight"/></a>   <p>如果您还不知道啥是Nobody，那么您实在是奥特曼了（如果您也不知道奥特曼是啥意思……）。话说由棒子国的几个“卖馄饨的小姑娘”组成了一个团体，<a href="http://www.youtube.com/results?search_query=Sorry+Sorry&amp;search_type=&amp;aq=f">Wonder Girls</a>，而她们最被人熟悉的单曲便是<a href="http://www.youtube.com/results?search_query=Wonder+Girls+Nobody&amp;search_type=&amp;aq=f">Nobody</a>。这首曲子已经被翻译为多种文字，这些小姑娘们也已经在全世界转了一大圈（去年底也来过上海）。当然，我一直觉得棒子文没啥好听的，而Nobody的流行也不是因为音乐或歌词，而是——扭啊扭，基本上就是用来诱惑男人的舞蹈。</p>  <p>有时候我们不得不佩服棒子国的编舞能力。Nobody能够在世界范围内的成功不是偶然，也不是靠着强大的整容技术搞出一个个近乎完美的身材相貌（许多朋友看“<a href="http://www.youtube.com/results?search_query=girls+generation&amp;search_type=&amp;aq=f">少女时代</a>”就是去看大腿的）。这方面另一个佐证便是<a href="http://www.youtube.com/results?search_query=Wonder+Girls+Nobody&amp;search_type=&amp;aq=f">Sorry Sorry</a>，而且这次是一群男人。现在Sorry Sorry的流行程度已经不亚于Nobody，不知道吸引了多少小女孩们的视线。而我看了<a href="http://www.youtube.com/watch?v=bqMq82S7OVQ&amp;feature=related">它的MV</a>也不得不承认，这帮小子真tmd的很帅。</p>  <p>如果你还是对它们的流行程度没有概念的话，看看菲律宾监狱集体版的<a href="http://www.youtube.com/watch?v=iVkKBHCVVbg">Nobody</a>和<a href="http://www.youtube.com/watch?v=_43TO_OPj-8&amp;feature=related">Sorry Sorry</a>吧——记得接着下巴哦。</p>  <p>当然，要说舞蹈技术，看过<a href="http://so.youku.com/search_video?q=%E8%88%9E%E6%9E%97%E4%BA%89%E9%9C%B8&amp;searchdomain=http%3A%2F%2Fso.youku.com&amp;searchType=video&amp;sbts=bar">舞林争霸</a>的人根本不会觉得棒子们有什么厉害的——那群百老汇级别的牛人们是真正的舞蹈家，他们的力量，技巧都非一朝一夕之功。<a href="http://v.youku.com/v_show/id_XMTQ3MjUwNjM2.html">他们的舞蹈</a>更令人震撼，但是远没有Nobody和Sorry Sorry那么流行，甚至永远不可能像后两者那样被那么多人模仿。道理很简单，因为棒子艺人的舞蹈能力有限，因此编舞只能设计一些技巧性不那么强的舞步，因此许多业余人士，学生，老师，警察，程序员……人人都能模仿，人人都能“传跳”。</p>  <p>其实这样的二分选择还有很多，各个行业都是如此。例如，李斯特的曲目在当时几乎只有他才能演奏（现在音乐学院的学生几乎个个不在话下），这给他带来了无限声望；而肖邦的许多作品在技术上容易许多，也更被人广泛接受——尤其是在我等业余人士中。那么，如果是您，您的选择是什么呢？</p>  <p>呃，Sorry Sorry的确不错，有机会我也要尝试一下。</p><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1665473.html?type=1" width="1" height="1" alt=""/><p>评论: 62　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/play-novel-nobody-sorry-sorry.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/play-novel-nobody-sorry-sorry.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>总结：0 bug事件、交大ACM夺冠</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/0-bug-and-sjtu-acm-champion.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Sat, 06 Feb 2010 19:51:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/0-bug-and-sjtu-acm-champion.html</guid><description><![CDATA[<p>阅读: 7651 评论: 54 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-02-07 03:51 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/0-bug-and-sjtu-acm-champion.html" target="_blank">原文链接</a></p><p>如果您还没有上<a href="http://twitter.com/" target="_blank">推特</a>（或是还没有想到办法上），那您失去的不光是一个风靡世界的网络应用，您同时还失去了一个优秀的信息获取平台及讨论环境。目前国内大部分靠谱的技术专家都在推特上有帐号，您几乎不会错失任何有价值的信息，更可以<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/03/twitter-talk-about-ms-dev-at-20100201.html" target="_blank">参与一些有价值讨论</a>。周六是休息日，因此也在推特上和大家聊得不亦乐乎。不过推特的一个缺陷可能便是难以留住有价值的信息，它们会随着时间推移渐渐消失，因此还是及时记录一下吧。</p>
<h1>0 bug事件</h1>
<p>0 bug事件原本什么都不是，只不过是有人使用非常普通、就事论事地方式，在豆瓣上指出了《0 bug》一书中不妥的地方。但是由于作者令人费解地谩骂和指责使&#8220;事态升级&#8221;。再由于网络上围观群众（<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/01/logic-is-not-everything.html" target="_blank">其中也有我的份</a>）与作者本身不断地进行&#8220;交锋&#8221;，0 bug事件终究一发不可收拾了。</p>
<p>其实我对这件事情已经不太在意了，因为我本来就是围观群众，而且目前0 bug事件已经出离了纯粹的技术讨论。不过现在我继续谈这个问题，是因为直接参与此次事件的双方又有新的动作了。首先是大牛<a href="http://www.cnblogs.com/miloyip/" target="_blank">Milo Yip</a>已经在园子里建立博客了，他的原话是希望&#8220;<font color="#ff0000">將會陸續把牆外的技術文章整理及分享</font>&#8221;，不过第一篇文章是<a href="http://www.cnblogs.com/miloyip/archive/2010/02/06/1665099.html" target="_blank">对0 bug事件的过程进行了系统的总结</a>；当然<a href="http://blog.csdn.net/tonyxiaohome/" target="_blank">0 bug</a>老师也没有闲着，他这几天还在勤劳地进行辩解。不久前又转载了一篇雄文《<a href="http://blog.csdn.net/tonyxiaohome/archive/2010/02/05/5291216.aspx" target="_blank">五毛党与网络打手</a>》，从始至终地坚持他的观点：此次事件是&#8220;敌对势力&#8221;在对他进行&#8220;有组织，有预谋&#8221;的抹黑。</p>
<p>而事情发展至今又有新的亮点，那便是CSDN的编辑同志们将此次事件相关的文章放上了首页。在此截图留念：</p>
<img src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/csdn-0bug.png?time=1265480825768" alt="" />
<p>哈，前两篇都是。<a href="http://blog.csdn.net/Raptor/archive/2010/02/05/5290875.aspx" target="_blank">第一篇文章</a>指出了0 bug老师在此次事件中的问题所在，其中一点正好回应了0 bug老师的观点：</p>
<blockquote>
<p>按我以前接触过的国内出版业人士来看，肖老师在BLOG里说的&#8220;书黑&#8221;这种事情在中国的确很可能发生。但是他的错误在于，把所有提意见的读者都当成了 书黑，这未免有点被迫害妄想过头了吧。要知道，请一帮网络黑社会也是要花不少钱的，一个编辑做一本书赚的钱估计是不够的，更何况目标还是这样一技术含量比 较高的书。 </p>
<p>其实光是从Milo指出的几个问题来看，他就不可能是什么书黑，哪有C++水平这么高的书黑&#8212;&#8212;要是C++功力这么深还当什么书黑啊，书黑能赚几个钱。而作者连这点都看不出来，显然他的C++功力也不怎么样，以此推断，那本书显然也的确不怎么样。</p>
</blockquote>
<p>而<a href="http://blog.csdn.net/tonyxiaohome/archive/2010/02/05/5290195.aspx" target="_blank">第二篇文章</a>的作者正是0 bug老师本人，其主要意思是指目前指出的问题似乎都有&#8220;无中生有&#8221;的感觉，因为这些&#8220;攻击&#8221;根本没有找准位置。</p>
<p>0 bug老师是<a href="http://student.csdn.net/" target="_blank">CSDN学生大本营</a>的头牌，因此CSDN的态度对于这个事件的意义重大。而这次CSDN首页推荐的作法，我个人看来已经是一种表态了。因为这两篇文章，无论是对&#8220;技术&#8221;及&#8220;产业&#8221;来说都几乎没有意义，推荐它们的原因似乎只是为了揭露0 bug事件本身。很明显，0 bug事件对于CSDN来说并不光彩，而CSDN的编辑同志没有遮掩，将其公开地表达出来，我想他们在这个问题上的确值得我们尊敬。</p>
<p>就我的看法，0 bug事件可以说是继《<a href="http://www.cnblogs.com/jeffreyzhao/archive/2009/09/23/1572868.html" target="_blank">JavaScript征途</a>》之后又一个和技术图书出版有关的经典案例了，两者的相同之处在于作者对于读者所指出的&#8220;硬伤&#8221;都采取了类似的态度。上次的事件似乎已经平息，只有<a href="http://www.google.com/search?hl=en&amp;ei=_8ttS5DKBsyIkAW4zrHUBw&amp;sa=X&amp;oi=spell&amp;resnum=0&amp;ct=result&amp;cd=1&amp;ved=0CBcQBSgA&amp;q=JavaScript+%E5%BE%81%E9%80%94&amp;spell=1" target="_blank">搜索引擎中的头条</a>清楚地记录着相关内容。我真的希望图书出版行业的这类经典案例可以少一些&#8212;&#8212;亦或是多出现一些，以此推动一个革命，最终改变目前令人难堪的现状？</p>
<p>在推特上，<a href="https://twitter.com/jasonlai" target="_blank">@jasonlai</a>同学发表了一些看法：</p>
<blockquote>
<p>老话，咱圈真乱。另外，国内技术媒体和出版圈子还是需要提高一下道德底线啊，出书审校不负责任，写软文换赞助的事情比比皆是。最近小关注了一下<a href="http://www.techcrunch.com/2010/02/04/an-apology-to-our-readers/" target="_blank">TechCrunch炒掉写软文实习生的事件</a>，感触颇深，能那样做才是受尊重的媒体。</p>
<p>仔细看了某口水战事件的讨论始末。有知识没文化真恐怖啊，某&#8220;大师&#8221;的气度，<a href="http://www.danielbru.com/2010/02/the-line-was-crossed/" target="_blank">还不如一个17岁小孩</a>。</p>
</blockquote>
<p>说到这里，我想起在我的博客上有朋友回复说，写《JavaScript征途》的朱老大最近又有一本新书《<a href="http://www.china-pub.com/49015" target="_blank">Flash CS4基础与案例教程</a>》出版发行了。我在这里谨代表我个人（请注意，不代表其他人哦）表示恭喜，并对国内有如此高产的作者感到自豪。让我们欢呼吧！</p>
<h1>交大ACM夺冠</h1>
<p>交大牛啊，继2002及2005年之后，他们<a href="http://news.csdn.net/a/20100205/216923.html" target="_blank">再次获得了ACM竞赛的世界冠军</a>！想当年我也在信息学奥林匹克竞赛的道路上探索过，但由于直升大学时被交大拒绝，一气之下报考复旦大学，从此改变了我的人生轨迹&#8212;&#8212;呃，扯远了，没别的意思，还是回到这则新闻上来。</p>
<p>不过，奇怪的是，似乎很多人的对这个事件还是发表了许多在我看来毫无道理负面评价（当然并不是所有的负面评价都是没有道理的，例如说ACM夺冠对于国内普遍现状几乎没有帮助，这我同意）。具体地就不多提了，您可以自己去看新闻后面的回复。我想说的是，我这次真的想用&#8220;喷子&#8221;来形容某些人了。</p>
<p>的确，国内大学教育是有很多问题，你们的失望我可以理解，但是为什么那么倾向于&#8220;轻视&#8221;一个东西？的确，交大的弟兄们是非常重视ACM竞赛，但是他们的确通过刻苦训练拿到了世界冠军，这一点不假。嗯，你可能会说&#8220;ACM这种比赛没有什么意义，考题海战术取胜，胜在中国人最擅长的应试教育，否则为什么拿了那么多次世界冠军，那些搞ACM的人最终成功的有几个？&#8221;那么我们现在就来讨论这个问题吧。</p>
<p>其实以前也有过类似的问题，便是&#8220;中科大少年班&#8221;。有许多人质疑对此类少年天才的培养方式&#8212;&#8212;的确，可能这种方法自有其不科学之处，但是类似于&#8220;除了张亚勤之外又有谁成功了&#8221;这样的论据我是非常不屑与之辩驳的。对此我只想问几个问题：成功的标准是什么？所有成功的人你都认识吗？</p>
<p>这里我不想谈什么&#8220;金钱不是唯一标准&#8221;等会被人耻笑为&#8220;幼稚&#8221;的道理，我们就拿钱来说吧。我举个例子，世界上有许多大型银行或是企业，他们的总裁，或是副总等高管都是有钱人，可谓非常成功，但是你现在能够说出其中几个人的名字？再问，就拿微软来说，除了盖兹，鲍尔默等人，你又能说出多少人呢？微软的<a href="http://www.microsoft.com/presspass/exec/techfellow/default.mspx" target="_blank">Technical Fellows</a>以及<a href="http://www.microsoft.com/presspass/exec/de/default.mspx" target="_blank">Distinguished Engineers</a>也都很成功（即使只谈钱），但是这几十人中你又认识多少人呢？</p>
<p>是啊，成功的人太多了，不能因为你没有听说过就&#8220;认定&#8221;他们不成功。事实上，无论是ACM冠军选手或是中科大少年班的成员，他们中的大部分在社会地位还是经济方面都是普通人难以项望其背的。只不过，他们没有让你们知道而已。例如，他们可能出国深造，成为顶尖的科研人员或是教授，享受着几十万美元的年薪，过着滋润的生活。你可能连Jim Gray都不知道是谁，不知道他们不是很正常的事情吗？</p>
<p>其实我在学校的时候也一直关注ACM成员，事实上我就没有发现有哪个ACM高手是写不好程序的。</p>
<p>Oren Eini写过<a href="http://ayende.com/Blog/archive/2009/12/12/setting-the-record-straight-i-am-not-the-main-contributor.aspx" target="_blank">这么一篇文章</a>，其中心思想是：</p>
<blockquote>
<p>I am not the main contributor (of NHibernate), I am just the loudest one.</p>
</blockquote>
<p>这句话的说法一点没错。就拿我来说，我<font color="#ff0000">不是</font>最好的.NET程序员，我只不过是在博客园积分最多，并且可能最积极参与社区活动而已。如果要正确定位我在社区中的表现，也只（可能）是loudest，而远远谈不上，甚至永远不可能是best。要在技术社区中产生正面的影响力，水平只是一方面，而&#8220;其他某些方面&#8221;也是非常重要的。记得网络红人<a href="http://blog.sina.com.cn/ayawawa" target="_blank">ayawawa</a>说过这么一句话：</p>
<blockquote>
<p>比我漂亮的都没我聪明，比我聪明的都没我漂亮。</p>
</blockquote>
<p>那么我也来说一句类似的：</p>
<blockquote>
<p>比我会吹牛的技术都没我好，技术比我好的都没我会吹牛。</p>
</blockquote>
<p>所以，我也就是一个小样儿而已&#8212;&#8212;但是我还是自信比ACM事件中大部分无理由的喷子们要强上不少。</p>
<p>五毛与无美分无异。</p>
<img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1665244.html?type=1" width="1" height="1" alt=""/><p>评论: 54　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/0-bug-and-sjtu-acm-champion.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/07/0-bug-and-sjtu-acm-champion.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>李笑来激起千层浪，赵姐夫力拒众强敌</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/03/twitter-talk-about-ms-dev-at-20100201.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Tue, 02 Feb 2010 16:49:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/03/twitter-talk-about-ms-dev-at-20100201.html</guid><description><![CDATA[<p>阅读: 7525 评论: 162 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-02-03 00:49 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/03/twitter-talk-about-ms-dev-at-20100201.html" target="_blank">原文链接</a></p><p>昨天晚上，<a href="http://www.lixiaolai.com">李笑来</a>（<a href="https://twitter.com/xiaolai">@xiaolai</a>）老师的无心之语却引起了推特上一次前后长达1个多小时的讨论——当时他似乎只是随手发了一句“Apple告诉我们的铁律是：表面功夫一定要做足”便不见了踪影，但是这句话立即引起了众果粉的共鸣。此后，我（<a href="https://twitter.com/jeffz_cn">@jeffz_cn</a>）的一句评论又引起了众人对微软开发平台的批判之声。在这次讨论中，几乎只有我孤军奋战为.NET平台进行辩解。因此事后有人给出一副对联为此次争论作出总结：</p>  <blockquote>   <p>上联：李笑来激起千层浪</p>    <p>下联：赵姐夫力拒众强敌</p>    <p>横批：全民扯谈</p> </blockquote>  <p>自然，无论是我还是其他人，在参与讨论的时候都抱有明显的个人倾向性。不过与常见的吵架不同，虽然大家观点向左，但是并没有任何谩骂的成份，同时也没有假惺惺的客套话。可以说所有人从头到底都保持着就事论事，据理力争。因此从旁观者的角度来看，这次讨论并非只是意气之争，其中还是包含了比较丰富的内容。</p>  <p>参与讨论的<a href="http://blog.devep.net/virushuo/">霍炬</a>（@virushuo）和<a href="http://tiny4.org/myhome/">郝培强</a>（@tinyfool）都是老程序员，他们在上世纪末也都是微软平台的开发人员，但是因为难以忍受微软在那时候“毫无克制”的技术更迭（如VC =&gt; COM =&gt; .NET），最终一前一后都转投了*nix平台。我昨天谈到，我<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/02/use-mac.html">加入水果党的主要原因</a>之一是想了解苹果机的妙处究竟在哪里，而他们两位便是让我产生这一想法的重要因素。而我，由于入行较晚，虽然“从理论上”说也经历过这一历史阶段（如VB，Delphi，以及Java开发环境混战的那一时期），但是在真正全身心投入微软平台时已经是.NET时代了，因此对于霍郝两位的观点并没有切身体会，而我坚持的观点便是：.NET平台发布至今并没有“革命性”的改变，而目前也可以看出微软已经在.NET平台上投入了未来5年甚至10年的心力，因此如今.NET程序员并不用担心遭遇当年的悲剧。</p>  <p>从这次讨论中可以了解到一些老程序员对（当年）微软开发平台的一些典型看法，这些看法放到现在究竟正确与否我认为并不重要，重要的是我们能够从中总结出哪些信息，这些信息又可以如何对我们将来的发展产生借鉴意义。因此，我详细地总结了这次讨论的完整内容——不过毕竟是人肉整理，不排除遗漏少量条目的可能。因此，我建议您可以上一下推特，follow一些人，这样下次再出现有价值的讨论也不会遗漏了。</p>  <p>由于讨论内容较多，我还是把它们放在下面的链接中了。其中，缩进代表了“回复”关系，但是由于推特的谈话性质，条目的上下位置并不表示发表时间的先后。</p>  <p><a href="http://docs.google.com/View?id=dgpjrmdf_141cgbw5gfx">http://docs.google.com/View?id=dgpjrmdf_141cgbw5gfx</a></p><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1662320.html?type=1" width="1" height="1" alt=""/><p>评论: 162　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/03/twitter-talk-about-ms-dev-at-20100201.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/03/twitter-talk-about-ms-dev-at-20100201.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>从今日起正式加入水果党</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/02/use-mac.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Mon, 01 Feb 2010 16:08:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/02/use-mac.html</guid><description><![CDATA[<p>阅读: 4143 评论: 66 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-02-02 00:08 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/02/use-mac.html" target="_blank">原文链接</a></p><p>想来我现在的笔记本也用了将近3年，虽然至今没有出现过任何问题，但更新换代似乎也不是件让人无法接受的事情——再加上老爸正好也需要一台笔记本，于是我想，不如就乘此机会升一下级吧。不过其实我本没有打算那么着急，只是两天前忽然出现了一个千载难逢的机会，一台十分便宜的MBP放在我面前，于是我当机立断，即刻入手。因此我在此庄严宣布：从今日起，我正式加入水果党。</p>  <p>以下是我今天新到的Mac Book Pro（<a href="http://search.taobao.com/search?commend=all&amp;isnew=2&amp;q=MB471&amp;source=search1">MB471</a>）。</p> <a href="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/mac/mbp.jpg"><img src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/mac/mbp_s.jpg" /></a>   <p>有不少朋友问我：你怎么放弃.NET了？更有一些*nix界（不好说是自由/开源界，毕竟Mac也是个封闭的平台）的朋友认为，作为一个微软MVP，又是在国内.NET社区小有影响的人物忽然转投*nix平台，这又是微软平台衰败的一个证据。当然，我没想这么多。我只是觉得：好玩。</p>  <p>唉，毕竟Windows平台上玩了这么多年，虽然不可能什么都玩遍，但多少还是有些厌倦了。再者，的确有很多东西在Windows平台上玩起来不是那么方便。例如.NET平台的跨平台实现mono，如果没有一个非Windows平台作为运行环境，想要真正玩好也是十分不容易的——新的工作环境让我有机会验证一些想法，我又怎能不牢牢把握住这个机会呢？一直有人坚持认为mono只是个玩具，那么也就让我亲自尝试一下吧。至于其他平台，如Java、Ruby、Python亦或是Haskell等等，自然就更不在话下了——再者，如果我要尝试Mac平台开发的话，也只能用Mac了……</p>  <p>谁说Windows平台的程序员视野狭隘呢？我就爱玩各种东西。</p>  <p>不过，仅仅是从这个角度来讲，我买一台PC装一个Linux操作系统，或是用虚拟机也可以达到相同的目的。可惜总能听到一些搞技术的朋友对我说Mac有多么多么好，Windows有多么多么差。对于这样的言论，我当然不服气了。但是我既然没有用过Mac，又能如何给出令人信服的反对意见呢？因此我也想乘此机会体会一下Mac系统的优秀之处，无论它是否真的优于Windows，我终究会有一个理性认识。爱吵架的兄弟们，先等我个一年半载啊。</p>  <p>当然，我肯定还是会装一个双系统跑个Win 7什么的，因为我实在离不开可恶的网银和可爱的游戏。</p><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1661404.html?type=1" width="1" height="1" alt=""/><p>评论: 66　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/02/use-mac.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/02/use-mac.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>光有逻辑也是万万不能的</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/01/logic-is-not-everything.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Sun, 31 Jan 2010 16:14:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/02/01/logic-is-not-everything.html</guid><description><![CDATA[<p>阅读: 6188 评论: 106 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-02-01 00:14 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/01/logic-is-not-everything.html" target="_blank">原文链接</a></p><p>之前我多次强调“逻辑”在讨论问题时的重要性，我也多次表示说，如果大家都运用合适的逻辑思维来讨论问题，那么论坛上8成吵架可以避免。现在我也想再次探讨一下这个话题，只不过这次我想反过来说：光有逻辑也是万万不能的。除了“逻辑”这个东西之外，讨论问题时还有很多其他东西值得注意。</p>  <p><a href="http://www.lixiaolai.com">李笑来</a>老师是国内最著名的“讲道理”倡导者之一。前一段时间对于“孔子”这部电影，他在推特上说了这么一句话：“坚决不去电影院看《孔子》的请举手！”。于是我和其他一些朋友就问到：“发生什么了？”不久，笑来老师就在博客上<a href="http://www.lixiaolai.com/index.php/archives/8588.html">谈了这个问题</a>，摘录如下：</p>  <blockquote>   <p>“抵制”的意思是说，有一样东西对你是有吸引力的（不管好坏），但是，你不要它。比如“抵制日货”，“日货”挺不错的，但是，基于某种原因有些人就“抵制”了它。抵制“法货”也是如此，抵制“诱惑”亦如是。</p>    <p>我们只是纷纷表示自己不会去看，这不叫“抵制”，只是“表态”——犯得着去抵制一个连诱惑都不是的东西吗？它甚至连日货法货都不是。甚至没有“号召”的意思，“号召”的意思是说，给别人一个信服的理由，令他们做出与原本可能不一样的决定。</p> </blockquote>  <p>笑来老师的意思是，他只是在表态，他不是在号召大家不去看。从字面上看，“请举手”的确没有在“号召”，例如“喜欢吃红烧肉的请举手”，相信没有人会认为这有其他意思。但是，为什么包括我在内的一些朋友就认为笑来老师是在号召大家不去看这部电影呢？仔细思考之下，我认为可能是这两个原因。</p>  <p>首先，在此之前笑来老师说过或转推过一些对于“孔子”电影的反面意见。在大部分情况下，某个人说的话都是有上下文的，在不同的上下文说同样的话，理解其起来也不一样。为什么说某些媒体没有道德？其中一个表现便是在转述别人的话时剥离上下文，以此曲解别人的意思，甚至让意思完全相反。因此，虽然经过分析之后，我能够理解笑来老师的确没有“号召”的意思，但在当时，这句话的确给人以不一样的感觉。</p>  <p>其次，这句话的表述是反面的，此外其用词比较强烈，如“坚决”。人类的语言是神奇的，一句话除了“最表面的意思”之外，其说话方式、用词的不同，都会影响别人对这句话的理解——这也是语言表达的非常重要的一部分。例如一个多月前在图灵俱乐部中，有朋友认为现在许多文字或翻译越来越不简练了，他认为“我和你要做一次私下的谈话”是典型的英文式中文，应该“我想和你私下谈谈”。但是，<a href="http://ozzzzzz.javaeye.com/blog/">o6z</a>的回复反驳了这个看法，认为“这个例子举的太差，这两句话的语气和目的差别太远。其实类似的表达还有很多，但是里面的含义却大有不同”：</p>  <ul>   <li>我和你要做一次私下的谈话----将来做一次正式的但是非公开的谈话。</li>    <li>我要和你做一次私下的谈话----现在马上就要跟你做一次正式的私下谈话。</li>    <li>我想和你私下谈谈----我想找个机会私下和你谈谈，更加具有私人性质的交谈，而不是上面两句更加具有工作或者官方色彩。</li>    <li>我想和你谈谈，私下的----直接的表达出我要跟你聊点不适宜公开的事情，有要搞阴谋的味道。</li>    <li>找个时间咱俩聊聊吧----更多的是客气话，希望拉近关系。</li> </ul>  <p>最近还有一个非常著名的例子便是“Google离开中国事件”。当时我说：“事情闹这么大，Google要么离开被人看作爷们，要么留下给人看作笑话”。最后的结果大家应该也就见到了，这几乎就是一场闹剧。这次事件继续在给Google的“不作恶”原则抹黑。但是也有人说，Google本来没什么啊，都是媒体炒作害苦的。没错，Google从头到底没有说“一定离开中国”，它只是说“考虑离开中国”。从逻辑上看，它最终选择留下并没有违背它说过的话，因此也不会有人追究它的责任（如，罚款）。但是，为什么似乎大部分人（还是我自动过滤了意见不同的看法？）觉得Google这次“吃了吐”呢？这就是它的“行为”和“说法”给人的“感觉”，在这方面“逻辑”不是唯一的评价依据。</p>  <p>这也是为什么有了“逻辑”还是会发生“争吵”的原因，尤其是在技术圈子里。因为，即使每句话都包含逻辑，许多话“还是不那么好听”，给人的感觉还是“话里有话”，慢慢大家的情绪都调动起来，争论难以避免。此外，在技术圈里，大家都是聪明人，能够很轻易地发现平时交流中对方话语中的问题——毕竟，例如论坛发帖时很少有人会字斟句酌。于是，我反驳不了你的“理由”，我反驳你的“说法”不也可以吗？只可惜，我们只有如“法庭辩论”这样的环境下才需要如此运用“逻辑”。平时，有必要吗？</p>  <p>此外，“逻辑”也可以用来“诡辩”，无理也可以说成有理——大家都是聪明人，这方面的能力总是不缺的。以下便是一例：</p>  <p>上个星期还发生了“0 bug门”事件：。简单地说，那是因为有人对于《<a href="http://www.douban.com/subject/4149139/">0 bug——C/C++商用工程之道</a>》写了一个书评，<a href="http://www.douban.com/review/2949973/">指出书中的一些问题</a>。于是作者，<a href="http://blog.csdn.net/tonyxiaohome">肖舸</a>（后文称o bug老师）不干了，认为评论者是故意来抹黑，认为评论者能力不够。但事实上，评论者<a href="http://cn.linkedin.com/in/miloyip">Milo Yip</a>的批评完全只是针对书上的内容，语气也非常委婉，反倒是0 bug老师在不断对评论者进行谩骂——并一再坚持评论者公开身份。后来，大家发现Milo其实是大牛，因此对其能力不足的的说法也不攻自破了……不知道当时0 bug老师心里是什么滋味。</p>  <p>那么现在结果怎么样呢？没怎么样，0 bug老师已经删除了自己的所有回复。只可惜，Google快照功能保留了一切，而这些结果也已经被<a href="http://blog.csdn.net/lanphaday/archive/2010/01/27/5260427.aspx">截图留档</a>。但即便如此又如何呢？0 bug老师还是义正词严地<a href="http://blog.csdn.net/tonyxiaohome/archive/2010/01/28/5265573.aspx">发表了一篇文章阐述了自己的不公遭遇</a>——都是聪明人，这方面的能力总是不缺的。再由于“谁的地盘谁作主”，0 bug老师的博客上只能看到众粉丝的溢美之辞。</p>  <p>所以，如果要形成良性讨论，光有逻辑也是万万不行的。我们还是需要点别的东西，例如控制情绪，与人宽容等等。</p><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1660688.html?type=1" width="1" height="1" alt=""/><p>评论: 106　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/01/logic-is-not-everything.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/02/01/logic-is-not-everything.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>数组排序方法的性能比较（5）：对象大小与排序性能</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/29/sort-array-linq-5-object-size-and-performance.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Thu, 28 Jan 2010 16:09:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/29/sort-array-linq-5-object-size-and-performance.html</guid><description><![CDATA[<p>阅读: 3150 评论: 21 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-01-29 00:09 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/29/sort-array-linq-5-object-size-and-performance.html" target="_blank">原文链接</a></p><p>在我<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/21/sort-array-linq-1-notes-and-benchmark.html">公开测试结果</a>之后，有朋友也进行了其他测试。在测试中我使用的是int数组，经过分析之后我们了解到<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/22/sort-array-linq-2-array-sort.html">Array.Sort&lt;T&gt;对于int数组有特殊的优化</a>。于是，某些朋友使用了一些引用类型的数组进行排序，得到Array.Sort&lt;T&gt;方法的性能落后于LINQ排序——虽然由于测试方式的问题，这个结果和结论都不太妥当。不过在讨论的过程中，我们都意识到了一个问题：在其他条件不变的情况下，引用类型的字段越多，Array.Sort&lt;T&gt;方法所需时间就越久。这次我们就来讨论一下这个问题。</p>  <h1>性能测试</h1>  <p>为了体现字段数量和排序时间的相关性，我们首先来构造一个方法，它可以使用Emit生成包含一定数量的字段：</p>  <pre class="code"><span style="color: blue">public abstract class </span><span style="color: #2b91af">TypeBase
</span>{
    <span style="color: blue">public int </span>ID;
}

<span style="color: blue">class </span><span style="color: #2b91af">Program
</span>{
    <span style="color: blue">public static </span><span style="color: #2b91af">ModuleBuilder </span>moduleBuilder = <span style="color: blue">null</span>;

    <span style="color: blue">static </span>Program()
    {
        <span style="color: blue">var </span>assemblyName = <span style="color: blue">new </span><span style="color: #2b91af">AssemblyName </span>{ Name = <span style="color: #a31515">&quot;SomeAssembly&quot; </span>};

        <span style="color: blue">var </span>domain = <span style="color: #2b91af">Thread</span>.GetDomain();
        <span style="color: blue">var </span>asmBuilder = domain.DefineDynamicAssembly(
            assemblyName, <span style="color: #2b91af">AssemblyBuilderAccess</span>.Run);

        moduleBuilder = asmBuilder.DefineDynamicModule(<span style="color: #a31515">&quot;SomeModule&quot;</span>);
    }

    <span style="color: blue">static </span><span style="color: #2b91af">Type </span>CreateType(<span style="color: blue">int </span>numberOfField)
    {
        <span style="color: blue">var </span>typeName = <span style="color: #a31515">&quot;TypeWith$&quot; </span>+ numberOfField + <span style="color: #a31515">&quot;$Fields&quot;</span>;
        <span style="color: blue">var </span>typeBuilder = moduleBuilder.DefineType(
            typeName, <span style="color: #2b91af">TypeAttributes</span>.Class, <span style="color: blue">typeof</span>(<span style="color: #2b91af">TypeBase</span>));

        <span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i &lt; numberOfField; i++)
        {
            typeBuilder.DefineField(<span style="color: #a31515">&quot;Field$&quot; </span>+ i, <span style="color: blue">typeof</span>(<span style="color: blue">int</span>), <span style="color: #2b91af">FieldAttributes</span>.Public);
        }

        <span style="color: blue">return </span>typeBuilder.CreateType();
    }
}</pre>

<p>方便起见，我让每种动态类型都继承统一的TypeBase类，这样我们排序的目标便可以定为TypeBase数组，而作为比较器的TypeBaseComparer也可以直接访问ID字段。然后便是测试用的方法：</p>

<pre class="code"><span style="color: blue">static void </span>Main(<span style="color: blue">string</span>[] args)
{
    <span style="color: #2b91af">CodeTimer</span>.Initialize();

    <span style="color: blue">var </span>random = <span style="color: blue">new </span><span style="color: #2b91af">Random</span>(<span style="color: #2b91af">DateTime</span>.Now.Millisecond);
    <span style="color: blue">var </span>array = <span style="color: #2b91af">Enumerable</span>.Repeat(0, 1000 * 500).Select(_ =&gt; random.Next()).ToArray();

    <span style="color: blue">for </span>(<span style="color: blue">var </span>num = 1; num &lt;= 512; num *= 2)
    {
        <span style="color: blue">var </span>type = CreateType(num);
        <span style="color: blue">var </span>arrayToSort = <span style="color: blue">new </span><span style="color: #2b91af">TypeBase</span>[array.Length];

        <span style="color: blue">for </span>(<span style="color: blue">var </span>i = 0; i &lt; array.Length; i++)
        {
            <span style="color: blue">var </span>instance = (<span style="color: #2b91af">TypeBase</span>)<span style="color: #2b91af">Activator</span>.CreateInstance(type);
            instance.ID = array[i];
            arrayToSort[i] = instance;
        }

        <span style="color: #2b91af">CodeTimer</span>.Time(
            <span style="color: #2b91af">String</span>.Format(<span style="color: #a31515">&quot;Type with {0} fields (Array.Sort)&quot;</span>, num),
            10, () =&gt; Sort(CloneArray(arrayToSort)));

        <span style="color: #2b91af">CodeTimer</span>.Time(
            <span style="color: #2b91af">String</span>.Format(<span style="color: #a31515">&quot;Type with {0} fields (ArraySorter)&quot;</span>, num),
            10, () =&gt; ArraySorter(CloneArray(arrayToSort)));
    }

    <span style="color: #2b91af">Console</span>.ReadLine();
}

<span style="color: blue">static void </span>Sort(<span style="color: #2b91af">TypeBase</span>[] array)
{
    <span style="color: #2b91af">Array</span>.Sort(array, <span style="color: blue">new </span><span style="color: #2b91af">TypeBaseComparer</span>());
}

<span style="color: blue">static void </span>ArraySorter(<span style="color: #2b91af">TypeBase</span>[] array)
{
    <span style="color: blue">new </span><span style="color: #2b91af">ArraySorter</span>&lt;<span style="color: #2b91af">TypeBase</span>&gt;().OrderBy(a =&gt; a.ID).Sort(array);
}</pre>

<p>在比较时，我们将测试从1个字段的类型开始，每次将字段数量翻倍，直至512个字段——虽然夺得有些夸张，但对于试验来说，我还是希望差距能够明显一些。既然说Array.Sort&lt;T&gt;的性能受对象体积影响比较明显，而LINQ排序相对稳定，那么我们就来比较Array.Sort&lt;T&gt;以及与LINQ排序实现类似的ArraySorter的执行时间吧。请注意，除了两种排序方式之外，其他条件都完全相同：相同的数量，相同的类型，相同的初始顺序，以及相同的“比较依据”。</p>

<p>测试结果如下：</p>


<p>绘制成图表：</p>
<img src="https://spreadsheets.google.com/pub?key=tid6TwjfPuB6Jfxk7DqtS5A&amp;oid=1&amp;output=image" /> 

<p>从图中可以看出明显的差别。当字段数量很少的时候，Array.Sort&lt;T&gt;的性能要比ArraySorter要高，这与我们之前的测试结果相符。但是在字段数量超过4个之后，ArraySorter的性能就开始领先了，且两者的差距随字段数量的增长会越来越大。因此，我们之前的观点是正确的。</p>

<p>这是为什么呢？在阅读以下内容时，您不如先自己思考一下？</p>

<h1>原因分析</h1>

<p>其实这还是一个和“局部性”有关的问题。<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/12/talk-about-code-performance-3-locality.html">局部性是影响性能非常主要的因素</a>之一，我之前<a href="http://www.cnblogs.com/JeffreyZhao/archive/2009/01/22/system-architecture-and-program-performance.html">也不止一次谈过这个问题</a>。那么在这里，局部性又是如何影响排序效率的呢？其实关键还是在于“比较器”上：</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">TypeBaseComparer </span>: <span style="color: #2b91af">IComparer</span>&lt;<span style="color: #2b91af">TypeBase</span>&gt;
{
    <span style="color: blue">public int </span>Compare(<span style="color: #2b91af">TypeBase </span>x, <span style="color: #2b91af">TypeBase </span>y)
    {
        <span style="color: blue">return </span>x.ID - y.ID;
    }
}</pre>

<p>TypeBaseComparer的实现非常简单，只是把两个TypeBase对象的ID值相减而已。显然，系统在执行这段程序的时候，会根据x或y的地址及ID字段的偏移量计算出一个地址，然后再去读取数据。不过我们知道，CPU在读取某个地址的的数据时，还会同时加载“一整条”的数据并缓存起来，这样再次读取附近的数据时会显得更快一些。那么试想一下，对于CPU来说，缓存及每个条目的大小是不变的，因此随着对象的体积增加，缓存中可以同时存在的对象数量便少了，这样虽然读取的次数不变，但是缓存的命中率就会随之下降。于是，对象体积增大，排序所消耗的时间也随之增加。</p>

<p>但是对于ArraySorter来说就完全不同了。根据ArraySorter的实现机制，它会首先根据keySelector得到排序字段——它在上例中就是个int值，IndexComparer然后在排序的时候将这个int数组保存起来，并且在排序时使用。因此，无论对象的体积是多少，在排序时ArraySorter永远只是在访问一个十分紧凑的int数组而已。排序结束后，ArraySorter也只是在操作一个个对象引用，它同样与对象的体积无关。由于排序其他条件不变，因此对象体积增大（造成局部性不佳）最终也只是导致keySelector工作的时候速度变慢，而其他部分统统不受影响。自然从某一时刻开始ArraySorter的性能会超过Array.Sort&lt;T&gt;，并且两者的差距会越来越大。</p>

<p>如果您对现在谈论的内容不很理解的话，可以了解一下与“局部性”有关的内容，并了解一下<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html">LINQ排序</a>及<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html">ArraySorter的实现方式</a>。</p>

<h1>总结</h1>

<p>可见，Array.Sort&lt;T&gt;与ArraySorter的性能高低并非是一句两句话可以说清楚的，在真实条件下选用哪种做法更有优势也不是一件容易确定的事情。不过，我们真需要把性能追求到这个地步吗？事实上，我相信在大部分实际的开发过程中，这点性能差距可以说是微乎其微。对于系统自带的Array.Sort&lt;T&gt;方法以及LINQ排序，其实它们并没有明显的替代关系。其实在某个特定的时候，您会发现其实两者间也只有一种符合条件——别多想，就用吧。</p>

<p>当然，一些明显的错误是需要避免的。例如，您在比较器中反复访问数据库的话，这就是您自己的问题了。</p>

<p>此外，我们这篇文章着重于测试和分析，但是如果可以直观地观察到“局部性”相关的数据岂不是更能说明问题吗？那么我们有什么办法通过实验来证明这一点呢？这问题其实也不大。例如，在使用Visual Studio 2008的<a href="http://www.cnblogs.com/JeffreyZhao/archive/2009/12/22/profiler-sampling.html">Profiler</a>时，我们可以让它同时监视<a href="http://msdn.microsoft.com/en-us/library/bb385772.aspx">L2 Cache的情况</a>。这个实验的设计和执行就交由您亲自来吧。</p>

<p>经过了这几篇文章，您对于.NET框架中的排序类库，是否还有什么疑惑呢？</p>

<p>本文代码：<a href="http://gist.github.com/288765">http://gist.github.com/288765</a></p>

<h1>相关文章</h1>

<ul>
  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/21/sort-array-linq-1-notes-and-benchmark.html">数组排序方法的性能比较（1）：注意事项及试验</a></li>

  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/22/sort-array-linq-2-array-sort.html">数组排序方法的性能比较（2）：Array.Sort&lt;T&gt;实现分析</a></li>

  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html">数组排序方法的性能比较（3）：LINQ排序实现分析</a></li>

  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html">数组排序方法的性能比较（4）：LINQ方式的Array排序</a></li>

  <li>数组排序方法的性能比较（5）：对象大小与排序性能</li>
</ul><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1658783.html?type=1" width="1" height="1" alt=""/><p>评论: 21　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/29/sort-array-linq-5-object-size-and-performance.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/29/sort-array-linq-5-object-size-and-performance.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>数组排序方法的性能比较（4）：LINQ方式的Array排序</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Wed, 27 Jan 2010 16:06:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html</guid><description><![CDATA[<p>阅读: 3270 评论: 21 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-01-28 00:06 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html" target="_blank">原文链接</a></p><p>经过前两篇文章的分析，我们已经了解了<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/22/sort-array-linq-2-array-sort.html">Array.Sort&lt;T&gt;</a>与<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html">LINQ排序</a>两种实现方式的差别：前者直接比较两个元素的大小，而后者先选出每个元素的“排序依据”再进行比较。因此，虽然后者需要相对较多的“周边工作”，但由于每次比较时都可以仅仅使用高效的基础类型（如int），因此从整体来看，两者的性能高低难以辨别。不过，既然我们已经了解LINQ排序“高效”的原因，又能否将其利用在数组排序上呢？程序是人写的，此类问题大都有肯定的答案。那么我们现在就来实现一下。</p>  <h1>API设计</h1>  <p>关于这个问题，我设想过几种API使用与设计方式。例如，我起初想使用“标准”的扩展方法：</p>  <pre class="code"><span style="color: #2b91af">Person</span>[] people = <span style="color: blue">null</span>;
people
    .OrderBy(p =&gt; p.Age) <span style="color: green">// 指定主要排序规则
    </span>.OrderBy(p =&gt; p.ID, <span style="color: blue">true </span><span style="color: green">/* decending */</span>) <span style="color: green">// 指定次要排序规则
    </span>.Sort(); <span style="color: green">// 排序</span></pre>

<p>在这样的API中，前两个OrderBy方法用于“收集排序条件”，而在最后的Sort方法中才对数组进行排序。这个API在使用上问题不大，但是在实现上却有两个缺点。首先，对于数组来说，已经有定义在其上的OrderBy方法，新的扩展方法与类库重名，难道要我换个名字嘛？我讨厌动这脑筋；其次，如果说第一个OrderBy方法是定义在数组上的扩展方法，那么第二个OrderBy方法也是数组的扩展方法吗？</p>

<p>其实不然。既然我们要“收集”排序规则，因此必然需要使用一个对象进行保存——就叫做OrderCriteria吧。那么第一个OrderBy方法便返回这样一个OrderCriteria对象，而第二个OrderBy则是定义在OrderCriteria类上的。那么，我们是不是需要为数组和OrderCriteria类各实现“一整套”的OrderBy方法呢？这实在是一件麻烦的事情啊。</p>

<p>于是，我又想到了这样的接口：</p>

<pre class="code"><span style="color: blue">new </span><span style="color: #2b91af">ArraySorter</span>&lt;<span style="color: #2b91af">Person</span>&gt;(people)
    .OrderBy(p =&gt; Age)
    .OrderBy(p =&gt; p.ID, <span style="color: blue">true</span>)
    .Sort();</pre>

<p>这样做的话，我们只要定义一个ArraySorter类型，再提供一套OrderBy方法即可。这种做法不仅仅是省力，“统一”的排序入口还有另一个好处，因为我们可以实现这样的逻辑：</p>

<pre class="code"><span style="color: blue">var </span>sorter = <span style="color: blue">new </span><span style="color: #2b91af">ArraySorter</span>&lt;<span style="color: #2b91af">Person</span>&gt;(people);

<span style="color: blue">if </span>(shouldOrderByAge)
{
    sorter.OrderBy(p =&gt; p.Age);
}

<span style="color: blue">if </span>(shouldOrderById)
{
    sorter.OrderBy(p =&gt; p.ID);
}

sorter.Sort();</pre>

<p>不过这个接口还是有个缺点：我们无法复用排序规则。例如，如果我们需要为两个类型相同的数组进行规则相同的排序，又该怎么办呢？因此，我打算把接口改为这种：</p>

<pre class="code"><span style="color: blue">var </span>sorter = <span style="color: blue">new </span><span style="color: #2b91af">ArraySorter</span>&lt;<span style="color: #2b91af">Person</span>&gt;();

<span style="color: blue">if </span>(shouldOrderByAge)
{
    sorter.OrderBy(p =&gt; p.Age);
}

<span style="color: blue">if </span>(shouldOrderById)
{
    sorter.OrderBy(p =&gt; p.ID);
}

sorter.Sort(people);
sorter.Sort(people2);</pre>

<p>由此，“排序器”负责“收集”排序规则，然后可以为多个数组进行排序，似乎颇为理想。</p>

<h1>下标比较器（IndexComparer）</h1>

<p>那么，我们的类库该如何设计？这里先要明确几个需求：</p>

<ol>
  <li>对于每个排序规则，都要先选择排序字段，再进行排序（这是我们写这个类库的意义）。 </li>

  <li>排序可以指定多个排序规则，排序规则之间有优先顺序。 </li>

  <li>不同的排序规则，其使用的字段、类型、比较器、顺序都可能有所不同。 </li>
</ol>

<p>请注意第三个需求，由于我们追求灵活性，因此排序的规则是不确定的。因此，不同的规则之间唯一可以统一的便是“数组下标”。我们在把握住这个“相同点”的基础上，定义这样一个比较器类型：</p>

<pre class="code"><span style="color: blue">internal class </span><span style="color: #2b91af">IndexComparer</span>&lt;TKey&gt; : <span style="color: #2b91af">IComparer</span>&lt;<span style="color: blue">int</span>&gt;
{
    <span style="color: blue">public </span><span style="color: #2b91af">IComparer</span>&lt;<span style="color: blue">int</span>&gt; NextIndexComparer;

    <span style="color: blue">private </span>TKey[] m_keyArray;
    <span style="color: blue">private </span><span style="color: #2b91af">IComparer</span>&lt;TKey&gt; m_keyComparer;
    <span style="color: blue">private bool </span>m_decesnding;

    <span style="color: blue">public </span>IndexComparer(TKey[] keyArray, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; keyComparer, <span style="color: blue">bool </span>decesnding)
    {
        <span style="color: blue">this</span>.m_keyArray = keyArray;
        <span style="color: blue">this</span>.m_keyComparer = keyComparer ?? <span style="color: #2b91af">Comparer</span>&lt;TKey&gt;.Default;
        <span style="color: blue">this</span>.m_decesnding = decesnding;
    }

    <span style="color: blue">public int </span>Compare(<span style="color: blue">int </span>indexX, <span style="color: blue">int </span>indexY)
    {
        <span style="color: blue">var </span>keyX = <span style="color: blue">this</span>.m_keyArray[indexX];
        <span style="color: blue">var </span>keyY = <span style="color: blue">this</span>.m_keyArray[indexY];

        <span style="color: blue">var </span>result = <span style="color: blue">this</span>.m_keyComparer.Compare(keyX, keyY);

        <span style="color: blue">if </span>(result == 0)
        {
            <span style="color: blue">return this</span>.NextIndexComparer == <span style="color: blue">null </span>? 0 :
                <span style="color: blue">this</span>.NextIndexComparer.Compare(indexX, indexY);
        }
        <span style="color: blue">else
        </span>{
            <span style="color: blue">return this</span>.m_decesnding ? -result : result;
        }
    }
}</pre>

<p>IndexComparer实现了IComparer&lt;int&gt;接口，它的比较内容不是排序的元素，而是“数组下标”。构造一个IndexComparer类型需要提供“排序字段”的数组keyArray，比较器keyComparer，及排序顺序。Compare方法接受的是数组的两个下标，返回值则表示输入的两个下标的先后顺序。因此，IndexComparer对象会从keyArray中两个排序字段的值，并返回比较结果。</p>

<p>值得注意的是，每个IndexComparer都会保存另一个NextIndexComparer引用，它可能为null，或指向另一个IndexComparer对象。因此，多个IndexComparer实际上组成的是一个“比较器链”，而Compare方法在调用时，如果发现当前的key相同，那么便会尝试使用下一个IndexComparer来比较输入的“数组下标”——这里其实是一个职责链模式应用，恰好满足我们对排序条件的优先顺序。</p>

<h1>排序条件（OrderCriteria）</h1>

<p>那么，由谁来构造IndexComparer链呢？自然是由“排序条件”来构造了：</p>

<pre class="code"><span style="color: blue">internal abstract class </span><span style="color: #2b91af">OrderCriteria</span>&lt;TElement&gt;
{
    <span style="color: blue">public abstract </span><span style="color: #2b91af">IComparer</span>&lt;<span style="color: blue">int</span>&gt; CreateComparer(TElement[] array);

    <span style="color: blue">public </span><span style="color: #2b91af">OrderCriteria</span>&lt;TElement&gt; NextCriteria;
}</pre>

<p>OrderCriteria&lt;TElement&gt;类是排序条件的基类，表示TElement数组的“一个”排序条件。和IndexComparer相同，每个OrderCriteria对象都维护着表示下一个排序条件的对象。OrderCriteria&lt;TElement&gt;有一个抽象方法，用于创建一个比较器——不，确切地说，应该是一个“比较器链”才对：</p>

<pre class="code"><span style="color: blue">internal class </span><span style="color: #2b91af">OrderCriteria</span>&lt;TElement, TKey&gt; : <span style="color: #2b91af">OrderCriteria</span>&lt;TElement&gt;
{
    <span style="color: blue">private </span><span style="color: #2b91af">IComparer</span>&lt;TKey&gt; m_keyComparer;
    <span style="color: blue">private </span><span style="color: #2b91af">Func</span>&lt;TElement, TKey&gt; m_keySelector;
    <span style="color: blue">private bool </span>m_descending;

    <span style="color: blue">public </span>OrderCriteria(
        <span style="color: #2b91af">Func</span>&lt;TElement, TKey&gt; keySelector, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; keyComparer, <span style="color: blue">bool </span>decesnding)
    {
        <span style="color: blue">this</span>.m_keySelector = keySelector;
        <span style="color: blue">this</span>.m_keyComparer = keyComparer;
        <span style="color: blue">this</span>.m_descending = decesnding;
    }

    <span style="color: blue">public override </span><span style="color: #2b91af">IComparer</span>&lt;<span style="color: blue">int</span>&gt; CreateComparer(TElement[] array)
    {
        <span style="color: blue">var </span>keyArray = <span style="color: blue">new </span>TKey[array.Length];
        <span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i &lt; array.Length; i++)
        {
            keyArray[i] = <span style="color: blue">this</span>.m_keySelector(array[i]);
        }

        <span style="color: blue">var </span>comparer = <span style="color: blue">new </span><span style="color: #2b91af">IndexComparer</span>&lt;TKey&gt;(keyArray, <span style="color: blue">this</span>.m_keyComparer, <span style="color: blue">this</span>.m_descending);
        <span style="color: blue">if </span>(<span style="color: blue">this</span>.NextCriteria != <span style="color: blue">null</span>)
        {
            comparer.NextIndexComparer = <span style="color: blue">this</span>.NextCriteria.CreateComparer(array);
        }

        <span style="color: blue">return </span>comparer;
    }
}</pre>

<p>OrderCriteria&lt;TElement, TKey&gt;表示“按照TKey类型来排序TElement数组”的排序规则。在CreateComparer方法中，它会使用keySelector计算出一个TKey数组作为排序依据，再用其构造一个IndexComparer对象。如果NextCriteria字段不为空，那么便以此指定当前IndexComparer的下一个比较器对象。最终返回的，自然就是一个完整的比较器链了。</p>

<h1>数组排序器（ArraySorter）</h1>

<p>终于涉及到面向用户的类型了。其实这个类型非常简单，只有一套OrderBy方法和一个Sort方法。我们首先来关注最核心的OrderBy方法（以及一个可能的重载）：</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">ArraySorter</span>&lt;TElement&gt;
{
    <span style="color: blue">private </span><span style="color: #2b91af">OrderCriteria</span>&lt;TElement&gt; m_headCriteria;
    <span style="color: blue">private </span><span style="color: #2b91af">OrderCriteria</span>&lt;TElement&gt; m_tailCriteria;

    <span style="color: blue">public </span><span style="color: #2b91af">ArraySorter</span>&lt;TElement&gt; OrderBy&lt;TKey&gt;(<span style="color: #2b91af">Func</span>&lt;TElement, TKey&gt; keySelector)
    {
        <span style="color: blue">return this</span>.OrderBy(keySelector, <span style="color: blue">null</span>, <span style="color: blue">false</span>);
    }

    <span style="color: blue">public </span><span style="color: #2b91af">ArraySorter</span>&lt;TElement&gt; OrderBy&lt;TKey&gt;(
        <span style="color: #2b91af">Func</span>&lt;TElement, TKey&gt; keySelector, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; keyComparer, <span style="color: blue">bool </span>descending)
    {
        <span style="color: blue">var </span>newCriteria = <span style="color: blue">new </span><span style="color: #2b91af">OrderCriteria</span>&lt;TElement, TKey&gt;(
            keySelector, keyComparer, descending);

        <span style="color: blue">if </span>(<span style="color: blue">this</span>.m_headCriteria == <span style="color: blue">null</span>)
        {
            <span style="color: blue">this</span>.m_headCriteria = <span style="color: blue">this</span>.m_tailCriteria = newCriteria;
        }
        <span style="color: blue">else
        </span>{
            <span style="color: blue">this</span>.m_tailCriteria.NextCriteria = newCriteria;
            <span style="color: blue">this</span>.m_tailCriteria = newCriteria;
        }

        <span style="color: blue">return this</span>;
    }

    ...
}</pre>

<p>每次调用OrderBy方法时，都会为ArraySorter内部的OrderCriteria单向链表增加一个元素。因此，整套类库的结构示意图大约是这样的：</p>
<img src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/ArraySorter-struct.png" /> 

<p>至于Sort方法，那就更加简单了：</p>

<pre class="code"><span style="color: blue">public void </span>Sort(TElement[] array)
{
    <span style="color: blue">if </span>(<span style="color: blue">this</span>.m_headCriteria == <span style="color: blue">null</span>) <span style="color: blue">return</span>;
    <span style="color: blue">var </span>indexComparer = <span style="color: blue">this</span>.m_headCriteria.CreateComparer(array);

    <span style="color: blue">var </span>indexArray = <span style="color: blue">new int</span>[array.Length];
    <span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i &lt; array.Length; i++) indexArray[i] = i;

    <span style="color: #2b91af">Array</span>.Sort(indexArray, array, indexComparer);
}</pre>

<p>如果没有指定任何排序条件自然直接返回，否则创建一个indexComparer链作为比较器。那么我们又是如何排序数组的呢？那就要谢过.NET类库中丰富的功能了。我们使用的是这个重载：</p>

<pre class="code"><span style="color: blue">static void </span>Sort&lt;TKey, TElement&gt;(TKey[] keys, TElement[] items, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; comparer);</pre>

<p>这个重载的功能是将keys数组作为排序依据，但同时也会根据对应的下标排序items数组——这不正是我们的需求吗？</p>

<h1>性能测试</h1>

<p>哈，写完了，测试一下性能吧。由于我们目前的实现是参考了LINQ排序，因此我们这里直接和它进行比较吧：</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">Person
</span>{
    <span style="color: blue">public int </span>ID;
}</pre>

<pre class="code"><span style="color: blue">static void </span>Main(<span style="color: blue">string</span>[] args)
{
    <span style="color: blue">var </span>random = <span style="color: blue">new </span><span style="color: #2b91af">Random</span>(<span style="color: #2b91af">DateTime</span>.Now.Millisecond);
    <span style="color: blue">var </span>array = <span style="color: #2b91af">Enumerable</span>.Repeat(0, 1000 * 500)
        .Select(_ =&gt; <span style="color: blue">new </span><span style="color: #2b91af">Person </span>{ ID = random.Next() }).ToArray();

    <span style="color: #2b91af">CodeTimer</span>.Initialize();
    <span style="color: #2b91af">CodeTimer</span>.Time(<span style="color: #a31515">&quot;SortWithLinq&quot;</span>, 10,
        () =&gt; SortWithLinq(CloneArray(array)));

    <span style="color: #2b91af">CodeTimer</span>.Time(<span style="color: #a31515">&quot;SortWithArraySorter&quot;</span>, 10,
        () =&gt; SortWithArraySorter(CloneArray(array)));

    <span style="color: #2b91af">Console</span>.ReadLine();
}

<span style="color: blue">static void </span>SortWithLinq(<span style="color: #2b91af">Person</span>[] people)
{
    <span style="color: blue">var </span>sorted =
        (<span style="color: blue">from </span>i <span style="color: blue">in </span>people
         <span style="color: blue">orderby </span>i.ID
         <span style="color: blue">select </span>i).ToList();
}

<span style="color: blue">static void </span>SortWithArraySorter(<span style="color: #2b91af">Person</span>[] people)
{
    <span style="color: blue">new </span><span style="color: #2b91af">ArraySorter</span>&lt;<span style="color: #2b91af">Person</span>&gt;().OrderBy(p =&gt; p.ID).Sort(people);
}

<span style="color: blue">static </span>T[] CloneArray&lt;T&gt;(T[] source)
{
    <span style="color: blue">var </span>dest = <span style="color: blue">new </span>T[source.Length];
    <span style="color: #2b91af">Array</span>.Copy(source, dest, source.Length);
    <span style="color: blue">return </span>dest;
}</pre>

<p>结果如何？</p>

<pre class="code"><strong>SortWithLinq</strong>
        Time Elapsed:   3,861ms
        CPU Cycles:     8,174,533,048
        Gen 0:          7
        Gen 1:          7
        Gen 2:          7

<strong>SortWithArraySorter</strong>
        Time Elapsed:   4,791ms
        CPU Cycles:     10,353,862,211
        Gen 0:          5
        Gen 1:          4
        Gen 2:          4</pre>

<p>什么？性能反而变差了！</p>

<p>经过仔细和分析和比对之后，我可以确定我们的做法和LINQ排序可谓如出一辙。事实上，我们的做法就是参考了LINQ排序的实现方式（如“职责链”），在保留其优点的前提下进行一定简化的结果。那么性能究竟差在什么地方呢？</p>

<h1>性能优化</h1>

<p>似乎唯一可能产生性能差距的地方只有排序算法了。LINQ排序使用的是重写的排序算法，而我们则直接调用了Array.Sort方法的某个重载，难道问题出在这里？为此，我找到了Array.Sort方法的根源，其中有这样的代码：</p>

<pre class="code"><span style="color: blue">namespace </span>System.Collections.Generic
{
    <span style="color: blue">internal class </span><span style="color: #2b91af">ArraySortHelper</span>&lt;TKey, TValue&gt;
    {
        <span style="color: blue">internal static void </span>QuickSort(
            TKey[] keys, TValue[] values, <span style="color: blue">int </span>left, <span style="color: blue">int </span>right, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; comparer)
        {
            ...
            <span style="color: blue">if </span>(values != <span style="color: blue">null</span>)
            {
                TValue local3 = values[a];
                values[a] = values[b];
                values[b] = local3;
            }

            ...
        }
    }
}</pre>

<p>在QuickSort方法中，如果传入了values数组，则会在交换keys数组中元素的同时，一并交换values对应下标的元素。还记得我们我们之前对<a href="http://www.cnblogs.com/JeffreyZhao/archive/2009/12/02/array-copy-benchmark.html">比较各种数组复制的性能的试验</a>吗？从结果中我们得知，各种复制方式中最慢的便是根据下标一一复制。因为对于引用类型来说，为了避免GC造成影响，在每次进行引用复制时都会动用Write Barrier，性能降低再所难免。</p>

<p>于是，我们尝试着把ArraySorter的Sort方法修改成这样吧：</p>

<pre class="code"><span style="color: blue">public void </span>Sort(TElement[] array)
{
    <span style="color: blue">if </span>(<span style="color: blue">this</span>.m_headCriteria == <span style="color: blue">null</span>) <span style="color: blue">return</span>;
    <span style="color: blue">var </span>indexComparer = <span style="color: blue">this</span>.m_headCriteria.CreateComparer(array);

    <span style="color: blue">var </span>indexArray = <span style="color: blue">new int</span>[array.Length];
    <span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i &lt; array.Length; i++) indexArray[i] = i;

    <span style="color: green">// Array.Sort(indexArray, array, indexComparer);

    </span><span style="color: #2b91af">Array</span>.Sort(indexArray, indexComparer);

    <span style="color: blue">var </span>arrayCopy = <span style="color: blue">new </span>TElement[array.Length];
    <span style="color: #2b91af">Array</span>.Copy(array, arrayCopy, array.Length);

    <span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i &lt; array.Length; i++)
    {
        array[i] = arrayCopy[indexArray[i]];
    }
}</pre>

<p>现在，我们来直接排序indexArray数组，并准备一个array数组的副本arrayCopy，最后再根据indexArray中保存的下标，把arrayCopy里的内容复制回array中去。那么再次试验一下吧：</p>

<pre class="code"><strong>SortWithLinq</strong>
        Time Elapsed:   3,756ms
        CPU Cycles:     7,856,900,073
        Gen 0:          8
        Gen 1:          8
        Gen 2:          8

<strong>SortWithArraySorter</strong>
        Time Elapsed:   3,672ms
        CPU Cycles:     7,833,900,349
        Gen 0:          5
        Gen 1:          4
        Gen 2:          4</pre>

<p>虽然领先不多（几乎还在误差范围内），但至少不比前者慢了（这个结果很稳定，您可以亲自进行多次试验）。您可能会想，我们最后再为array数组赋值的时候又没有使用Array.Copy，为什么会有性能提升呢？这是因为，如果直接使用Array.Sort(keys, items)进行排序的话，对items数组的读写（交换元素）次数会高出许多。因此在作了这样的修改时候，目前的性能和原来相比会有不小的提升。</p>

<h1>总结</h1>

<p>啊哈，那么和原来的Array.Sort方法相比，我们编写的ArraySorter又会有哪些好处呢？关于这个问题，我们下次再来讨论吧，您也可以自己先进行一些测试。</p>

<p>当然，现在的ArraySorter还需要一些改进。例如，它还没有对输入参数进行校验，重载也不足够。另外，对于引用类型来说，我们最后的优化是有效果的，那么对于值类型或其他类型来说又如何呢？我还没有进行试验，这就交给您来发现了。</p>

<p>本文代码：<a href="http://gist.github.com/287857">http://gist.github.com/287857</a></p>

<h1>相关文章</h1>

<ul>
  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/21/sort-array-linq-1-notes-and-benchmark.html">数组排序方法的性能比较（1）：注意事项及试验</a></li>

  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/22/sort-array-linq-2-array-sort.html">数组排序方法的性能比较（2）：Array.Sort&lt;T&gt;实现分析</a></li>

  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html">数组排序方法的性能比较（3）：LINQ排序实现分析</a></li>

  <li>数组排序方法的性能比较（4）：LINQ方式的Array排序</li>
</ul><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1657793.html?type=1" width="1" height="1" alt=""/><p>评论: 21　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/28/sort-array-linq-4-linq-style-array-sort.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>数组排序方法的性能比较（3）：LINQ排序实现分析</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Tue, 26 Jan 2010 16:02:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html</guid><description><![CDATA[<p>阅读: 3993 评论: 21 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-01-27 00:02 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html" target="_blank">原文链接</a></p><p>上次我们<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/22/sort-array-linq-2-array-sort.html">分析了Array.Sort&lt;T&gt;方法的实现方式</a>，并了解到类库会为一些特例而使用高性能的排序方式——int数组便是这样一例，因此<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/21/sort-array-linq-1-notes-and-benchmark.html">从测试结果上来看</a>其性能特别高。不过从数据上看，即便是在普通的情况下，Array.Sort&lt;T&gt;的性能也比LINQ排序要高。不过也有朋友从测试中得出的结论正好相反，这又是为什么呢？那么现在，我们再来分析一下LINQ排序的实现方式吧，希望这样可以了解到两者性能差别的秘密。</p>  <p>只可惜，LINQ排序的代码在System.Core.dll程序集中，微软没有发布这部分源代码，我们只得使用.NET Reflector来一探究竟了。</p>  <h1>LINQ排序接口的定义、使用及扩展</h1>  <p>所谓LINQ排序，便是使用定义在System.Linq.Enumerable类中的几个扩展方法，它们是：</p>  <pre class="code"><span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderBy&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector);

<span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderBy&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; comparer);

<span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderByDescending&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector);

<span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderByDescending&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; comparer);</pre>

<p>为了使用时的方便，我往往会补充一些额外的接口，例如：</p>

<pre class="code"><span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderBy&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector, <span style="color: blue">bool </span>decending)
{
    <span style="color: blue">return </span>decending ?
        source.OrderByDescending(keySelector) :
        source.OrderBy(keySelector);
}</pre>

<p>这样在使用时，便可以使用一个布尔值来表示排序的方向（升序或是降序）而不需要从两个方法之间“手动”选择一个。此外，构造一个IComparer&lt;TKey&gt;类型也实在有些麻烦，于是我按照Array.Sort&lt;T&gt;的做法重新继续扩展了一个使用委托对象作为“比较器”的接口：</p>

<pre class="code"><span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderBy&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector, <span style="color: #2b91af">Comparison</span>&lt;TKey&gt; compare, <span style="color: blue">bool </span>decending)
{
    <span style="color: blue">return </span>decending ?
        source.OrderByDescending(keySelector, <span style="color: blue">new </span><span style="color: #2b91af">FunctorComparer</span>&lt;TKey&gt;(compare)) :
        source.OrderBy(keySelector, <span style="color: blue">new </span><span style="color: #2b91af">FunctorComparer</span>&lt;TKey&gt;(compare));
}</pre>

<p>至于FunctorComparer类的实现，由于过于简单就省略了吧，贴出来也只是占用地方而已。有了这个接口，在排序的时候我们就可以这样使用了：</p>

<pre class="code">employee.OrderBy(p =&gt; p.Manager, (m1, m2) =&gt; ... <span style="color: green">/* 比较逻辑 */</span>, <span style="color: blue">false</span>);</pre>

<p>不过，无论是哪个接口、重载还是扩展，它的（除this外）的第一个参数便是keySelector，它的含义便是选择（select）出排序的“依据”。这个参数不可省略（除非您提供扩展），因此即便是int数组这样的类型，需要排序时也必须指定“自己”为排序依据：</p>

<pre class="code">intArray.OrderBy(i =&gt; i);</pre>

<p>这也是LINQ排序和Array.Sort&lt;T&gt;的本质区别之一。</p>

<h1>OrderedEnumerable的实现</h1>

<p>无论是哪个接口，最终创建的都是OrderedEnumerable&lt;TElement, TKey&gt;类型，例如：</p>

<pre class="code"><span style="color: blue">public static </span><span style="color: #2b91af">IOrderedEnumerable</span>&lt;TSource&gt; OrderBy&lt;TSource, TKey&gt;(
    <span style="color: blue">this </span><span style="color: #2b91af">IEnumerable</span>&lt;TSource&gt; source, <span style="color: #2b91af">Func</span>&lt;TSource, TKey&gt; keySelector)
{
    <span style="color: blue">return new </span><span style="color: #2b91af">OrderedEnumerable</span>&lt;TSource, TKey&gt;(source, keySelector, <span style="color: blue">null</span>, <span style="color: blue">false</span>);
}</pre>

<p>OrderedEnumerable&lt;TElement, TKey&gt;的含义是“根据TKey排序TElement序列的结果”，它的构造函数仅仅是保留传入的参数：</p>

<pre class="code"><span style="color: blue">internal </span>OrderedEnumerable(
    <span style="color: #2b91af">IEnumerable</span>&lt;TElement&gt; source, <span style="color: #2b91af">Func</span>&lt;TElement, TKey&gt; keySelector, <span style="color: #2b91af">IComparer</span>&lt;TKey&gt; comparer, <span style="color: blue">bool </span>descending)
{
    <span style="color: green">// 省略参数校验</span>

    <span style="color: blue">base</span>.source = source;
    <span style="color: blue">this</span>.parent = <span style="color: blue">null</span>;
    <span style="color: blue">this</span>.keySelector = keySelector;
    <span style="color: blue">this</span>.comparer = (comparer != <span style="color: blue">null</span>) ? comparer : ((<span style="color: #2b91af">IComparer</span>&lt;TKey&gt;) <span style="color: #2b91af">Comparer</span>&lt;TKey&gt;.Default);
    <span style="color: blue">this</span>.descending = descending;
}</pre>

<p>可见，如果您没有提供比较器，类库会自动选用Comparer&lt;TKey&gt;.Default进行比较。这个类会尽可能地寻找可用的比较方式，在“万不得已”的情况下只得跑出异常。如果您对它的实现感兴趣可以自行阅读代码——甚至无需使用.NET Reflector。</p>

<p>事实上，在OrderedEnumerable&lt;TElement, TKey&gt;中并没有提供排序等关键性功能，它只是override了基类的GetEnumerableSorter方法，用于提供一个“排序器”。它的基类是OrderdEnumerable&lt;TElement&gt;，其含义是“排序TElement序列的结果”，它并不涉及到“排序方式”，而只是提供了一个抽象方法用于获得一个“排序器”——没错，这就是它的子类，如OrderedEnumerable&lt;TElement, TKey&gt;的职责了（还记得TKey的含义吗：“根据TKey进行排序”）。</p>

<p>不过，事实上除了OrderdEnumerable&lt;TElement, TKey&gt;以外也没有其他子类了，由于这些都是internal类型，因此我认为这样有些“过渡设计”。根据我们昨天“人肉反编译”的结果，可以得到OrderedEnumerable&lt;TElement&gt;的完整实现：</p>

<pre class="code"><span style="color: blue">internal abstract class </span><span style="color: #2b91af">OrderedEnumerable</span>&lt;TElement&gt; : <span style="color: #2b91af">IEnumerable</span>&lt;TElement&gt;...
{
    <span style="color: blue">internal </span><span style="color: #2b91af">IEnumerable</span>&lt;TElement&gt; source;

    <span style="color: blue">internal abstract </span><span style="color: #2b91af">EnumerableSorter</span>&lt;TElement&gt; GetEnumerableSorter(<span style="color: #2b91af">EnumerableSorter</span>&lt;TElement&gt; next);

    <span style="color: blue">public </span><span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt; GetEnumerator()
    {
        <span style="color: blue">var </span>buffer = <span style="color: blue">new </span><span style="color: #2b91af">Buffer</span>&lt;TElement&gt;(<span style="color: blue">this</span>.source);
        <span style="color: blue">if </span>(buffer.count &lt;= 0) <span style="color: blue">yield break</span>;

        <span style="color: blue">var </span>sorter = <span style="color: blue">this</span>.GetEnumerableSorter(<span style="color: blue">null</span>);
        <span style="color: blue">var </span>map = sorter.Sort(buffer.items, buffer.count);

        <span style="color: blue">for </span>(<span style="color: blue">var </span>i = 0; i &lt; buffer.count; i++)
        {
            <span style="color: blue">yield return </span>buffer.items[map[i]];
        }
    }

    ...
}</pre>

<p>与我们平时接触到的排序算法不同，EnumerableSorter的Sort方法并不改变原数组，它只是生成根据buffer.items数组生成一个排序之后的“下标序列”——即map数组。当外部需要输出排序后的序列时，OrderedEnumerable&lt;TElement&gt;才会根据map中的下标顺序，依次输出buffer.items数组中的元素。</p>

<p>请注意，到目前为止我们还是没有接触到最终的排序实现。换句话说，现在我们还是不清楚LINQ排序性能高（或低）的关键。</p>

<h1>排序实现：EnumerableSorter</h1>

<p>LINQ排序的实现关键还是在于EnumerableSorter&lt;TElement&gt;，我们且看其Sort代码：</p>

<pre class="code"><span style="color: blue">internal abstract class </span><span style="color: #2b91af">EnumerableSorter</span>&lt;TElement&gt;
{
    <span style="color: blue">internal abstract int </span>CompareKeys(<span style="color: blue">int </span>index1, <span style="color: blue">int </span>index2);
    <span style="color: blue">internal abstract void </span>ComputeKeys(TElement[] elements, <span style="color: blue">int </span>count);

    <span style="color: blue">private void </span>QuickSort(<span style="color: blue">int</span>[] map, <span style="color: blue">int </span>left, <span style="color: blue">int </span>right)
    {
        ...
    }

    <span style="color: blue">internal int</span>[] Sort(TElement[] elements, <span style="color: blue">int </span>count)
    {
        <span style="color: blue">this</span>.ComputeKeys(elements, count);

        <span style="color: blue">int</span>[] map = <span style="color: blue">new int</span>[count];
        <span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i &lt; count; i++)
        {
            map[i] = i;
        }

        <span style="color: blue">this</span>.QuickSort(map, 0, count - 1);
        <span style="color: blue">return </span>map;
    }
}</pre>

<p>从之前的分析中得知，Sort方法的作用是返回一个排好序的下标数组。它会调用ComputeKeys抽象方法“事先”进行Key（也就是排序依据）的计算。然后再使用快速排序来排序map数组。在QuickSort中，它使用CompareKeys方法来获得“两个下标”所对应的元素的先后顺序。仅此而已，没什么特别的。甚至我在这里都不打算分析ComputeKeys和CompareKeys两个方法的实现，因为他们实在过于直接：前者会把source序列中的元素依次调用keySelector委托，以此获得一个与source对应的TKey数组，而后者便是根据传入的下标来比较TKey数组中对应的两个元素的大小。</p>

<p>不过，我还是强烈建议您阅读一下EnumerableSorter&lt;TElement&gt;及其子类EnumerableSorter&lt;TElement, TKey&gt;的实现，以此了解LINQ to Object是如何优雅地支持以下表达式的：</p>

<pre class="code"><span style="color: blue">var </span>sorted = <span style="color: blue">from </span>p <span style="color: blue">in </span>people
             <span style="color: blue">orderby </span>p.Age
             <span style="color: blue">orderby </span>p.ID <span style="color: blue">descending
             select </span>p;</pre>

<p>这个表达式的含义是“将Person序列首先根据Age属性进行升序排列，如果Age相同则再根据ID降序排”——类库在实现时使用了类似于“职责链模式”的做法，颇为美观。</p>

<h1>LINQ排序与Array.Sort&lt;T&gt;的性能比较</h1>

<p>如果您仔细阅读EnuerableSorter的QuickSort方法，会发现它使用的快速排序算法并不“标准”。快速排序的性能关键之一是选择合适的pivot元素，但是QuickSort方法总是选择最中间的元素——(left + right) / 2。此外，它也没有在元素小于一定阈值时使用更高效的插入排序。因此，从理论上来说，QuickSort方法使用的快速排序算法，其性能不如Array.Sort&lt;T&gt;。</p>

<p>不过，根据姜敏兄的测试结果，LINQ排序的性能超过Array.Sort&lt;T&gt;，这又是怎么回事呢？事实上，虽然姜兄的这个测试存在很大的问题（代码写错了），最后得到的结论“性能高低和元素类型有关”的结论也不确切，但是它也的确能体现一些问题。这个问题事实上已经由<a href="http://www.cnblogs.com/Ivony/">Ivony...</a>老大解释过了，不过为了信息完整思维连贯，我在这里再进行详细说明一下。</p>

<p>从理论上来说，Array.Sort&lt;T&gt;和LINQ排序的时间复杂度是相同的，因此性能“似乎不会有太大不同”，但是从实验结果上看差距还是十分明显的。因为从实际上看，Array.Sort&lt;T&gt;对于特殊类型有特殊处理，此外LINQ排序会有复制元素的开销，因此我之前我认为“找不到LINQ排序的性能有优势的理由”。可惜这句话已经站不住脚了，我们来观察一下两种排序方式在实现上的主要区别：</p>

<ul>
  <li>Array.Sort&lt;T&gt;：使用IComparer&lt;T&gt;对象比较两个元素的大小。 </li>

  <li>LINQ排序：首先根据keySelector获得TKey序列，然后在排序时使用IComparer&lt;TKey&gt;比较两个TKey元素的大小。 </li>
</ul>

<p>那么，以此您是否可以判断出以下两个排序方法的性能高低？</p>

<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">Person
</span>{
    <span style="color: blue">public int </span>Age { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
}

<span style="color: blue">public class </span><span style="color: #2b91af">PersonComparer </span>: <span style="color: #2b91af">IComparer</span>&lt;<span style="color: #2b91af">Person</span>&gt;
{
    <span style="color: blue">public int </span>Compare(<span style="color: #2b91af">Person </span>x, <span style="color: #2b91af">Person </span>y)
    {
        <span style="color: blue">return </span>x.Age - y.Age;
    }
}</pre>

<pre class="code"><span style="color: #2b91af">Person</span>[] people = ...

<span style="color: blue">var</span> byLinq = people.OrderBy(p =&gt; p.Age).ToList();
<span style="color: blue">var </span>byArray = <span style="color: #2b91af">Array</span>.Sort(people, <span style="color: blue">new </span><span style="color: #2b91af">PersonComparer</span>());</pre>

<p>在实际测试之前我无法做出判断，因为它们其实各有千秋：</p>

<ul>
  <li>Array.Sort&lt;T&gt;：虽然不需要进行额外的元素复制，但是调用PersonComparer.Compare方法的开销较大——访问Age属性相当于调用get_Age方法（如果没有内联的话——不过从实际结果看的确被内联了）。</li>

  <li>LINQ排序：虽然需要进行额外的元素复制，而且需要事先计算出排序用的键值（Age属性），但是在排序时只需直接比较int即可，效率较高。</li>
</ul>

<p>这其实也就是某些测试中发现LINQ排序性能较高的“秘密”。为什么同样排序Person序列时，我的测试（<a href="http://gist.github.com/282796">http://gist.github.com/282796</a>）表明Array.Sort&lt;T&gt;较快，而其他一些朋友却得到LINQ排序较快的结果呢？这是因为我的Person类直接使用了公开字段而不是属性，这样避免了方法调用的开销（因为有内联，这点应该不是问题）。此外，另一些朋友的PersonComparer在比较两个int时使用了x.Age.CompareTo方法——这又比直接进行int减法要慢上一些了。</p>

<p>那么，还有影响两者性能的因素吗？我们有办法提高数组排序的性能吗？毕竟很多时候我们需要直接排序，而不是生成新的序列。下次我们再来讨论这些问题吧。</p>

<h1>相关文章</h1>

<ol>
  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/21/sort-array-linq-1-notes-and-benchmark.html">数组排序方法的性能比较（1）：注意事项及试验</a></li>

  <li><a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/22/sort-array-linq-2-array-sort.html">数组排序方法的性能比较（2）：Array.Sort&lt;T&gt;实现分析</a></li>

  <li>数组排序方法的性能比较（3）：LINQ排序实现分析</li>
</ol><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1657030.html?type=1" width="1" height="1" alt=""/><p>评论: 21　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/27/sort-array-linq-3-linq-sort.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>人肉反编译使用yield关键字的方法</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/26/decompile-methods-with-yield-manually.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Mon, 25 Jan 2010 16:06:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/26/decompile-methods-with-yield-manually.html</guid><description><![CDATA[<p>阅读: 5340 评论: 28 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-01-26 00:06 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/26/decompile-methods-with-yield-manually.html" target="_blank">原文链接</a></p><p>我认为这是一个真命题：“没有用.NET Reflector反编译并阅读过代码的程序员不是专业的.NET程序员”。.NET Reflector强大的地方就在于可以把IL代码反编译成可读性颇高的高级语言代码，并且能够支持相当多的“模式”，根据这些模式它可以在一定程度上把某些语法糖给还原，甚至可以支持简单的Lambda表达式和LINQ。只可惜，.NET Reflector还是无法做到极致，某些情况下生成的代码还是无法还原到易于理解——yield关键字便是这样一个典型的情况。不过还行，对于不复杂的逻辑，我们可以通过人肉来“整理”个大概。</p>  <h1>简单yield方法编译结果分析</h1>  <p>yeild的作用是简化枚举器，也就是IEnumerator&lt;T&gt;或IEnumerable&lt;T&gt;的实现。“人肉”反编译的关键在于发现编译器的规律，因此我们先来观察编译器的处理结果。值得注意的是，我们这里所谈的“分析”，都采用的是微软目前的C# 3.0编译器。从理论上来说，这些结果或是规律，都有可能无法运用在Mono和微软之前或今后的C#编译器上。首先我们准备一段使用yield的代码：</p>  <pre class="code"><span style="color: blue">static </span><span style="color: #2b91af">IEnumerator</span>&lt;<span style="color: blue">int</span>&gt; GetSimpleEnumerator()
{
    <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">&quot;Creating Enumerator&quot;</span>);

    <span style="color: blue">yield return </span>0;
    <span style="color: blue">yield return </span>1;
    <span style="color: blue">yield return </span>2;

    <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">&quot;Enumerator Created&quot;</span>);
}</pre>

<p>为了简化问题，我们在这里采用IEnumerator&lt;T&gt;。自动生成的IEnumerable&lt;T&gt;和IEnumerator&lt;T&gt;区别不大，您可以自己观察一下，有机会我会单独讨论和分析其中的区别。经过编译之后再使用.NET Reflector进行反编译，得到的结果是：</p>

<pre class="code"><span style="color: blue">private static </span><span style="color: #2b91af">IEnumerator</span>&lt;<span style="color: blue">int</span>&gt; GetSimpleEnumerator()
{
    <span style="color: blue">return new </span><span style="color: #2b91af">&lt;GetSimpleEnumerator&gt;d__0</span>(0);
}

[<span style="color: #2b91af">CompilerGenerated</span>]
<span style="color: blue">private sealed class </span><span style="color: #2b91af">&lt;GetSimpleEnumerator&gt;d__0</span> : <span style="color: #2b91af">IEnumerator</span>&lt;<span style="color: blue">int</span>&gt;, ...<span style="color: #2b91af">
</span>{
    <span style="color: green">// Fields
    </span><span style="color: blue">private int </span>&lt;&gt;1__state;
    <span style="color: blue">private int </span>&lt;&gt;2__current;

    <span style="color: green">// Methods
    </span>[<span style="color: #2b91af">DebuggerHidden</span>]
    <span style="color: blue">public </span>&lt;GetSimpleEnumerator&gt;d__0(<span style="color: blue">int </span>&lt;&gt;1__state)
    {
        <span style="color: blue">this</span>.&lt;&gt;1__state = &lt;&gt;1__state;
    }

    <span style="color: blue">private bool </span>MoveNext()
    {
        <span style="color: blue">switch </span>(<span style="color: blue">this</span>.&lt;&gt;1__state)
        {
            <span style="color: blue">case </span>0:
                <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">&quot;Creating Enumerator&quot;</span>);
                <span style="color: blue">this</span>.&lt;&gt;2__current = 0;
                <span style="color: blue">this</span>.&lt;&gt;1__state = 1;
                <span style="color: blue">return true</span>;

            <span style="color: blue">case </span>1:
                <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                <span style="color: blue">this</span>.&lt;&gt;2__current = 1;
                <span style="color: blue">this</span>.&lt;&gt;1__state = 2;
                <span style="color: blue">return true</span>;

            <span style="color: blue">case </span>2:
                <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                <span style="color: blue">this</span>.&lt;&gt;2__current = 2;
                <span style="color: blue">this</span>.&lt;&gt;1__state = 3;
                <span style="color: blue">return true</span>;

            <span style="color: blue">case </span>3:
                <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">&quot;Enumerator Created&quot;</span>);
                <span style="color: blue">break</span>;
        }

        <span style="color: blue">return false</span>;
    }

    ...
}</pre>

<p>以上便是编译器生成的逻辑，它将yield关键字这个语法糖转化为普通的.NET结构（再次强调，这只是微软目前的C# 3.0编译器所产生的结果）。从中我们可以得出一些结论：</p>

<ul>
  <li>原本GetSimpleEnumerator方法中包含yield的逻辑不复存在，取而代之的是一个由编译器自动生成的IEnumerator类的实例。 </li>

  <li>原本GetSimpleEnumerator方法中包含yield的逻辑，被编译器自动转化为对应IEnumerator类中的MoveNext方法的逻辑。 </li>

  <li>编译器将包含yield逻辑转化为一个状态机，并使用自动生成的state字段保存当前状态。 </li>

  <li>每次调用MoveNext方法时，都通过switch语句判断state的值，直接进入特定的逻辑片断，并指定下一个状态。 </li>
</ul>

<p>因为从yield关键字的作用便是“中断”一个方法的逻辑，使它在下次执行MoveNext方法的时候继续执行。这就意味着自动生成的MoveNext代码必须通过某一个手段来保留上次调用结束之后的“状态”，并根据这个状态决定下次调用的“入口”——这是个典型的状态机的“思路”。由此看来，编译器如此实现，其“设计”意图也是比较直观的，相信您理解起来也不会有太大问题。</p>

<h1>较为复杂的yield方法</h1>
上一个例子非常简单，因为GetSimpleEnumerator的逻辑非常简单（只有“顺序”，而没有“循环”和“选择”）。此外，这个方法也没有使用局部变量及参数，于是我们这里不妨再准备一个相对复杂的方法： 

<pre class="code"><span style="color: blue">private static </span><span style="color: #2b91af">IEnumerator</span>&lt;<span style="color: blue">int</span>&gt; GetComplexEnumerator(<span style="color: blue">int</span>[] array)
{
    <span style="color: #2b91af">&lt;GetComplexEnumerator&gt;d__2</span> d__ = <span style="color: blue">new </span><span style="color: #2b91af">&lt;GetComplexEnumerator&gt;d__2</span>(0);
    d__.array = array;
    <span style="color: blue">return </span>d__;
}

[<span style="color: #2b91af">CompilerGenerated</span>]
<span style="color: blue">private sealed class </span><span style="color: #2b91af">&lt;GetComplexEnumerator&gt;d__2</span> : <span style="color: #2b91af">IEnumerator</span>&lt;<span style="color: blue">int</span>&gt;, ...
{
    <span style="color: green">// Fields
    </span><span style="color: blue">private int </span>&lt;&gt;1__state;
    <span style="color: blue">private int </span>&lt;&gt;2__current;
    <span style="color: blue">public int </span>&lt;i&gt;5__4;
    <span style="color: blue">public int </span>&lt;i&gt;5__6;
    <span style="color: blue">public int </span>&lt;sumEven&gt;5__3;
    <span style="color: blue">public int </span>&lt;sumOdd&gt;5__5;
    <span style="color: blue">public int</span>[] array;

    <span style="color: green">// Methods
    </span>[<span style="color: #2b91af">DebuggerHidden</span>]
    <span style="color: blue">public </span>&lt;GetComplexEnumerator&gt;d__2(<span style="color: blue">int </span>&lt;&gt;1__state)
    {
        <span style="color: blue">this</span>.&lt;&gt;1__state = &lt;&gt;1__state;
    }

    <span style="color: blue">private bool </span>MoveNext()
    {
        <span style="color: green">// 第一部分</span>
        <span style="color: blue">switch </span>(<span style="color: blue">this</span>.&lt;&gt;1__state)
        {
            <span style="color: blue">case </span>0:
                <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">&quot;Creating Enumerator&quot;</span>);
                <span style="color: blue">this</span>.&lt;sumEven&gt;5__3 = 0;
                <span style="color: blue">this</span>.&lt;i&gt;5__4 = 0;
                <span style="color: blue">goto </span><strong>Label_0094</strong>;

            <span style="color: blue">case </span>1:
                <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                <span style="color: blue">goto </span><strong>Label_0086</strong>;

            <span style="color: blue">case </span>2:
                <span style="color: blue">goto </span><strong>Label_00F4</strong>;

            <span style="color: blue">default</span>:
                <span style="color: blue">goto </span><strong>Label_0123</strong>;
        }

        <span style="color: green">// 第二部分</span>
    <strong>Label_0086:</strong>
        <span style="color: blue">this</span>.&lt;i&gt;5__4++;
    <strong>Label_0094:</strong>
        <span style="color: blue">if </span>(<span style="color: blue">this</span>.&lt;i&gt;5__4 &lt; <span style="color: blue">this</span>.array.Length)
        {
            <span style="color: blue">if </span>((<span style="color: blue">this</span>.array[<span style="color: blue">this</span>.&lt;i&gt;5__4] % 2) == 0)
            {
                <span style="color: blue">this</span>.&lt;sumEven&gt;5__3 += <span style="color: blue">this</span>.array[<span style="color: blue">this</span>.&lt;i&gt;5__4];
                <span style="color: blue">this</span>.&lt;&gt;2__current = <span style="color: blue">this</span>.&lt;sumEven&gt;5__3;
                <span style="color: blue">this</span>.&lt;&gt;1__state = 1;
                <span style="color: blue">return true</span>;
            }
            <span style="color: blue">goto </span><strong>Label_0086</strong>;
        }
        <span style="color: blue">this</span>.&lt;sumOdd&gt;5__5 = 0;
        <span style="color: blue">this</span>.&lt;i&gt;5__6 = 0;
        <span style="color: blue">while </span>(<span style="color: blue">this</span>.&lt;i&gt;5__6 &lt; <span style="color: blue">this</span>.array.Length)
        {
            <span style="color: blue">if </span>((<span style="color: blue">this</span>.array[<span style="color: blue">this</span>.&lt;i&gt;5__6] % 2) == 0)
            {
                <span style="color: blue">goto </span><strong>Label_00FB</strong>;
            }
            <span style="color: blue">this</span>.&lt;sumOdd&gt;5__5 += <span style="color: blue">this</span>.array[<span style="color: blue">this</span>.&lt;i&gt;5__6];
            <span style="color: blue">this</span>.&lt;&gt;2__current = <span style="color: blue">this</span>.&lt;sumOdd&gt;5__5;
            <span style="color: blue">this</span>.&lt;&gt;1__state = 2;
            <span style="color: blue">return true</span>;
        <strong>Label_00F4:</strong>
            <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
        <strong>Label_00FB:</strong>
            <span style="color: blue">this</span>.&lt;i&gt;5__6++;
        }
        <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">&quot;Enumerator Created.&quot;</span>);
    <strong>Label_0123:</strong>
        <span style="color: blue">return false</span>;
    }

    ...
}</pre>

<p>这下MoveNext的逻辑便一下子复杂了很多。我认为，这是由于编译器期望生成体积小的代码，于是它使用了goto来进行自由的跳转。其实从理论上说，把这个方法分为N个阶段之后，便可以让它们完全独立地分开，只不过此时各状态间便会出现许多重复的逻辑。不过，这段代码看似复杂，其实您仔细分析便会发现，它其实也只是将代码拆成了上下两部分（如代码注释所示）：</p>

<ul>
  <li>第一部分：状态机的控制逻辑，即根据当前状态进行跳转。 </li>

  <li>第二部分：主体逻辑，只不过使用goto代替了普通语句中由for/if组成的逻辑，这么做的目的是为了插入Label，可以让第一部分的代码直接跳转到合适的地方——换句话说，<font color="#ff0000">由第一部分跳转到的Label便是yield return出现的地方</font>。 </li>
</ul>

<p>从上面的代码中我们还可以看出方法的“参数”及“局部变量”的转化规则：</p>

<ul>
  <li>参数被转化为IEnumerator类的公开字段，命名方式不变，原本的array参数直接变成array字段。 </li>

  <li>局部变量被转化为IEnumerator类的公开字段，并运用一定的命名规则改名（主要是为了避免和自动生成的current及state字段产生冲突）。对于局部变量localVar，将被转化为&lt;localVar&gt;X__Y的形式。 </li>

  <li>其他需要自动生成的字段为&lt;&gt;1__state及&lt;&gt;2__current，它们只是进行辅助逻辑，不再赘述。 </li>
</ul>

<p>至此，我们已经掌握了编译器基本的转化规律，可以将其运用到“人肉反编译”的过程中去。</p>

<h1>试验：人肉反编译OrderedEnumerable</h1>

<p>事实上，.NET框架中的System.Linq.OrderedEnumerable类便是一个包含yield方法的逻辑，使用.NET Reflector得到的相关代码如下：</p>

<pre class="code"><span style="color: blue">internal abstract class </span><span style="color: #2b91af">OrderedEnumerable</span>&lt;TElement&gt; : <span style="color: #2b91af">IOrderedEnumerable</span>&lt;TElement&gt;, ...<span style="color: #2b91af">
</span>{
    <span style="color: blue">internal </span><span style="color: #2b91af">IEnumerable</span>&lt;TElement&gt; source;

    <span style="color: blue">internal abstract </span><span style="color: #2b91af">EnumerableSorter</span>&lt;TElement&gt; GetEnumerableSorter(<span style="color: #2b91af">EnumerableSorter</span>&lt;TElement&gt; next);

    <span style="color: blue">public </span><span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt; GetEnumerator()
    {
        <span style="color: #2b91af">&lt;GetEnumerator&gt;d__0</span>&lt;TElement&gt; d__ = <span style="color: blue">new </span><span style="color: #2b91af">&lt;GetEnumerator&gt;d__0</span>&lt;TElement&gt;(0);
        d__.&lt;&gt;4__this = (<span style="color: #2b91af">OrderedEnumerable</span>&lt;TElement&gt;) <span style="color: blue">this</span>;
        <span style="color: blue">return </span>d__;
    }

    [<span style="color: #2b91af">CompilerGenerated</span>]
    <span style="color: blue">private sealed class </span><span style="color: #2b91af">&lt;GetEnumerator&gt;d__0</span> : <span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt;, ...<span style="color: #2b91af">
    </span>{
        <span style="color: green">// Fields
        </span><span style="color: blue">private int </span>&lt;&gt;1__state;
        <span style="color: blue">private </span>TElement &lt;&gt;2__current;
        <span style="color: blue">public </span><span style="color: #2b91af">OrderedEnumerable</span>&lt;TElement&gt; &lt;&gt;4__this;
        <span style="color: blue">public </span><span style="color: #2b91af">Buffer</span>&lt;TElement&gt; &lt;buffer&gt;5__1;
        <span style="color: blue">public int </span>&lt;i&gt;5__4;
        <span style="color: blue">public int</span>[] &lt;map&gt;5__3;
        <span style="color: blue">public </span><span style="color: #2b91af">EnumerableSorter</span>&lt;TElement&gt; &lt;sorter&gt;5__2;

        [<span style="color: #2b91af">DebuggerHidden</span>]
        <span style="color: blue">public </span><span style="color: #2b91af">&lt;GetEnumerator&gt;d__0</span>(<span style="color: blue">int </span>&lt;&gt;1__state)
        {
            <span style="color: blue">this</span>.&lt;&gt;1__state = &lt;&gt;1__state;
        }

        <span style="color: blue">private bool </span>MoveNext()
        {
            <span style="color: blue">switch </span>(<span style="color: blue">this</span>.&lt;&gt;1__state)
            {
                <span style="color: blue">case </span>0:
                    <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                    <span style="color: blue">this</span>.&lt;buffer&gt;5__1 = <span style="color: blue">new </span>Buffer&lt;TElement&gt;(<span style="color: blue">this</span>.&lt;&gt;4__this.source);
                    <span style="color: blue">if </span>(<span style="color: blue">this</span>.&lt;buffer&gt;5__1.count &lt;= 0)
                    {
                        <span style="color: blue">goto </span><strong>Label_00EA</strong>;
                    }
                    <span style="color: blue">this</span>.&lt;sorter&gt;5__2 = <span style="color: blue">this</span>.&lt;&gt;4__this.GetEnumerableSorter(<span style="color: blue">null</span>);
                    <span style="color: blue">this</span>.&lt;map&gt;5__3 = <span style="color: blue">this</span>.&lt;sorter&gt;5__2.Sort(<span style="color: blue">this</span>.&lt;buffer&gt;5__1.items, <span style="color: blue">this</span>.&lt;buffer&gt;5__1.count);
                    <span style="color: blue">this</span>.&lt;sorter&gt;5__2 = <span style="color: blue">null</span>;
                    <span style="color: blue">this</span>.&lt;i&gt;5__4 = 0;
                    <span style="color: blue">break</span>;

                <span style="color: blue">case </span>1:
                    <span style="color: blue">this</span>.&lt;&gt;1__state = -1;
                    <span style="color: blue">this</span>.&lt;i&gt;5__4++;
                    <span style="color: blue">break</span>;

                <span style="color: blue">default</span>:
                    <span style="color: blue">goto </span><strong>Label_00EA</strong>;
            }
            <span style="color: blue">if </span>(<span style="color: blue">this</span>.&lt;i&gt;5__4 &lt; <span style="color: blue">this</span>.&lt;buffer&gt;5__1.count)
            {
                <span style="color: blue">this</span>.&lt;&gt;2__current = <span style="color: blue">this</span>.&lt;buffer&gt;5__1.items[<span style="color: blue">this</span>.&lt;map&gt;5__3[<span style="color: blue">this</span>.&lt;i&gt;5__4]];
                <span style="color: blue">this</span>.&lt;&gt;1__state = 1;
                <span style="color: blue">return true</span>;
            }
        <strong>Label_00EA:</strong>
            <span style="color: blue">return false</span>;
        }

        ...
    }
}</pre>

<p>很自然，我们需要“人肉反编译”的便是OrderedEnumerable类的GetEnumerator方法。<font color="#ff0000">首先，为了便于理解代码，我们首先还原各名称。</font>既然我们已经知道了局部变量及current/state的命名规则，因此这个工作其实并不困难：</p>

<pre class="code"><span style="color: blue">private bool </span>MoveNext()
{
    <span style="color: blue">switch </span>(__state)
    {
        <span style="color: blue">case </span>0:
            __state = -1;
            <span style="color: blue">var </span>buffer = <span style="color: blue">new </span><span style="color: #2b91af">Buffer</span>&lt;TElement&gt;(<span style="color: blue">this</span>.source);
            <span style="color: blue">if </span>(buffer.count &lt;= 0)
            {
                <span style="color: blue">goto </span><strong>Label_00EA;</strong>
            }

            <span style="color: blue">var </span>sorter = <span style="color: blue">this</span>.GetEnumerableSorter(<span style="color: blue">null</span>);
            <span style="color: blue">var </span>map = sorter.Sort(buffer.items, buffer.count);
            sorter = <span style="color: blue">null</span>;
            <span style="color: blue">var </span>i = 0;
            <span style="color: blue">break</span>;

        <span style="color: blue">case </span>1:
            __state = -1;
            i++;
            <span style="color: blue">break</span>;

        <span style="color: blue">default</span>:
            <span style="color: blue">goto </span><strong>Label_00EA;
</strong>    }

    <span style="color: blue">if </span>(i &lt; buffer.count)
    {
        __current = buffer.items[map[i]];
        __state = 1;
        <span style="color: blue">return true</span>;
    }

<strong>Label_00EA:</strong>
    <span style="color: blue">return false</span>;
}</pre>

<p>值得注意的是，在上面的方法中，this是由原来的&lt;&gt;4__this字段还原而来，它表示的是OrderedEnumerable类型（而不是自动生成的IEnumerator类）的实例。此外，其中的局部变量您需要将其理解为“自动在多次MoveNext调用中保持状态的变量”——这和C语言中的静态局部变量有些接近。自然，__state和__current变量都是自动生成用于保存状态的变量，我们姑且保留它们。</p>

<p>接下来，我们将要<font color="#ff0000">还原state等于0时的逻辑</font>。因为我们知道，它其实是yield方法中“第一个yield return”之前的逻辑：</p>

<pre class="code"><span style="color: blue">private </span><span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt; GetEnumerator()
{
    <span style="color: blue">var </span>buffer = <span style="color: blue">new </span><span style="color: #2b91af">Buffer</span>&lt;TElement&gt;(<span style="color: blue">this</span>.source);
    <span style="color: blue">if </span>(buffer.count &lt;= 0) <span style="color: blue">yield break</span>;
    
    <span style="color: blue">var </span>sorter = <span style="color: blue">this</span>.GetEnumerableSorter(<span style="color: blue">null</span>);
    <span style="color: blue">var </span>map = sorter.Sort(buffer.items, buffer.count);
    <span style="color: green">// 省略sorter = null（为什么？:P）
    
    </span><span style="color: blue">var </span>i = 0;
    <span style="color: blue">if </span>(i &lt; buffer.count)
    {
        <span style="color: blue">yield return </span>buffer.items[map[i]];
    }
    
    ...
}</pre>

<p>我们发现，在buffer.count小于等于0的时候MoveNext直接返回false了，于是在GetEnumerator方法中我们便使用yield break直接退出。在上面的代码中我们已经还原至第一个yield return，那么当调用下一个MoveNext时（即state为1）逻辑又该如何进行呢？我们再“机械”地还原一下：</p>

<pre class="code"><span style="color: blue">private </span><span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt; GetEnumerator()
{
    ...

    i++;
    <span style="color: blue">if </span>(i &lt; buffer.count)
    {
        <span style="color: blue">yield return </span>buffer.items[map[i]];
    }
    <span style="color: blue">else
    </span>{
        <span style="color: blue">yield break</span>;
    }

    ...
}</pre>

<p>接着，我们会发现代码会不断重复上面这段逻辑，因此我们可以使用一个“死循环”将其包装起来。至此，GetEnumerator便还原成功了：</p>

<pre class="code"><span style="color: blue">private </span><span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt; GetEnumerator()
{
    <span style="color: blue">var </span>buffer = <span style="color: blue">new </span><span style="color: #2b91af">Buffer</span>&lt;TElement&gt;(<span style="color: blue">this</span>.source);
    <span style="color: blue">if </span>(buffer.count &lt;= 0) <span style="color: blue">yield break</span>;
    
    <span style="color: blue">var </span>sorter = <span style="color: blue">this</span>.GetEnumerableSorter(<span style="color: blue">null</span>);
    <span style="color: blue">var </span>map = sorter.Sort(buffer.items, buffer.count);
    
    <span style="color: blue">var </span>i = 0;
    <span style="color: blue">if </span>(i &lt; buffer.count)
    {
        <span style="color: blue">yield return </span>buffer.items[map[i]];
    }

    <span style="color: blue">while </span>(<span style="color: blue">true</span>)
    {
        i++;
        <span style="color: blue">if </span>(i &lt; buffer.count)
        {
            <span style="color: blue">yield return </span>buffer.items[map[i]];
        }
        <span style="color: blue">else
        </span>{
            <span style="color: blue">yield break</span>;
        }
    }
}</pre>

<p>不过，又有多少人会写这样的代码呢？的确，这段代码是我们“机械翻译”的结果。不过经过观察，事实上这段代码可以被修改成如下写法：</p>

<pre class="code"><span style="color: blue">private </span><span style="color: #2b91af">IEnumerator</span>&lt;TElement&gt; GetEnumerator()
{
    <span style="color: blue">var </span>buffer = <span style="color: blue">new </span><span style="color: #2b91af">Buffer</span>&lt;TElement&gt;(<span style="color: blue">this</span>.source);
    <span style="color: blue">if </span>(buffer.count &lt;= 0) <span style="color: blue">yield break</span>;

    <span style="color: blue">var </span>sorter = <span style="color: blue">this</span>.GetEnumerableSorter(<span style="color: blue">null</span>);
    <span style="color: blue">var </span>map = sorter.Sort(buffer.items, buffer.count);

    <span style="color: blue">for </span>(<span style="color: blue">var </span>i = 0; i &lt; buffer.count; i++)
    {
        <span style="color: blue">yield return </span>buffer.items[map[i]];
    }
}</pre>

<p>至此就完美了。最后这步转换我们利用了人脑的优越性，这样“看出”一种优雅的模式也并非难事——不过这也并非只能靠“感觉”，因为我在上面谈到，编译器会尽可能生成紧凑的代码，这意味着它和“源代码”相比不会有太多的重复。但经由我们“机械还原”之后，会发现这样一段代码其实是重复出现的：</p>

<pre class="code"><span style="color: blue">if </span>(i &lt; buffer.count)
{
    <span style="color: blue">yield return </span>buffer.items[map[i]];
}</pre>

<p>于是我们便可以朝着“合并代码片断”的方向去思考，得到最终的结果还是有规律可循的。</p>

<h1>总结</h1>

<p>如果您关注我最近的文章，并且在看到OrderedEnumerable这个类型之后应该会有所察觉：这篇文章只是我在“<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/21/sort-array-linq-1-notes-and-benchmark.html">分析Array和LINQ排序实现</a>”过程中的一个插曲。没错，这是LINQ排序实现的一小部分。OrderedEnumerable利用了yield关键字，这样我们使用.NET反编译之后代码的可读性很差。为此，我便特地研究了一下对yield进行“人肉反编译”的做法。不过在一开始，我原本其实是想仔细分析一下yield相关的“编译规律”，但是我发现在《<a href="http://www.amazon.com/Depth-What-you-need-master/dp/1933988363/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1264433866&amp;sr=8-1">C# in Depth</a>》一书中已经<a href="http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx">对这个话题有了非常详尽的描述</a>，只得作罢。之后我又看了<a href="http://csharpindepth.com/">这本书网站</a>上公开的样张，感觉非常不错。</p>

<p>事实上，自从ASP.NET 2.0开始，我似乎就没有看过任何一本ASP.NET 2.0/3.0或是C# 2.0/3.0/4.0的书了，因为我认为这些书中的所有内容都可以从MSDN文档，互联网（如博客）以及自己使用、分析的过程中了解到。不过现在，《C# in Depth》似乎让我对此类技术图书的“偏见”有所动摇了——但只此一本而已，估计我还是不会去买这样的书。:)</p>

<p>对了，昨天我向“有关部门”了解到，《C# in Depth》已经由图灵出版社引进，翻译完毕，只等审校和出版了。</p><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1655520.html?type=1" width="1" height="1" alt=""/><p>评论: 28　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/26/decompile-methods-with-yield-manually.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/26/decompile-methods-with-yield-manually.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>昨日搬至办公室的书籍</title><link>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/25/books-20100125-at-snda-office.html</link><dc:creator>Jeffrey Zhao</dc:creator><author>Jeffrey Zhao</author><pubDate>Mon, 25 Jan 2010 04:00:00 GMT</pubDate><guid>http://www.cnblogs.com/JeffreyZhao/archive/2010/01/25/books-20100125-at-snda-office.html</guid><description><![CDATA[<p>阅读: 4967 评论: 70 作者: <a href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">Jeffrey Zhao</a> 发表于 2010-01-25 12:00 <a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/25/books-20100125-at-snda-office.html" target="_blank">原文链接</a></p><p>由于家里地方小，总有一部分书籍需要跟着我辗转至各公司。其中一些公司只能让我放抽屉（地方小）甚至地板上，而有一些公司就能让我把书都摞桌子上。昨天下午我特地来了一次办公室，只为搬运书籍。灭霍霍，可真是一个体力活。</p>  <p>一会儿打算把一些书放到公共的图书馆去，在此之前先留个合影吧（点击放大）：</p> <a href="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/books100125-snda.jpg" target="_blank;"><img src="http://images.cnblogs.com/cnblogs_com/jeffreyzhao/books100125-snda.jpg" width="600" /></a>   <p>其中大部分是原版书。我喜欢原版书，经常找各种理由奖励给自己，例如“发工资了”、“过生日了”或是“今年过节不收礼”等等。不过这些大都是前些时候购买的，近一年多时间已经“收敛”了许多，因为毕竟这也是一笔较为可观的投资，且“盲目购书”也让我花了不少冤枉钱——对于书来说，买而不看便是浪费。因此，我现在大都只挑一些最为经典的书籍购买，并且很可能是看得七七八八后（有了Kindle DX后看电子书也方便了）再买做“收藏”之用。</p>  <p>在大公司有个好处，便是有足够的人可以进行交流。在这里完全可以组织一个读书俱乐部，大家一起交换和分享学习心得——最近似乎就有对《卓有成效的程序员》的交流活动。人多，书自然也不会少，就在上个星期，我已经看了两本想看却没有买的书了——虽然对于原版书我很舍得花钱，但似乎我不太购买中文图书。照片里的中文书大都是出版社或朋友赠送的。</p>  <p>对了，如果您是出版行业的朋友，请记得多多送书给我。我一定会放到公共图书馆去，让大家一起阅读的。:)</p><img src="http://www.cnblogs.com/JeffreyZhao/aggbug/1655745.html?type=1" width="1" height="1" alt=""/><p>评论: 70　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/25/books-20100125-at-snda-office.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/JeffreyZhao/archive/2010/01/25/books-20100125-at-snda-office.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56852/" target="_blank">淘宝网通过索引模式涉足网络文学</a><span style="color:gray">(2010-02-10 15:59)</span><br/>· <a href="http://news.cnblogs.com/n/56851/" target="_blank">苹果发布 iPhone/iPad SDK 3.2 beta2 开发包</a><span style="color:gray">(2010-02-10 15:37)</span><br/>· <a href="http://news.cnblogs.com/n/56850/" target="_blank">“谷姐”：披着“谷歌”羊皮的悲哀？</a><span style="color:gray">(2010-02-10 15:32)</span><br/>· <a href="http://news.cnblogs.com/n/56849/" target="_blank">2010，奇虎的本命年</a><span style="color:gray">(2010-02-10 15:26)</span><br/>· <a href="http://news.cnblogs.com/n/56847/" target="_blank">微软赢得Windows XP WGA诉讼</a><span style="color:gray">(2010-02-10 15:23)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/news/tag/Buzz/" target="_blank">Google Buzz相关新闻</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item></channel></rss>