﻿<?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>博客园-Anders Liu的.NET空间</title><link>http://www.cnblogs.com/AndersLiu/</link><description>传播知识源于掌握知识，但又高于掌握知识。我要做一个掌握很多知识的传播知识的人。</description><language>zh-cn</language><lastBuildDate>Mon, 06 Jul 2009 09:59:36 GMT</lastBuildDate><pubDate>Mon, 06 Jul 2009 09:59:36 GMT</pubDate><ttl>60</ttl><item><title>书评——Windows高级调试</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/07/04/book-review-advanced-windows-debugging.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 04 Jul 2009 11:47:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/07/04/book-review-advanced-windows-debugging.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1516930.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/07/04/book-review-advanced-windows-debugging.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1516930.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1516930.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>书评——Windows高级调试</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/07/04/book-review-advanced-windows-debugging.html" title="书评——Windows高级调试">http://www.cnblogs.com/AndersLiu/archive/2009/07/04/book-review-advanced-windows-debugging.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<p>调试，是通过尝试运行程序找到问题根源的过程。这和测试不同，测试是发现问题的过程，但不深究问题产生的位置、原因。调试是开发人员必须具备的能力，即便是在分工很细致的大企业中——设计、开发、测试往往由不同的工程师完成——调试这项工作却常常（甚至是必须）由开发人员自己进行。</p>
<p>调试有两个层面，一种是基本调试，是我们每天都在接触的。通常，一个完善的集成开发环境通常都自带有调试器；而如今开发人员的开发习惯，也是写完一段语法完整的代码后就顺手启动调试器调试一下。这类调试通常是特定语言的、源代码级别的、用户态的，对于上层的逻辑错误通常是足够的；但如果遇到更深层次的问题，仅仅通过基本调试就无能为力了。</p>
<p>另一种是高级调试，这种调试通常使用独立的调试器（未与开发环境集成），并能脱离源代码进行调试；甚至可以调试操作系统的底层问题（内核调试）。高级调试并不是开发人员所必需具备的能力（甚至很多开发人员并不熟练掌握高级调试），但掌握高级调试可以大幅提高自身功力，对于开发技术的进阶是必不可少的。</p>
<p>《Windows高级调试》（以下简称《AWD》）一书正是讲解如何在Windows操作系统上进行高级调试的。其实“高级调试”不过是“另一种调试方法”而已，它也有自己的入门、进阶、精通等程度。而《AWD》一书，在老刘看来应该是属于“入门”和“进阶”这两个级别的，特别适合于掌握了一定的开发技术和基本调试技术，想学习高级调试的朋友。</p>
<p>《AWD》分为三部分，第一部分主要介绍高级调试应具备的基本工具，包括调试器和符号文件等。虽然在第一章中介绍了很多种调试器，但整本书用得最多的还是windbg.exe。</p>
<p>第二部分则从实践的角度介绍了多种调试场景，如内存、资源、进程间通信和同步等。这一部分是整本书的核心，细读下来不仅能掌握高级调试的基本方法，还能对操作系统原理有更加深刻的掌握。</p>
<p>第三部分则是高级调试中的高级主题，这一部分内容对于大多数开发人员来说可能并不是必须的，甚至日常工作根本用不到。但对于对调试技术非常着迷的朋友，或者是系统开发者，这部分却是必不可少的。正如Raymond McCollum所说，大多数书籍在涉及到如此高级的主题时，往往“留给读者练习”或“请参阅其他高级参考书”，而本书则属于那些“高级参考书”。</p>
<p>前面提到本书属于“入门”和“进阶”的级别，并非看轻此书。高级调试技术本身就是相对复杂的，但本书却没有故弄玄虚，而是从多种常见的调试场景入手，介绍如何使用调试器来完成特定的调试任务。这样新进入调试领域的开发者就能很快地上手，实际感受到调试的快感和乐趣。而即便是经常使用调试器的熟手，也可以将本书作为参考，快速查找各种场景中的调试命令。</p>
<p>《AWD》一书的遗憾在于，并没有非常详尽地介绍每种调试器，所以读者朋友不得不“参与其他文章或书籍”来掌握各种调试器的基本操作。当然，如果不是这样的话，您手中的书可就不是500余页这么简单了。</p>
<p>（完）</p>
</div>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1516930.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48008/" target="_blank">IBM发布全球首款开源智能编译器</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>操作无权限的MSMQ队列</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/06/25/manipulate-msmq-without-permission.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 25 Jun 2009 06:42:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/06/25/manipulate-msmq-without-permission.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1510973.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/06/25/manipulate-msmq-without-permission.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1510973.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1510973.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>操作无权限的MSMQ队列</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/06/25/manipulate-msmq-without-permission.html" title="操作无权限的MSMQ队列">http://www.cnblogs.com/AndersLiu/archive/2009/06/25/manipulate-msmq-without-permission.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：在操作MSMQ队列的权限时一定要小心，如果意外地删除了自己的访问权限，即便自己是管理员，也无法对队列进行操作，尤其是无法删除该队列。本文将介绍一种方法，可以绕过MSMQ的权限控制，只要自己是管理员，能够操作C:\Windows\System32中的文件，即可获得对任意队列的操作权。</p>
</div>
<p>首先，我们重现一个这样的“死”队列。在消息队列的管理器中新建一个队列（私有或公有均可），命名为test。右键单击该队列，选择Properties，然后进入Security选项卡，在窗口上部选择Everyone，并在窗口下部的权限列表中，勾选第一行Full Control对应的Deny复选框，最后单击OK按钮。</p>
<p>这样，我们就对所有人禁用了对该队列的一切操作。现在，包括系统管理员在内的任何用户都无法操作该队列，如查看属性或删除队列等操作均无法进行。</p>
<div class="al-ins">
<p class="al-ins-title">注解</p>
<p>为了方便通过搜索引擎找到本文，这里列出查看属性和删除队列时的提示信息。</p>
<p>查看属性：</p>
<p>The properties of PUBLIC=e808d736-9552-4e78-8f8e-fbb7710d3005 cannot be obtained.<br />
Error: The queue does not exist or you do not have sufficient permissions to perform the operation.</p>
<p>删除队列：</p>
<p><computer-name>\test cannot be deleted.<br />
Error: The queue does not exist or you do not have sufficient permissions to perform the operation.</p>
</div>
<p>解决的方法如下：</p>
<p>打开该目录：C:\Windows\System32\msmq\storage\lqs，对于Vista及以上版本的操作系统，UAC会弹出提示，需要提升权限。因此，该方法要求操作者必须具有管理员权限。</p>
<p>在该目录下可以看到一些以16进制数字命名的文件，其中每个文件都对应着一个消息队列的配置。这些文件都是纯文本文件，可以使用记事本等类似程序打开。其内容类似于：</p>
<pre class="al-code-para">
[Properties]
Type=00000000-0000-0000-0000-000000000000
Instance=e808d736-9552-4e78-8f8e-fbb7710d3005
BasePriority=0
Journal=00
Quota=4294967295
JournalQuota=4294967295
CreateTime=1245907917
ModifyTime=1245907918
<b>QueueName=\test</b>
Label=test
Authenticate=00
PrivLevel=1
Transaction=00
<b>Security=01000480e8280000042...</b>
Signature=MSMQStorage
TimeStamp=cd0b434a
</pre>
<p>其中的QueueName字段指明了对应队列的路径（名字）。通过遍历每个文件，即可找到我们刚刚建立的test队列。其中的Security字段就是队列的权限配置，其值是一长串16进制数字，这里限于篇幅只截取了前面若干个字符。</p>
<p>因此，不管当前操作者是否真的具有修改队列权限的权限，只需要修改Security字段的值就能修改队列的权限。</p>
<p>我们并不知道这个字段值的实际格式是什么，但可以使用一种取巧地方法——新建一个队列（此时往往具备对该队列进行操作的所有权限），并将新队列的Security字段值复制给当前队列即可。</p>
<p>下面新建一个队列test2，不要修改其属性。再到C:\Windows\System32\msmq\storage\lqs下找到对应于test2队列的文件，将Security字段的值复制下来，并用该值覆盖test队列文件中的字段值即可。</p>
<p>（完）</p>
</div>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1510973.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48007/" target="_blank">IE颓势不减 微软下月公布最新浏览器架构</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>搞死ILDASM</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/04/23/ildasm-crash.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 23 Apr 2009 15:42:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/04/23/ildasm-crash.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1442512.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/04/23/ildasm-crash.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1442512.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1442512.html</trackback:ping><description><![CDATA[<p>复制下面的代码：</p>  <p>.assembly a {}    <br />.module a.dll </p>  <p>.field static int32[-268435455...268435456] f_m268435455</p>  <p>保存为a.il。打开Visual Studio的命令行窗口，执行下面的命令，将这段代码编译为a.dll：</p>  <p>ilasm /dll a.il</p>  <p>然后执行下面的命令，尝试用ILDASM打开生成的dll文件：</p>  <p>ildasm a.dll</p>  <p>好了，ILDASM死了：</p>  <p><a href="http://images.cnblogs.com/cnblogs_com/AndersLiu/WindowsLiveWriter/ILDASM_146ED/ildasm-crash_2.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="ildasm-crash" border="0" alt="ildasm-crash" src="http://images.cnblogs.com/cnblogs_com/AndersLiu/WindowsLiveWriter/ILDASM_146ED/ildasm-crash_thumb.jpg" width="164" height="244" /></a></p>  <p>这是ILASM在处理下标为负数的数组时存在的问题，但也不排除ILDASM自己的问题。</p>  <p>今天先抛出这个问题，改天再撰文分析数组的元数据签名，以及ILASM在这方面的BUG。</p>  <p>注：</p>  <p>1. 上述程序可放心试验，只会影响ildasm.exe一个进程，不用怕机器整个挂掉。</p>  <p>2. 该程序针对.net 2.0~3.5，4.0没有试过，不知道改进了没有。</p>  <p>3. 经仔细思考后发现，这个故障并不仅因为下标为负数，还因为数组的长度过长；如果将上标268435456改小一些，就会发现ILDASM不死了，但得到的下标值是错误的。</p><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1442512.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48002/" target="_blank">竞争日趋激烈 微软欲借 Windows 7 扭转战局</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>一个查看PE文件内容的小工具——CliPeViewer</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/04/18/cli-pe-viewer-release-1.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 18 Apr 2009 03:25:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/04/18/cli-pe-viewer-release-1.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1438612.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/04/18/cli-pe-viewer-release-1.html#Feedback</comments><slash:comments>24</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1438612.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1438612.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>一个查看PE文件内容的小工具——CliPeViewer</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/18/cli-pe-viewer-release-1.html" title="一个查看PE文件内容的小工具——CliPeViewer">http://www.cnblogs.com/AndersLiu/archive/2009/04/18/cli-pe-viewer-release-1.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<p>猛击此链接，下载CliPeViewer：<a href="http://files.cnblogs.com/AndersLiu/CliPeViewer.zip" title="猛击此链接，下载CliPeViewer">http://files.cnblogs.com/AndersLiu/CliPeViewer.zip</a></p>
<p>CliPeViewer是老刘编写的一个WinForm小程序，能够用来查看托管PE文件中的各种结构。目前有很多能够查看PE信息的程序，支持.NET的也有。不过，CliPeViewer与他们的最大区别在于，这个小程序更在意原汁原味地暴露一个PE文件中的内容，尤其是对于文件中的每个结构、每个字段，除了显示了它的值之外，还显示出了它在PE文件中的偏移量和原始数据。</p>
<p>坦率地说，CliPeViewer还没有写完。尚欠缺的功能包括：显示IL指令、托管方法的定义（方法头、异常处理块等），以及对元数据签名的解析。之所以放出这个不成熟的版本，是因为老刘等不起了。2005年，老刘就开始学习托管PE文件格式和IL语言，并有想法写这样一个程序，甚至编写了这个程序的最原始版本。</p>
<p>2006年，因为种种原因这件事就放下了。到了2007年，工作之余闲暇时间不少，于是又把这一摊子捡了起来。2008年，老刘惊喜地发现，自.NET 2.0以来，不管CLR怎么进展，编程语言如何进化，但底层的元数据结构似乎一直没发生变化，ECMA-335标准也一直停留在2006年6月的版本。于是老刘发现这件事有搞头，下定决心要搞一搞。</p>
<p>前不久，看了一些C# 4.0的资料，更加坚定了这种看法。C# 4.0里所有的新功能，都是.NET 2.0时代就已经提供了的，只是一直没有在上层语言中体现出来。</p>
<p>从2005年到2009年，不短了，结果一个像样的版本也没写出来。先后开了不下10个项目，名字也改了5、6次了，最后都是不了了之。目前这个CliPeViewer是我最满意的一个版本，也是打算继续跟进下去的版本。老刘觉得不能等了，等到自己满意了，.NET兴许都成历史文物了。</p>
<p>这个程序的代码（NB的多次否定句来了）有够烂，但足够简单，却又很多。所以这里就不放出源代码了，如果实在感兴趣可使用.NET Reflector偷窥。</p>
<p>后面老刘可能会继续修改这个版本，也没准推翻重来。但老刘保证要做的事包括：1）写一个通用点的XML文件，描述PE文件结构，这样大家（包括老刘自己）就可以通过编写代码生成器来生成文件结构的定义了。2）让AndersLiu.CliPe.dll尽可能地为第三方所用。3）写一些文章介绍PE文件格式。</p>
<p>下面截图奉上。</p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/r_cli-pe-viewer-release-1-fig01.jpg" alt="CliPeViewer屏幕截图" /></p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/r_cli-pe-viewer-release-1-fig02.jpg" alt="CliPeViewer屏幕截图" /></p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/r_cli-pe-viewer-release-1-fig03.jpg" alt="CliPeViewer屏幕截图" /></p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/r_cli-pe-viewer-release-1-fig04.jpg" alt="CliPeViewer屏幕截图" /></p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/r_cli-pe-viewer-release-1-fig05.jpg" alt="CliPeViewer屏幕截图" /></p>
<p>（完）</p>
</div>

<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1438612.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48001/" target="_blank">上海电信计划2012年80%用户实现100M带宽</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>ReadOnlyDictionary之应用场景</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/04/17/usage-of-readonly-dictionary.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 17 Apr 2009 09:09:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/04/17/usage-of-readonly-dictionary.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1438163.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/04/17/usage-of-readonly-dictionary.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1438163.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1438163.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>ReadOnlyDictionary之应用场景</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/17/usage-of-readonly-dictionary.html" title="ReadOnlyDictionary之应用场景">http://www.cnblogs.com/AndersLiu/archive/2009/04/17/usage-of-readonly-dictionary.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<p>前两天发布了<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html" title="隐藏接口实现 及 ReadOnlyDictionary">《隐藏接口实现 及 ReadOnlyDictionary》</a>一文，有不少朋友提出疑问。其中一个主要问题就是，这样做真的可以实现“只读”吗？如果还有其他变量引用了ReadOnlyDictionary所包装的普通字典，依然可以通过另外这个变量来修改字典中的内容。有朋友甚至提到了，可以在创建ReadOnlyDictionary时，将原有字典中的内容复制出一份，来做到真正的“只读”。</p>
<p>其实，这些主要是因为我对这个类的应用场景没有介绍清楚的缘故。其实我这里提到的ReadOnlyDictionary也好，.NET提供的ReadOnlyCollection也罢，其目的并不是让整个字典（或集合）真的“只读”，而是希望某些具有字典（或集合）性质类成员，能够做到“在当前类中可读写，在类的外部只读”。</p>
<p>考虑这样一个场景，我要实现一个FileStructure类，表示文件结构，其中包含了多个Field。每个FileStructure里的每个Field都具有不同的名字，我希望用户能够用名字检索到对应的字段，所以最好的方式是将字段保存在一个字典中。</p>
<pre class="al-code-para">
public class FileStructure
{
    public Dictionary&lt;string, Field&gt; Fields
    {
        get { return _fields; }
    }
 
    public FileStructure()
    {
        _fields = new Dictionary&lt;string, Field&gt;();
 
        _fields.Add("DosHeader", new Field(...));
        _fields.Add("PEHeader", new Field(...));
        _fields.Add("OptionalHeader", new Field(...));
    }
 
    private Dictionary&lt;string, Field&gt; _fields;
}
</pre>
<p>上面的代码虽然保证了Fields属性是只读的——可以确保用户无法用自己的字典实例来替换FileStructure所有的——但却不能防止用户自己向Fields属性中添加其他字段。</p>
<p>如果使用ReadOnlyDictionary，就能很好地避免这一问题。</p>
<pre class="al-code-para">
public class FileStructure
{
    public ReadOnlyDictionary&lt;string, Field&gt; Fields  // Change ‘Dictionary’ to ‘ReadOnlyDictionary’
    {
        get { return _roFields; }  // Change '_fields' to '_roFields'
    }
 
    public FileStructure()
    {
        _fields = new Dictionary&lt;string, Field&gt;();
        _roFields = new ReadOnlyDictionary&lt;string, Field&gt;(_fields);  // Added
 
        _fields.Add("DosHeader", new Field(...));
        _fields.Add("PEHeader", new Field(...));
        _fields.Add("OptionalHeader", new Field(...));
    }
 
    private Dictionary&lt;string, Field&gt; _fields;
    private ReadOnlyDictionary&lt;string, Field&gt; _roFields;  // Added
}
</pre>
<p>现在，就不怕用户“擅自”修改字典中的内容了。</p>
<p>在ReadOnlyDictionary的实现中，并没有复制所包装的字典中的内容。这样做的好处是显而易见的——其一，节省空间。其二，ReadOnlyDictionary能够及时反映内部普通字典上发生的变化；也就是说，如果在类的“内部”向字典中添加了新值，或修改了某些条目，则在类的“外部”可以从之前拿到的只读字典中，访问到这些变化过的条目。</p>
<p>链接：
<ul class="al-ul">
<li>访问《隐藏接口实现 及 ReadOnlyDictionary》——<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html" title="隐藏接口实现 及 ReadOnlyDictionary">http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html</a></li>
<li>下载ReadOnlyDictionary——<a href="http://files.cnblogs.com/AndersLiu/ReadOnlyDirectionary.zip" title="下载ReadOnlyDictionary">http://files.cnblogs.com/AndersLiu/ReadOnlyDirectionary.zip</a></li>
</ul>
</p>
<p>（完）</p>
</div>

<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1438163.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48000/" target="_blank">数万名网友签名抗议星际争霸2取消局域网功能</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>隐藏接口实现 及 ReadOnlyDictionary</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 16 Apr 2009 04:57:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1437125.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1437125.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1437125.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>隐藏接口实现 及 ReadOnlyDictionary</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html" title="隐藏接口实现 及 ReadOnlyDictionary">http://www.cnblogs.com/AndersLiu/archive/2009/04/16/hide-interface-member-and-readonly-dictionary.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：本文介绍了如何从类型中隐藏掉接口的某个成员，并介绍了应用这种技巧实现的只读字典——ReadOnlyDictionary。</p>
</div>
<p>接口代表着一种契约。但有的时候，接口所达成的契约并不适用于全部的场景，或者说，接口可能定义得“太宽了”。这个时候，就有必要隐藏起某些接口成员。</p>
<p>然而，接口既然是一种“契约”，这就要求实现方必须为接口中的所有成员提供实现。所以，这里说到的“隐藏”，是指从对象的视角上隐藏。换言之，就是只有直接在对象上调用成员时，看不到某些接口成员，但如果将对象强制转换为接口类型，依然能看到所有的接口成员。</p>
<p>在C#中，接口的显式实现可以帮助我们实现这一功能。下面的代码可以帮助我们回忆一下接口的显式实现。</p>
<pre class="al-code-para">
public interface IFoo
{
    void Foo();
}

public class Implement : IFoo
{
    // 显式实现的接口成员：
    void IFoo.Foo() { /* 实现 */ }
}

public class Program
{
    static void Main()
    {
        Implement impl = new Implement();
        impl.Foo();  // 编译错误。显式实现的接口成员不能直接在对象上调用。

        IFoo foo = (IFoo)impl;
        foo.Foo();  // 编译正确。
    }
}
</pre>
<p>下面举个实际的例子。熟悉集合的朋友一定对ICollection&lt;T&gt;接口不陌生。该接口定义了集合类型的一般成员，包括Add、Clear、Contains、CopyTo、GetEnumerator、Remove、Count等等。从集合的角度来看，这些成员是很充分的。</p>
<p>但是，考虑这样一个场景——希望实现一个只读集合类，这个类不允许改变集合的内容。很明显，只读集合也“是一个”集合，所以理应实现ICollection&lt;T&gt;接口。然而，接口中的Add、Clear和Remove等方法无疑是破坏了集合的“只读”特征。</p>
<p>因此，在实现只读集合时，可以用这样一种方式来实现只读集合类——在类型中定义一个普通集合类型的私有字段，也就是说，让只读集合“包装”一个普通集合。这个只读集合类依然实现ICollection&lt;T&gt;接口，其中不会破坏“只读”性质的成员，直接采用普通集合的实现；而对于会破坏“只读”性质的接口成员，则采用显式接口实现的方式，从对象实例中隐藏掉该成员。</p>
<p>最后，为了防止用户将对象强制转换为接口类型，并试图调用那些会破坏只读性质的成员，需要在这些成员的实现代码中抛出异常（推荐使用NotSupportedException异常）。</p>
<p>例如，下面介绍一个ReadOnlyCollection类：</p>
<p>首先，令该类实现ICollection&lt;T&gt;接口，定义一个私有字段存放待包装的普通集合，并在构造器中为其赋值。</p>
<pre class="al-code-para">
public class ReadOnlyCollection&lt;T&gt; : ICollection&lt;T&gt;
{
    private ICollection&lt;T&gt; _collection;

    public ReadOnlyCollection&lt;ICollection&lt;T&gt; collection)
    {
        if(collection == null)
            throw new NotSupportedException();

        _collection = collection;
    }
}
</pre>
<p>对于那些不会破坏只读性质的成员，直接利用普通集合的实现即可。这里以Contains方法为例。</p>
<pre class="al-code-para">
public bool Contains(T value)
{
    return _collection.Contains(value);
}
</pre>
<p>而对于那些会破坏只读性质的成员，则采用接口成员的显式实现，并在实现代码中抛出异常。这里以Add方法为例。</p>
<pre class="al-code-para">
void ICollection&lt;T&gt;.Add(T value)
{
    throw new NotSupportedException();
}
</pre>
<p>非常cool的是，.NET从2.0开始，已经为我们提供了这样一个只读集合，位于System.Collections.ObjectModel命名空间中。</p>
<p>当然，这里为了能够简单地说明问题，提供的代码示例与.NET中的实现并不太一样，有兴趣的朋友可以用.NET Reflector查看ReadOnlyCollection&lt;T&gt;类的源代码。</p>
<p>遗憾的是，.NET只为我们提供了只读集合，却没有提供只读字典（或者是提供了，但我不知道）。所以在这里我仿照ReadOnlyCollection&lt;T&gt;类，编写了一个ReadOnlyDictionary&lt;TKey, TValue&gt;类。代码略长，这里就不贴了，感兴趣的朋友可以通过下面的链接下载：
</p>
<p><a href="http://files.cnblogs.com/AndersLiu/ReadOnlyDirectionary.zip" title="猛击此处下载：ReadOnlyDirectionary.zip">http://files.cnblogs.com/AndersLiu/ReadOnlyDirectionary.zip</a></p>

</div>

<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1437125.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47999/" target="_blank">Silverlight打造杰克逊纪念专题</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>书评——《Microsft .NET Framework 3.5 – Windows Forms Application Development》</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/04/11/book-review-windows-forms.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 11 Apr 2009 05:20:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/04/11/book-review-windows-forms.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1433644.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/04/11/book-review-windows-forms.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1433644.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1433644.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>书评——《Microsft .NET Framework 3.5 – Windows Forms Application Development》</h1>
<div class="al-copy">
	<p>本文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/11/book-review-windows-forms.html" title="书评——《Microsft .NET Framework 3.5 – Windows Forms Application Development》">http://www.cnblogs.com/AndersLiu/archive/2009/04/11/book-review-windows-forms.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：本文大力吹捧了一下微软的新书《Microsft .NET Framework 3.5 – Windows Forms Application Development》，这是一本考试用书，但对于初学者和进阶者来说，帮助的确很大。</p>
</div>
<img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/o_book-review-windows-forms-cover-large.jpg" alt="Microsft .NET Framework 3.5 – Windows Forms Application Development" style="float:right;margin:10px" />
<p>《Microsft .NET Framework 3.5 – Windows Forms Application Development》（以下简称《WinForm》）本身是一本考试用书（Training Kit, MCTS EXAM 70-505）。因此，对于希望学习Windows Forms编程的朋友来说，这本书比市面上其他WinForm类技术图书效果更好一些。</p>
<p>考试用书最大的特征就是直入主题、开门见山。所以从第一章开始你就将学习到WinForm的核心技术，而不是所谓的“背景”啊、“简介”啊什么的。所以，这本700余页的书含金量还是很高的。</p>
<p>《WinForm》这本书不仅是一本介绍技术的书，还介绍了不少技巧。比如第一章，除了介绍了窗体的各种属性之外，还介绍了非矩形窗体。这是很难能可贵的，因为一般考试用书或官方书籍，往往都会偏重技术而忽略技巧；而在实际的开发工作中，往往技巧要比技术更管用。</p>
<p>《WinForm》一书总结起来就一个字——全。WinForm领域能想到的，这本书全有。窗体、使用基本的Windows控件、创建自己的控件、打印、拖拽、异步、数据访问，应有尽有。对于初学者来说，本书可以建立一个全面的知识体系；而即便是WinForm老手，也难免有一些技术是自己鲜有涉足的地方，则更可以把这本书当作一部参考，用到时随手翻来。</p>
<p>可能有些朋友比较迷惑，WinForm编程，不就是拖拖鼠标吗？用得着看书学习么？呵呵，小拖拽里有大学问，有的时候，操作上的一个小偏差，可能会带来意想不到的问题。《WinForm》一书还有一个特征就是对操作的介绍很详实，该选择哪个菜单项、点击那个位置，都一一有所说明。此外，这本书还提供了大量的屏幕截图，更加直观地展示了应作的操作和操作的结果。</p>
<p>唯一遗憾的就是，这么全面的一本书只介绍了从初级到中高级的技术技巧，而不涉及高级技巧。比如在使用异步的时候，会有很多值得注意的地方；打印控制、自定义控件等等，背后都还有很多更有价值的话题，而这些主题往往很难从第三方的技术图书或技术文章中获取。</p>
<p>最后，本书有一点名不副实的地方就是挂了个“.NET Framework 3.5”的名头。事实上，WinForm到了.NET 2.0之后也就基本定型了，.NET 3.0之后微软开始大力发展WPF。所以本书介绍的知识和技术，应该是2.0就开始使用的了——换言之，如果你一直在用2.0（可能没有条件或没有欲望换成3.5），那么依然可以使用这本书学习WinForm，不用担心“.NET 3.5太新了”。</p>
<p>（完）</p>
</div>

<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1433644.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47998/" target="_blank">传诺基亚正在开发Android手机</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] Aggregator Provider模式——白皮书</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/04/11/aggregator-provider-pattern-white-paper.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 11 Apr 2009 03:43:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/04/11/aggregator-provider-pattern-white-paper.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1433593.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/04/11/aggregator-provider-pattern-white-paper.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1433593.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1433593.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>[翻译] Aggregator Provider模式——白皮书</h1>
<div class="al-copy">
	<p>原文地址：<a href="http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=agpp&DownloadId=5402" title="Aggregator Provider Pattern: White Paper">http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=agpp&DownloadId=5402</a></p>
	<p>译文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/04/11/aggregator-provider-pattern-white-paper.html" title="[翻译] Aggregator Provider模式——白皮书">http://www.cnblogs.com/AndersLiu/archive/2009/04/11/aggregator-provider-pattern-white-paper.html</a></p>
	<p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：Aggregator Provider Pattern是Provider Pattern的一种扩展，用于创建和利用多个具有相同提供器接口的类的实例。该模式有一个Aggregator类实现了提供器接口，并包含了一系列实现了相同提供器接口的类的实例集合。</p>
</div>
<div class="al-ins">
<p class="al-ins-title">参考</p>
<p><a href="http://code.msdn.microsoft.com/agpp" title="Aggregator Provider Pattern: White Paper and Samples">http://code.msdn.microsoft.com/agpp</a></p>
</div>
<h2>Definition<br />定义</h2>
<p class="r">Aggregator Provider Pattern is an extension of Provider Pattern, which enables us to create and utilize multiple instance of the class having the same provider interface. In this pattern, there is an Aggregator class which implements the provider interface and contains a collection of instances of classes having the same provider interface.</p>
<p>Aggregator Provider Pattern是Provider Pattern的一种扩展，用于创建和利用多个具有相同提供器接口的类的实例。该模式有一个Aggregator类实现了提供器接口，并包含了一系列实现了相同提供器接口的类的实例集合。</p>
<p class="r">The underlying caller class of this aggregator is simply unaware of how many provider instances do the caller Provider Aggregator contains, but all of the provider instances will be utilized with a single invocation from the caller class.</p>
<p>这个聚合器的潜在调用方类并不知道Provider Aggregator中包含多少个提供器实例，但在调用方类发起的一次调用中，所有的提供器实例都会被调用到。</p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/o_aggregator-provider-pattern-white-paper-fig01.jpg" alt="UML图" /></p>
<h2>Comparison with Provider Pattern<br />与Provider Pattern的比较</h2>
<p class="r">Provider Aggregator Pattern is fully compatible with the existing Provider Pattern and the power of provider pattern can be easily extended to use multiple providers concurrently without any modification on the caller classes that were using a provider.</p>
<p>Provider Aggregator Pattern与现有的Provider Pattern完全兼容，而且Provider Pattern的功能可以很方便地扩展到同时使用多个提供器，而无需对使用提供器的调用方进行任何修改。</p>
<p class="r">In short Provider Pattern is concerned with the utilization of one of the available providers; whereas Aggregator Provider Pattern is concerned with the utilization of all of the available providers at the same time.</p>
<p>简单来说，Provider Pattern考虑的是如何调用可用的提供器中的某一个，而Aggregator Provider Pattern考虑的是如何同时调用所有可用的提供器。</p>
<h2>Example Demonstration<br />示例演示</h2>
<p class="r">Aggregator Provider Pattern is useful when we need a configurable framework to add/remove multiple services used by one caller/user. For instance we can have Logger Provider framework, where we need log info to be saved at text files, save to database and sent to email addresses and so on. Having an easy configurable framework along with Aggregator Provider Pattern will enable us to add or remove more services without requiring the code modification in the code that uses this provider.</p>
<p>当我们需要一个可配置的框架，用于为调用方/用户添加/移除多种服务时，就可以使用Aggregator Provider Pattern。例如我们有一个Logger Provider框架，我们需要将日志信息保存到文本文件、保存到数据库并向指定的地址发送email。具有一个一个应用了Aggregator Provider的简单可配置框架，我们可以添加或移除更多的服务，而无需修改使用该提供器的代码。</p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/o_aggregator-provider-pattern-white-paper-fig02.jpg" alt="类关系图" /></p>
<p class="r">Regarding the example case that just been described can utilize the Aggregator Provider Pattern, by creating the classes as illustrated above. The code snippet below shows a basic usage of this pattern, where the last line will perform the log operation based in list of log providers loaded in the aggregator class dynamically.</p>
<p>上面通过创建类图的方式描绘了一个案例。下面给出的代码片段给出了该模式的基本使用方式，最后一行代码基于在聚合器类中动态加载的一系列提供器执行了log操作。</p>
<p><img src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/o_aggregator-provider-pattern-white-paper-fig03.jpg" alt="代码示例" /></p>
<p>（完）</p>
<h2>后记</h2>
<p>我在最近的一个项目中使用了Provider模式，恰好也是日志相关的，我遇到了同样的问题——需要将日志同时放入文本文件、数据库和系统的EventLog中。这时，传统的Provider模式的确难以满足需求，不过我的解决方案很简单——从配置文件中加载所有的Provider，放在一个集合里，然后每次需要记录日志时，遍历集合中的所有Provider，完成调用。</p>
<p>后来我想到了Façade模式，仿照Façade的思想，写了一个方法来协助我遍历这个集合。然后代码就这样固定下来了。</p>
<p>现在想想，那个辅助方法的签名，不是喝Provider接口中的方法签名一样么？如果能多走一步，不就得到了这里所说的Aggregator Provider模式了么？</p>
<p>不过，就算这样做了又如何？我能认为我发现了一种新模式么？</p>
<p>就算我认为发现了一种新模式又如何？会有人认同么？因为这实在是太简单了，当大家遇到类似问题时，多想一下一定会发现这种模式的。如果这样一篇文章直接出现在博客园的首页，恐怕负面评论要多过正面的。</p>
<p>中国人是聪明、勤劳的，模仿能力和举一反三的能力都很强，但却缺乏创新。为什么会缺乏创新呢？我认为是缺乏被认可。</p>
<p>所以借这个机会，我想呼吁博客园的各位专家牛人和粪青达人，从我做起，做到“认可”先。希望不管首页出现多么“新手”、“小白”、“不自知之明”的文章，我们都应首先做到鼓励。要知道这也许是一个新人创新的开始，一个“建议发到新手区”或者“这样的文章也上首页”，也许就浇灭了一朵创新的火种。</p>
<p>（完，这次是真的完）</p>

</div>

<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1433593.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47998/" target="_blank">传诺基亚正在开发Android手机</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] Anders Hejlsberg访谈录——Geek of the Week</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/03/30/anders-hejlsberg-geek-of-the-week.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 30 Mar 2009 07:38:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/03/30/anders-hejlsberg-geek-of-the-week.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1425189.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/03/30/anders-hejlsberg-geek-of-the-week.html#Feedback</comments><slash:comments>21</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1425189.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1425189.html</trackback:ping><description><![CDATA[<div class="al-body">
<h1>[翻译] Anders Hejlsberg访谈录——Geek of the Week</h1>
<div class="al-copy">
	<p>原文地址：<a href="http://www.simple-talk.com/opinion/geek-of-the-week/anders-hejlsberg-geek-of-the-week/" title="Anders Hejlsberg: Geek of the Week">http://www.simple-talk.com/opinion/geek-of-the-week/anders-hejlsberg-geek-of-the-week/</a></p>
	<p>原作者：<a href="http://www.simple-talk.com/author/richard-morris/" title="Richard Morris">Richard Morris</a></p>
	<p>译文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2009/03/30/anders-hejlsberg-geek-of-the-week.html" title="[翻译] Anders Hejlsberg访谈录——Geek of the Week">http://www.cnblogs.com/AndersLiu/archive/2009/03/30/anders-hejlsberg-geek-of-the-week.html</a></p>
	<p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：Anders Hejlsberg，C#和.NET框架背后的创造型天才，在12年前加入微软之前，他就以编译器编写者的身份驰名16年了。他的BLS Pascal、Turbo Pascal和Delphi彻底变革了软件开发方式。今天，他依然能够冒出新的想法和激进的倡议。</p>
</div>
<p class="r">Anders Hejlsberg, the creative genius behind C#, and much of the .NET framework, had already been famous for sixteen years as a compiler-writer before he joined Microsoft twelve years ago. His BLS Pascal, Turbo Pascal, and Delphi had revolutionized the way that we develop software. Today, he is still bubbling with new ideas and radical initiatives.</p>
<p>Anders Hejlsberg，C#和.NET框架背后的创造型天才，在12年前加入微软之前，他就以编译器编写者的身份驰名16年了。他的BLS Pascal、Turbo Pascal和Delphi彻底变革了软件开发方式。今天，他依然能够冒出新的想法和激进的倡议。</p>
<p class="r">Whether he truly is an international superhero of C# as some claim, Anders Hejlsberg is certainly a visionary. Known as one of the 
'big brains' at Microsoft, he was born in Copenhagen in Denmark and studied engineering at the Technical University of Denmark in Lyngby.</p>
<p>不管他是否像人们声称的那样是C#的国际化超级英雄，Anders Hejlsberg绝对是一个幻想家。作为微软“大脑”之一的他，出生在Denmark（丹麦）的Copenhagen（哥本哈根），并在位于Lyngby的Technical University of Denmark学习了工程师。</p>
<p class="r">It was here that Anders began writing programs for the Nascom microcomputer including a Pascal compiler for CP/M and MS-DOS and marketed for the Nascom 2, under the name Blue Label Software Pascal, or BLS Pascal. Later he went to work for Borland and rewrote his compiler to become Turbo Pascal for the IBM PC. This new compiler sold for $49.95, which was far cheaper than any other commercial high-level language. It became hugely popular in the 1980s thanks in part to an aggressive pricing strategy and having one of the first full-screen IDEs. It became popular with hobbyists as a structured replacement for BASIC. This was followed by 'Super Pascal' which had labels, a return statement and expressions as names of types.</p>
<p>在这里Anders开始为Nascom微型计算机编写程序，包括一个针对CP/M和MS-DOS的Pascal编译器，和针对Nascom 2销售的名为Blue Label Software Pascal的编译器，亦称BLS Pascal。之后他去Borland参加工作，并编写了自己的编译器，这款编译器最终变成了针对IBM PC的Turbo Pascal。这款新型编译器售价$49.95，比当时任何其他商用高级语言编译器都要便宜很多。这款编译器在二十世纪80年代非常流行，很大程度上是因为这种盛气凌人的价格策略，以及其具备的首个全屏幕IDE。它深受初学者喜爱，并成为了BASIC的替代品。这之后是Super Pascal，它具备标签、返回语句和类型名字表达式。</p>
<p class="r">Anders' Turbo Pascal version 5.5 finally added object orientation to Pascal, but the Object-oriented culture was in full swing, and so Anders and his team used Apple's draft standard for Object Pascal to create Delphi with a reference-based object model, virtual constructors and destructors, and properties. Delphi is still used to this day to produce high-quality commercial software.</p>
<p>Anders的Turbo Pascal 5.5版最终为Pascal添加了面向对象，但此时面向对象的文化已经全面地铺开了，因此Anders和他的团队使用了Apple关于Object Pascal的标准草案来创建Delphi，包括一个基于引用的对象模型、虚构造器和析构器以及属性。今天人们依然在使用Delphi生产高质量的商业软件。</p>
<p class="r">Anders joined Microsoft in 1996 and was architect for the Visual J++ development system and the Windows Foundation Classes (WFC). The WFC was primarily designed for creating GUIs for Java applications on Windows. J++ was close to Java, but differed in important aspects. A dispute with Sun over this was settled when Microsoft agreed not to advance J++ beyond its mirrored implementation of Java, version 1.1.4. The technology of J++ was eventually recycled, and survived for a while, as part of the Microsoft .NET platform and the J# programming language.</p>
<p>Anders于1996年加入Microsoft，担任Visual J++开发系统和Windows Foundation Classes（WFC）的架构师。WFC主要设计用于使用Java来为Windows应用程序创建GUI。J++很贴近Java，但在关键方面有所不同。为此Microsoft和Sun产生了争执，而最终以Microsoft同意不再发展J++使其超越对应的Java实现——版本1.1.4——而告终。J++的技术最终被回收，并作为Microsoft .NET平台的一部分和J#编程语言的形式得以暂时存在。</p>
<p class="r">Anders was rapidly promoted to Distinguished Engineer in 2000. He is now a Technical Fellow in the Developer Division and chief designer of the C# programming language and a key participant in the development of the Microsoft .NET framework.</p>
<p>很快Anders在2000年被提升为Distinguished Engineer。他现在是Developer Division的Technical Fellow和C#编程语言的首席设计师以及Microsoft .NET Framework开发团队中的关键成员。</p>
<p class="r">Since its initial release in 2000, the C# programming language has been widely adopted and is now standardized by ECMA and ISO.</p>
<p>自从2000年首次发布以来，C#编程语言得到了广泛应用，并已被ECMA和ISO组织纳为标准。</p>
<p class="r">He has co-authored "The C# Programming Language", published by Addison Wesley, and has received numerous software patents. In 2001, he was the recipient of the prestigious Dr. Dobbs Excellence in Programming Award.</p>
<p>他还是《The C# Programming Language》（Addison Wesley出版）一书的合著者，并且获得了多项软件专利。在2001年，他还获得了最有威信的Dr. Dobbs Excellence in Programming Award奖项。</p>
<hr />
<div class="al-ins">
<p class="al-ins-title">译注</p>
<p>本文原作者Richard Morris，以下简称RM；Anders Hejlsberg，简称AH。</p>
</div>
<p><strong>RM:</strong></p>
<p class="r">"Anders, why is C# called C#. Wasn't it originally named Cool? There's a rumour going around that it was renamed because of the desire to attract geeks. I jest of course, but why the name change?"</p>
<p>“Anders，为什么C#叫做C#？最初它不是叫Cool么？有传闻说它改名是为了吸引极客（Geek）的注意。当然了，我是在开玩笑，但为什么要改名呢？”</p>
<p><strong>AH:</strong></p>
<p class="r">"Yes, the codename for C# was COOL, which stood for C-style Object Oriented Language. We actually liked that name and even looked at keeping it for the final product, but the trademark lawyers weren't, um, cool with it. So, we had to convene the language naming committee. We wanted to have a reference to the language's C heritage in the name and finally settled on C#. Some other candidates I recall were e-C, Safe C, C-square, C-cube, C-prime, C-star, and Cesium... Looking and those now I'm pretty happy with our choice."</p>
<p>“是的，C#的研发代号就是COOL，表示C-style Object Oriented Language。我们非常喜欢这个名字，并且非常希望能够将其用在最终的产品中，但是商标法不允许，嗯，它太酷了。所以，我们必须召开语言命名委员会会议。我们希望语言的名字能够体现出C的传统，于是最终敲定为C#。其他一些我能回忆起来的候选名字还有e-C、Safe C、C-square、C-cube、C-prime、C-star和Cesium……回忆起这些，我现在还是为我们的选择感到高兴。”</p>
<p><strong>RM:</strong></p>
<p class="r">"What were the fundamental flaws in other languages that you believe drove the development of Common Language Runtime (CLR), and in turn, C#?"</p>
<p>“你认为其他语言中的哪些本质上的缺陷驱动着CLR和C#的开发？”</p>
<p><strong>AH:</strong></p>
<p class="r">"I wouldn’t say we were motivated by fundamental flaws in other languages. It was more that we needed to modernize the Windows developer experience. At the time we had multiple distinct development toolsets, e.g. Visual Basic for Rapid Application Development, C++/MFC for systems-level programming, VBScript and IIS for Web applications, and so on. There was little or no sharing of code and skills between these toolsets and your application model would effectively dictate a particular programming language. Furthermore, application interoperability was done through COM, which is a very low-level mechanism, programmers often had to do explicit memory management, there was no common exception and error handling mechanisms, etc. etc. We wanted to fix all of those issues by creating a unified programming platform that supported multiple programming languages, was object-oriented at the core, and provided a shared API framework with modern services such as garbage collection and exception handling."</p>
<p>“我们并不以其他语言中的本质缺陷为动机。我们更多是为了给Windows开发人员提供更加现代化的体验。那个时候我们拥有很多不同的开发工具集，如用于快速应用程序开发的Visual Basic、用于系统级编程的C++/MFC、用于Web应用程序的VBScript和IIS等等。在这些工具集之间，很少甚至没有代码和经验能够共享，而且你的应用程序模型往往是受特定的编程语言支配的。另外，应用程序的互操作是通过COM进行的，这是一种非常底层的机制，程序员通常必须进行显式的内存管理，也没有通用的异常和错误处理机制，等等等等。我们希望创建一个统一的编程平台来解决这些问题，这样一个平台应该能够支持多种编程语言、以面向对象为核心，并能提供一个共享的API框架以及流行的服务，如垃圾收集和异常处理。”</p>
<p><strong>RM:</strong></p>
<p class="r">"There's no real concept of inner classes in C#--you can declare a class inside another class, but it's not really the same as an inner class in Java, unless you're talking about those declared static. Did you consider introducing them when you were designing the language? if so why did you reject them?"</p>
<p>“在C#中没有真正的内部类的概念——你可以在另一个类中声明一个类，但这并不是像Java中那样的真正的内部类，除非你将它声明为静态的。在你设计这个语言的时候，你考虑过加入这个概念吗？如果考虑过，你为什么又放弃了呢？”</p>
<p><strong>AH:</strong></p>
<p class="r">"Correct, C#'s nested classes do not implicitly carry a reference to an instance of the enclosing class, so in Java terms they are like static inner classes. C#'s view is that nesting of classes is just a way to control lexical scoping. If you need to, it takes but a line or two of code to store a reference to an outer class instance in a nested class, so you can pretty easily emulate Java's inner classes.</p>
<p>“是的，C#的嵌套类并不隐式地携带一个指向包装类实例的引用，所以从Java的角度看，这更像是静态的内部类。C#认为类的嵌套只是一种控制词法作用域的途径。如果你需要的话，可以通过一两行代码在包装类示例中存放一个嵌套类的实例，因此你可以非常简单地模拟出Java的内部类。</p>
<p class="r">I think a much bigger difference is C#'s support of lambda expressions and closures, both cornerstones of functional programming. These are still missing from Java, although there have been multiple proposals to add them. In terms of expressiveness I think these are far more important than inner classes."</p>
<p>我想还有一个巨大的区别在于C#对lambda表达式和闭包的支持，这都是函数式编程的基石。但Java依然没有加入这些特性，即便有很多提议要求添加。从表现力的角度来说，我认为这些远比内部类更重要。”</p>
<p><strong>RM:</strong></p>
<p class="r">"I've noticed that the event registration/un-registration thread is unsafe? Why is that?"</p>
<p>“我注意到事件的注册/取消注册不是线程安全的，这是为什么？”</p>
<p><strong>AH:</strong></p>
<p class="r">"No, that's not the case. If you declare an event without explicitly implementing the add and remove accessors (the C# language specification uses the term "field-like event" for this), the C# compiler automatically generates thread-safe accessors for you.</p>
<p>“不，并不是这样的。如果你在声明一个事件时没有显式实现其add和remove访问器（C#语言规范使用‘类似于字段的事件’这种语法），C#编译器会自动为你生成线程安全的访问器。</p>
<p class="r">Perhaps what you are referring to is that you must take extra care when writing thread safe code that raises events. For example, say you have a class that declares and raises an EngineFlameout event:</p>
<p>你所提到的问题，可能是在编写线程安全的用于触发事件的代码时需要格外主义的问题。例如，假设你有一个类，声明并触发了一个EngineFlameout事件：</p>
<pre class="al-code-para">
public class RetroRocket
{
    public event EventHandler EngineFlameout;

    protected virtual void OnEngineFlameout(EventArgs e) {
        if (EngineFlameout != null) EngineFlameout(this, e);
    } 
}
</pre>
<p class="r">This code looks innocent enough, but it isn't thread safe. Specifically, a race condition arises when another thread unsubscribes to the EngineFlameout in the short period of time between the null check and the invocation of the event handler—the event might then become null and an exception would occur. The code can be made thread safe by first copying the event delegate into a local variable, i.e.</p>
<p>这段代码看上去有够简单，但它并不是线程安全的。尤其是，当在null检查和调用事件处理器之间的这一小段时间里，如果其他线程取消注册EngineFlameout事件，就会产生竞态条件——此时事件可能是null，并且会抛出一个异常。只要首先将事件委托复制到一个局部变量中，就能让这段代码变成线程安全的，例如：</p>
<pre class="al-code-para">
protected virtual void OnEngineFlameout(EventArgs e){
    var handler = EngineFlameout; 
    if (handler != null) handler(this, e); 
}
</pre>
<p class="r">This is really no different from code that tests whether an object reference is null before calling a method on the object, it just so happens that we're dealing with events here. As with literally any piece of code written in an imperative programming language, extra work is required to ensure thread safety."</p>
<p>对于任何首先检查对象是否为null，再在对象上调用方法的代码而言都是这样，处理事件时也不例外。事实上，任何使用命令式编程语言编写的代码，都需要做额外的工作才能确保线程安全。”</p>
<p><strong>RM:</strong></p>
<p class="r">"It's probably more of a CLR issue, but why doesn't the "throw" statement work as advertised? It always resets the stack trace even if used without an argument "</p>
<p>“这个可能是CLR的一个大问题，为什么‘throw’语句不像宣称的那样工作？它总是重置堆栈跟踪，即便在使用的时候没有携带参数。”</p>
<p><strong>AH:</strong></p>
<p class="r">"If you "throw" without an argument (known as a rethrow), the StackTrace property of the exception is not reset. That is one of the main differences between the implicit and explicit forms of the "throw" statement. That said, the CPU stack is always unwound upon entry to a catch block, even if that catch block rethrows the exception or throws another exception. This might be the issue you are referring to. It is a natural effect of the CLR's exception handling being built on top of Windows Structured Exception Handling (SEH). A team mate of mine, Mike Stall, has a good blog post on this topic: </p>
<p>“如果你在‘throw’的时候没有携带参数（称作rethrow），异常的StackTrace属性是不会重置的。这是隐式和显式形成‘throw’语句之间的主要差别之一。这就是说，在进入一个catch块的时刻，CPU堆栈永远都是未展开的，即便catch块rethrow了该异常或throw了另一个异常。这可能就是你所提到的问题。这是CLR异常处理的本质特征，因为它是构建在Windows结构化异常处理（SEH）之上的。我的一个团队成员Mike Stall写了一篇有关这个主题的不错的blog：</p>
<p><a href="http://blogs.msdn.com/jmstall/archive/2007/02/07/catch-rethrow.aspx">http://blogs.msdn.com/jmstall/archive/2007/02/07/catch-rethrow.aspx</a>”</p>
<p><strong>RM:</strong></p>
<p class="r">"When you want to hack up an experiment quickly, do you use C#? if not, what do you use?"</p>
<p>“当你需要做一个快速实验时，你会使用C#吗？如果不使用C#，你会用什么？”</p>
<p><strong>AH:</strong></p>
<p class="r">"Well, I pretty much do all my experimentation in C#. Dogfooding is important!"</p>
<p>“嗯，我的几乎所有试验都是用C#做的。狗食是很重要的！”</p>
<p><strong>RM:</strong></p>
<p class="r">"How do you think the introduction of the new "dynamic" type in C# 4.0 will affect application performance and reliability?"</p>
<p>“你如何看待C# 4.0中新引入的‘dynamic’类型？它会对应用程序的性能和可靠性带来哪些影响？”</p>
<p><strong>AH:</strong></p>
<p class="r">"The "dynamic" type in C# 4.0 makes it much, much easier to interface with anything for which you do not have a static .NET type—for example, COM and OLE Automation libraries, the JavaScript and HTML object models, objects from dynamic languages such as Python and Ruby, or REST based Web Services. Where you would previously have to go through reflection-like APIs, such as Type.InvokeMember and ScriptObject.Invoke, you can now just write regular methods calls that are resolved dynamically at run-time.</p>
<p>“C# 4.0中的‘dynamic’类型极大地简化了与其他不具备静态.NET类型的系统之间的接口——如COM和OLE Automation库、JavaScript和HTML对象模型、来自动态语言如Python和Ruby的对象或基于REST的Web Services。之前你必须使用反射风格的API，如Type.InvokeMember和ScriptObject.Invoke，而现在你只需要编写常规的方法调用，而在运行时对其进行动态解析。</p>
<p class="r">C# 4.0 really is a happy marriage of static and dynamic programming—instead of picking one or the other, you get to do both in the same programming language.</p>
<p>C# 4.0真的是静态和动态编程之间完美的联姻——不用破坏其中的任何一方，两者都能用同一种编程语言来实现。</p>
<p class="r">I expect performance and reliability to both benefit. Similar to IronPython and IronRuby, C# 4.0 uses the Dynamic Language Runtime (DLR) for all dynamic dispatch, and the DLR has been extensively tuned to provide great performance."</p>
<p>我认为这对性能和可靠性方面都有益处。和IronPython和IronRuby类似，C# 4.0使用动态语言运行时（DLR）进行所有的动态分派，而DLR经过了大量的微调，可以提供卓越的性能。”</p>
<p><strong>RM:</strong></p>
<p class="r">"By the time .NET came on the scene, Java had already been around for a number of years as a successful managed platform, and whilst there are obvious similarities between C# and Java, there are also some differences. When designing the C# programming language, what did you learn from the Java experience, and were there any shortcomings in that language that you tried to avoid?"</p>
<p>“在.NET走上历史舞台的时候，Java作为一款成功的托管平台已经风光了好几年了。而C#和Java之间有很多明显类似的地方，也有很多不同。在设计C#编程语言时，你从Java中借鉴了哪些经验，又有哪些不足是你需要努力避免的？”</p>
<p><strong>AH:</strong></p>
<p class="r">"Well, Java certainly demonstrated the value of type safety, exception handling, and garbage collection in a mainstream programming language. Those just do wonders for productivity. </p>
<p>“嗯，Java的确展示出了再主流编程语言中，类型安全、异常处理和垃圾收集的巨大价值。这些都是生产力方面的奇迹。</p>
<p class="r">Many programming languages influenced C#. Java was one of them, but C++ and Delphi also influenced our design. If I had to call out a few of the differences between C# and Java from a programming language point of view, I might mention C#'s unified type system (value types, boxing, and unboxing), first class support for properties and events, delegate types, separation of logical naming and physical packaging, and unsafe code.</p>
<p>C#受到很多编程语言的影响。Java正是其中之一，但C++和Delphi也影响了我们的设计。如果非要从编程语言的角度说出C#和Java之间的区别，我认为这就是C#的统一类型系统（值类型、装箱和拆箱），对类的一流支持，包括属性和事件、委托类型、逻辑名字和物理打包之间的分离、以及不安全代码。</p>
<p class="r">Also, C# and .NET's design for generics doesn't rely on erasure but rather has true representation of type parameters at run-time. I think this brings about many important advantages. And, of course, in C# 3.0 we introduced Language Integrated Query (LINQ) which really has no counterpart in Java."</p>
<p>另外，C#和.NET对泛型的支持并不依赖于擦除，而是对运行时类型参数的真实体现。我想这会带来很多优点。当然，在C# 3.0中我们还引入了语言集成查询（LINQ），这在Java中绝对没有对应的特性。”</p>
<p><strong>RM:</strong></p>
<p class="r">"Do you believe the LINQ query comprehension syntax (is this its official name?) is a successful feature, or more of an interesting experiment?"</p>
<p>“你认为LINQ查询内涵语法（这是官方的名字吗？）是一种成功的特性，还是更多只是一种有趣的实验？”</p>
<p><strong>AH:</strong></p>
<p class="r">"We call them "Query Expressions" in the language reference, and, yes, I absolutely consider them a successful feature. The fact that you can write your queries in a high-level SQL-like syntax is incredibly important to a lot of programmers. It makes the feature much more approachable. Yet, because the compiler simply turns queries into sequences of method calls with lambda expression arguments, we get a great extensibility story.</p>
<p>“在语言参考中我们称之为‘查询表达式’，我绝对认为这是一种成功的特性。实际上可以用高级的类似于SQL的语法来编写查询，对很多程序员来说出乎意料的重要。这使得这种特性更加贴近开发者。而且，因为编译器只是简单地将查询转变为一系列对携带lambda表达式参数的方法调用，所以我们能得到强大的可扩展性。</p>
<p class="r">When I talk to C# or VB.NET programmers now, most of them use LINQ in their day to day coding, be it LINQ to Objects, LINQ to XML, LINQ to SQL, the Entity Framework, or one of the many third party LINQ providers. And if they don't, it is usually more a question of downlevel deployment than usefulness of the feature. The ability to query in-memory objects, XML, and relational data with a single unified syntax is just really compelling."</p>
<p>现在，在我和C#或VB.NET程序员聊天时，发现他们中的大多数都在每天的编程工作中使用LINQ，他们使用LINQ to Objects、LINQ to XML、LINQ to SQL、Entity Framework或者很多第三方LINQ提供程序。如果他们不使用LINQ，在降级部署时就会遇到比使用该特性更多的问题。使用单一统一语法来查询内存中对象、XML和关系型数据的这种能力，是确实可以通过编译的。”</p>
<p><strong>RM:</strong></p>
<p class="r">"Nemerle showed that adding meta-programming support to C# was a serious possibility? Have you ever considered it?"</p>
<p>“Nemerle表明在C#中添加元编程支持是很有可能的？你考虑过这一点吗？”</p>
<p><strong>AH:</strong></p>
<p class="r">"It is one of the directions we are looking at for future versions of C#. Indeed, I see meta-programming as a piece of our bigger "Compiler as a Service" theme that we are working on for a future release. We want to open up our compiler so it becomes an API you can call to compile a piece of code and get back expression trees and/or IL. This enables a whole host of scenarios, such as application programmability, an interactive prompt, user-written refactorings, and domain specific languages that have little islands of C# imbedded in them."</p>
<p>“这是我们所关注的C#未来版本的一个发展方向。确实，我认为元编程是我们‘编译器即服务’这个庞大主题中的一部分，我们正在为他的发布进行着努力。我们希望开放编译器，使其成为一个API，你可以调用这个API来编译代码片段，并得到表达式树和/或IL。这实现了一种完全宿主的场景，如应用程序可编程性、交互式提示符、用户编写的重构和领域特定语言，所有这些都可以提供一小块地方将C#嵌入进去。</p>
<p>（完）</p>
</div><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1425189.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47996/" target="_blank">7月编程语言排行榜</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>透过IL看C# (3)——foreach语句</title><link>http://www.cnblogs.com/AndersLiu/archive/2009/02/04/csharp-via-il-foreach.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Tue, 03 Feb 2009 17:17:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2009/02/04/csharp-via-il-foreach.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1383528.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2009/02/04/csharp-via-il-foreach.html#Feedback</comments><slash:comments>23</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1383528.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1383528.html</trackback:ping><description><![CDATA[摘要: foreach语句是C#中一种重要的循环语句，用于遍历一个数组或对象集合中的每一个元素。这一篇文章介绍了在面对数组、IEnumerable接口和自定义类型时，编译器为foreach语句生成的IL代码。&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2009/02/04/csharp-via-il-foreach.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1383528.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47995/" target="_blank">Google Voice 上手</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>透过IL看C# (外一篇)——警惕常量陷阱</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sun, 23 Nov 2008 02:05:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1339257.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html#Feedback</comments><slash:comments>37</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1339257.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1339257.html</trackback:ping><description><![CDATA[<h1 class="al-title">透过IL看C# (外一篇)<br/>警惕常量陷阱</h1>
<div class="al-copy">
	<p>原文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html" title="透过IL看C# (外一篇)——警惕常量陷阱">http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html</a></p>
	<p>原创：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：常量的含义本是“永远不会变的量”，但是如果作为类库开发人员，把常量用作“可以由我变，但不能由你变”的量，那就可能铸成大错了。</p>
</div>

<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
/* 468x60, 创建于 08-10-28, 博客园文章内容广告new */
google_ad_slot = "2526711197";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p class="al-p">下面是老刘写的一个类库中的一个类：</p>
<div class="al-ins">
	<p class="al-ins-title">代码1 - 老刘的“类库”</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">namespace AndersLiu.CSharpViaIL.Constant_Library
{
	public class Library
	{
		public const int Version = 1;
		
		public string GetVersion()
		{
			return string.Format("You are currently using version {0}.", Version);
		}
	}
}</textarea>
</div>
<p class="al-p">其中的常量Version表示类库的当前版本，而方法GetVersion会给出描述当前版本的字符串。用下面的命令行可以将其编译为一个DLL：</p>
<div class="al-ins">
<p class="al-p">csc /t:library Library.cs</p>
</div>
<p class="al-p">接下来，一个倒霉的家伙从老刘这买了这个类库，写了自己的程序：</p>
<div class="al-ins">
	<p class="al-ins-title">代码2 - 老刘的“消费者”</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">namespace AndersLiu.CSharpViaIL.Constant_Program
{
	using System;
	using AndersLiu.CSharpViaIL.Constant_Library;
	
	class Program
	{
		static void Main()
		{
			Console.WriteLine("Library Version: {0}", Library.Version);
			
			Library lib = new Library();
			string verstr = lib.GetVersion();
			Console.WriteLine(verstr);
		}
	}
}</textarea>
</div>
<p class="al-p">这个家伙用下面的命令行编译了自己的程序：</p>
<div class="al-ins">
<p class="al-p">csc /r:Library.dll Program.cs</p>
</div>
<p class="al-p">运行程序，一切正常，屏幕显示出预期的结果：</p>
<div class="al-ins">
<p class="al-p">Library Version: 1<br />
You are currently using version 1.</p>
</div>
<p class="al-p">过了两天，老刘升级代码了。都升级哪些部分了呢？把Version的值改成2了，然后重新编译了Library。所以代码就不贴了。前面这个家伙因为很乖，从老刘这里买了正版，所以老刘承诺免费升级50次，因此他拿到了新版的Library.dll。</p>
<p class="al-p">当这个家伙再跑自己的程序（注意他没有重新编译自己的代码），问题来了：</p>
<div class="al-ins">
<p class="al-p">Library Version: 1<br />
You are currently using version 2.</p>
</div>
<p class="al-p">我们看到，通过常量访问得到的版本依然是1，而通过类库方法得到的版本字符串是2。</p>
<p class="al-p">这是怎么回事呢？让我们祭出ILDasm，看一下Version常量的定义：</p>
<div class="al-ins">
	<p class="al-ins-title">代码3 - IL中的常量定义</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">.field public static literal int32 Version = int32(0x00000002)</textarea>
</div>
<p class="al-p">Version定义中的关键字literal表明，这是一个字面常量值。“字面”意味着其值将被直接编译到IL代码中，而不会保留对这个常量的引用。</p>
<p class="al-p">因此，当我们看到Program.cs中Main方法的IL代码后，就不那么吃惊了：</p>
<div class="al-ins">
	<p class="al-ins-title">代码4 - 引用Version常量部分的IL代码</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">IL_0001:  ldstr      "Library Version: {0}"
IL_0006:  ldc.i4.1
IL_0007:  box        [mscorlib]System.Int32
IL_000c:  call       void [mscorlib]System.Console::WriteLine(string,
                                                              object)
</textarea>
</div>
<p class="al-p">我们可以看到，这里根本没有Version这个常量的影子。只有IL_0006: ldc.i4.1这一行直接加载了数值1——在编译Program的时候，类库中Version常量的值。</p>
<p class="al-p">有朋友可能想说，那重新编译一下Program不就解决问题了？可问题在于，.NET的一个重要原则就是部署容易和消除DLL陷阱。不能强制要求客户在类库升级后还要重新编译自己的程序。</p>
<p class="al-p">所以，对于类库设计人员来说，当暴露一个公开的常量时要非常小心，只有确信一个值永远不会发生变化（包括今后的一系列升级）时，才能使用常量。否则请使用只读（只有get访问器）属性或只读（readonly）字段来返回这样的值。</p>


<div class="al-ins">
<p class="al-p">返回目录：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html" title="透过IL看C#">透过IL看C#</a></p>
</div>
<script type="text/javascript" src="http://files.cnblogs.com/AndersLiu/(1.5.1)shCore.js"></script>
<script type="text/javascript" src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushCSharp.js"></script>
<script type="text/javascript" src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushXml.js"></script>
<script type="text/javascript" language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1339257.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47994/" target="_blank">Google号召社区力量为互联网加速</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>透过IL看C# (2)——switch语句（下）</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/11/06/csharp-via-il-switch-2.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 06 Nov 2008 02:04:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/11/06/csharp-via-il-switch-2.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1327848.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/11/06/csharp-via-il-switch-2.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1327848.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1327848.html</trackback:ping><description><![CDATA[<h1 class="al-title">透过IL看C# (2)<br/>switch语句（下）</h1>
<div class="al-copy">
	<p>原文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/06/csharp-via-il-switch-2.html" title="透过IL看C# (2)——switch语句（下）">http://www.cnblogs.com/AndersLiu/archive/2008/11/06/csharp-via-il-switch-2.html</a></p>
	<p>原创：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：switch语句是C#中常用的跳转语句，可以根据一个参数的不同取值执行不同的代码。本文介绍了当向switch语句中传入不同类型的参数时，编译器为其生成的IL代码。这一部分介绍的是，在switch语句中使用字符串类型的情况。</p>
</div>

<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
/* 468x60, 创建于 08-10-28, 博客园文章内容广告new */
google_ad_slot = "2526711197";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p class="al-p">之前我们介绍了在switch语句中使用整数类型和枚举类型的情况。这一部分继续介绍使用string类型的情况。string类型是switch语句接受的唯一一种引用类型参数。</p>
<p class="al-p">下面来看一段C#代码。</p>
<div class="al-ins">
<p class="al-ins-title">代码1 - 使用string类型参数的switch语句</p>
<textarea class="C#" cols="60" name="al-code" rows="25">
static void TestSwitchString(string s)
{
	switch(s)
	{
		case null: Console.WriteLine("<null>"); break;
		case "one": Console.WriteLine(1); break;
		case "two": Console.WriteLine(2); break;
		case "three": Console.WriteLine(3); break;
		case "four": Console.WriteLine(4); break;
		default: Console.WriteLine("<Unknown>"); break;
	}

	Console.WriteLine("After switch.");
}
</textarea>
</div>
<p class="al-p">代码1展示的方法中只有一个switch语句，它接收一个字符串类型的参数s，并根据6种不同的情况显示不同的文字。它将被编译器翻译成什么样子的代码呢？这个switch语句是否依然能利用IL中的switch指令呢？</p>
<p class="al-p">答案马上揭晓。且看由代码1得到的IL，如代码2所示。</p>
<div class="al-ins">
<p class="al-ins-title">代码2 - 代码1得到的IL代码</p>
<textarea class="C#" cols="60" name="al-code" rows="25">
.method private hidebysig static void  TestSwitchString(string s) cil managed
{
  // Code size       124 (0x7c)
  .maxstack  2
  .locals init (string V_0)
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  stloc.0
  IL_0003:  brfalse.s  IL_003b
  IL_0005:  ldloc.0
  IL_0006:  ldstr      "one"
  IL_000b:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_0010:  brtrue.s   IL_0047
  IL_0012:  ldloc.0
  IL_0013:  ldstr      "two"
  IL_0018:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_001d:  brtrue.s   IL_004f
  IL_001f:  ldloc.0
  IL_0020:  ldstr      "three"
  IL_0025:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_002a:  brtrue.s   IL_0057
  IL_002c:  ldloc.0
  IL_002d:  ldstr      "four"
  IL_0032:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_0037:  brtrue.s   IL_005f
  IL_0039:  br.s       IL_0067
  IL_003b:  ldstr      "<null>"
  IL_0040:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0045:  br.s       IL_0071
  IL_0047:  ldc.i4.1
  IL_0048:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_004d:  br.s       IL_0071
  IL_004f:  ldc.i4.2
  IL_0050:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0055:  br.s       IL_0071
  IL_0057:  ldc.i4.3
  IL_0058:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_005d:  br.s       IL_0071
  IL_005f:  ldc.i4.4
  IL_0060:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0065:  br.s       IL_0071
  IL_0067:  ldstr      "<Unknown>"
  IL_006c:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0071:  ldstr      "After switch."
  IL_0076:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_007b:  ret
} // end of method Program::TestSwitchString
</textarea>
</div>
<p class="al-p">呵呵，第一感觉就是，没见到switch指令。下面我们来简要地分析一下这些代码。</p>
<p class="al-p">首先是IL_0000到IL_0002，这里还是首先将参数复制到了一个局部变量中，在前面介绍使用整数和枚举的情况时，我们也看到了类似的情况。老刘对此的猜测是，这是由于IL中<em>没有</em>修改方法参数的指令（如starg），而C#语言支持在方法体内给参数赋值——虽然这个值的改动不会影响到调用方法时传递进来的实参。因此，为了满足C#语言的这种特点，编译器生成了一个局部变量，并在方法一开始将参数值复制进来，之后便可以操作这个参数了（修改其值）。再次重申，这是老刘自己的猜测。</p>
<p class="al-p">如果上述猜测成立的话，那么C#编译器实际上还可以做一些改进，即判断如果方法体内没有修改参数值，则可以省去这个局部变量。</p>
<p class="al-p">接下来的IL_0003一行是一个条件跳转，ILDasm给出的指令是brfalse，其实写brnull更合适。brfalse和brnull还有brzero指令是一组同义词（他们底层的指令代码是一样的）。这组指令的作用是，从栈顶取出一个元素，判断其值是否为0（值类型所有字段全零、引用类型是null），如果是的话，则跳转到指令参数所指定的语句去执行，否则继续执行下一条指令。</p>
<p class="al-p">很明显，如果参数s是null的话，该指令将导致执行流程直接跳转到表示case null的指令块中。</p>
<p class="al-p">接下来，从IL_0005到IL_0037，每四条指令为一组，分别比较了s和四个不同的字符串的相等性，如果与某一个值相等，则跳转到对应的地址，该地址就是这个字符串常量对应的case子句。字符串的相等性是通过op_Equality方法进行的，这相当于使用“==”运算符判断字符串是否相等。</p>
<p class="al-p">每个指令块（case子句）执行完毕之后，都会有一行br.s IL_0071，这个IL_0071对应的就是switch语句之后的其他语句。</p>
<p class="al-p">由此可见，对于代码1所示的C#程序片段，编译器实际上是将switch语句翻译成了相当于一串if语句的形式。那么，如此一来，当case子句过多时，岂不是会导致程序变慢？</p>
<p class="al-p">下面再来看一段代码，我们在switch中放入更多的case子句，请参见代码3。</p>
<div class="al-ins">
<p class="al-ins-title">代码3 - 拥有更多case子句的switch语句</p>
<textarea class="C#" cols="60" name="al-code" rows="25">
static void TestSwitchString2(string s)
{
  switch (s)
  {
    case null: Console.WriteLine("<null>"); break;
    case "one": Console.WriteLine(1); break;
    case "two": Console.WriteLine(2); break;
    case "three": Console.WriteLine(3); break;
    case "four": Console.WriteLine(4); break;
    case "five": Console.WriteLine(5); break;
    default: Console.WriteLine("<Unknown>"); break;
  }

  Console.WriteLine("After switch.");
}
</textarea>
</div>
<p class="al-p">哈哈，老刘不厚道啊，不就多了一个case "five"子句么。</p>
<p class="al-p">是的，就多这一个。下面我们来看一下代码3对应的IL代码。</p>
<div class="al-ins">
<p class="al-ins-title">代码4 - 代码3对应的IL代码</p>
<textarea class="C#" cols="60" name="al-code" rows="25">
.method private hidebysig static void  TestSwitchString2(string s) cil managed
{
  // Code size       205 (0xcd)
  .maxstack  4
  .locals init (string V_0,
           int32 V_1)
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  stloc.0
  IL_0003:  brfalse.s  IL_0084
  IL_0005:  volatile.
  IL_0007:  ldsfld     class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{16CB032A-D97A-40BA-84F1-233334FEF4FA}'::'$$method0x6000007-1'
  IL_000c:  brtrue.s   IL_0057
  IL_000e:  ldc.i4.5
  IL_000f:  newobj     instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::.ctor(int32)
  IL_0014:  dup
  IL_0015:  ldstr      "one"
  IL_001a:  ldc.i4.0
  IL_001b:  call       instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
                                                                                                                !1)
  IL_0020:  dup
  IL_0021:  ldstr      "two"
  IL_0026:  ldc.i4.1
  IL_0027:  call       instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
                                                                                                                !1)
  IL_002c:  dup
  IL_002d:  ldstr      "three"
  IL_0032:  ldc.i4.2
  IL_0033:  call       instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
                                                                                                                !1)
  IL_0038:  dup
  IL_0039:  ldstr      "four"
  IL_003e:  ldc.i4.3
  IL_003f:  call       instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
                                                                                                                !1)
  IL_0044:  dup
  IL_0045:  ldstr      "five"
  IL_004a:  ldc.i4.4
  IL_004b:  call       instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
                                                                                                                !1)
  IL_0050:  volatile.
  IL_0052:  stsfld     class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{16CB032A-D97A-40BA-84F1-233334FEF4FA}'::'$$method0x6000007-1'
  IL_0057:  volatile.
  IL_0059:  ldsfld     class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>{16CB032A-D97A-40BA-84F1-233334FEF4FA}'::'$$method0x6000007-1'
  IL_005e:  ldloc.0
  IL_005f:  ldloca.s   V_1
  IL_0061:  call       instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::TryGetValue(!0,
                                                                                                                        !1&)
  IL_0066:  brfalse.s  IL_00b8
  IL_0068:  ldloc.1
  IL_0069:  switch     ( 
                        IL_0090,
                        IL_0098,
                        IL_00a0,
                        IL_00a8,
                        IL_00b0)
  IL_0082:  br.s       IL_00b8
  IL_0084:  ldstr      "<null>"
  IL_0089:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_008e:  br.s       IL_00c2
  IL_0090:  ldc.i4.1
  IL_0091:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0096:  br.s       IL_00c2
  IL_0098:  ldc.i4.2
  IL_0099:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_009e:  br.s       IL_00c2
  IL_00a0:  ldc.i4.3
  IL_00a1:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_00a6:  br.s       IL_00c2
  IL_00a8:  ldc.i4.4
  IL_00a9:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_00ae:  br.s       IL_00c2
  IL_00b0:  ldc.i4.5
  IL_00b1:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_00b6:  br.s       IL_00c2
  IL_00b8:  ldstr      "<Unknown>"
  IL_00bd:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_00c2:  ldstr      "After switch."
  IL_00c7:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_00cc:  ret
} // end of method Program::TestSwitchString2
</textarea>
</div>
<p class="al-p">耶？有奇怪的东西出现。你是不是第一眼也看到了IL_0007这一条指令了？别忙，我们一点一点地拆解它。</p>
<p class="al-p">首先，这条指令是ldsfld——加载静态字段。然后给出了字段的类型，是class [mscorlib]System.Collections.Generic.Dictionary`2&lt;string,int32&gt;类型，这是一个已经实例化的字典泛型类。然后就是具体要加载的字段了，其形式应该为“ClassName::FieldName”；因此可以看出，这个字段所属的类型是&lt;PrivateImplementationDetails&gt;{16CB032A-D97A-40BA-84F1-233334FEF4FA}，字段的名字是$$method0x6000007-1。</p>
<div class="al-ins">
<p class="al-ins-title">注解</p>
<p class="al-p">在ILAsm语言中，#、$、@、_（下划线）和`（注意不是单引号，而是和波浪线“~”位于同一键位上的撇字符）都是标识符中的合法字符。另外，ILAsm还支持用单引号将标识符包围起来，这样甚至还可以在其中使用一些非法字符。</p>
</div>
<p class="al-p">用ILDasm以图形化界面打开生成的程序集，果然可以看到这样一个类型和他的这个字段，如图1所示。</p>
<div class="al-ins">
<p class="al-ins-title">图1 - 编译器为switch语句生成的内部类型</p>
<img src="http://www.cnblogs.com/images/cnblogs_com/AndersLiu/161779/o_csharp-via-il-switch-fig01.jpg" alt="图1 - 编译器为switch语句生成的内部类型" />
</div>
<p class="al-p">在继续进行之前，老刘再来给大家做一个猜测——这个类型的名字和字段的名字是咋来的。</p>
<p class="al-p">首先是类型的名字，名字中的尖括号和花括号主要是用来防止与用户编写的标识符发生冲突，因为在绝大多数高级语言中，尖括号和花括号都不能用在标识符中。尖括号中的“PrivateImplementationDetails”明确指出了这个类型是编译器内部实现的，是不属于用户的。尖括号后面，一对花括号之间很明显是一个GUID，观察一下就会发现，这个GUID就是当前模块的MVID（双击“M A N I F E S T”节点可以看到mvid）。</p>
<p class="al-p">接下来是字段的名字，前导的两个$也是防止命名冲突的。之后的method0x6000007，表示这是给元数据标识为“0x6000007”的方法使用的。最后的“-1”表示这个字段是这个方法中用到的第一个内部实现的结构。</p>
<p class="al-p">好了，现在我们知道了，编译器自动为我们生成了一个类型，并在其中提供了一个字典类的静态字段。接下来，我们详细看一下发生了什么。</p>
<p class="al-p">首先IL_0000至IL_0003这几条指令和代码2中的一样，此处不再赘述。IL_0005一行是一个前缀指令volatile.，表明它后面的ldsfld指令要加载的字段是一个“易变”字段，也就是说这个字段可能会被其他进程改变。这就告诉了运行时环境，在访问字段时不要缓存它的值。</p>
<div class="al-ins">
<p class="al-ins-title">注解</p>
<p class="al-p">在IL指令中，前缀指令只修饰紧随其后的一条指令，其他指令不受影响。</p>
</div>
<p class="al-p">IL_0007和IL_000C两行判断之前提到的那个“内部字段”是否为null，如果不是null则跳转到IL_0057，否则继续执行下面的指令，建立一个新的Dictionary&lt;string,int32&gt;类型的字段。同样，这里的brtrue写作brinst更为合适（brtrue和brinst也是一组同义词，其指令代码是一样的）。</p>
<p class="al-p">接下来的IL_000e到IL_0052，先是初始化了一个字典类对象，然后分别将case子句中出现的五个字符串（null除外）作为key插入到了这个字典中，每个字符串对应一个整数，从0到4。最后将这个对象保存在“内部字段”中。</p>
<p class="al-p">接下来走到了IL_0057，也就是之前判断“内部字段”不为空时跳转到的位置。从IL_0057到IL_0061是通过调用字典类的TryGetValue方法尝试从字典中找到key的值是switch参数s所指定的项。</p>
<p class="al-p">从这里，我们可以看到，在IL中调用方法时，参数是自左向右依次压入堆栈的；如果调用的是实例方法，则在哪个对象上调用方法，应该最先将这个对象压入堆栈。例如在这里，首先压入了“内部字段”，然后是第0个局部变量（复制进来的参数s），最后是第1个参数的地址。</p>
<p class="al-p">此外，我们还看到了C#中out参数是如何实现的。对于方法声明，out参数会被声明为类似“Type&”这样的类型，这是一个托管指针。在传值时，通过ldloca指令可以得到局部变量的地址。</p>
<p class="al-p">IL_0066，如果上述TryGetValue方法没有找到对应的key，则跳转到IL_00b8——从这一行的内容来看——是default子句的位置。</p>
<p class="al-p">IL_0069，啊哈，看到了我们所熟悉的switch指令。根据刚刚取到的整数值，跳转到个个case子句中去运行。</p>
<p class="al-p">再看switch指令之后的IL_0082位置上的指令，这是一个无条件跳转，直接跳到IL_00b8——default子句。回顾一下switch指令的用法，当从栈顶取到的整数值比switch指令中的跳转地址数量要大时，会忽略switch指令，直接执行接下来的指令。所以，可以认为switch指令后面紧随的指令应该类似于C#语言中switch语句中的default子句。但在这里，编译器按照习惯，将default子句对应的IL代码放到了最后，并在switch指令之后紧接着放置一个无条件跳转，跳转到default子句中。</p>
<p class="al-p">至此，这段代码基本就分析完了。</p>
<h2 class="al-t1">小结</h2>
<p class="al-p">本文介绍了在switch语句中使用字符串对象作为参数的情形。</p>
<p class="al-p">可以看到，当case子句数量不多时，编译器会将其翻译为类似于一系列if语句这样的结构，并通过“==”运算符来与每种case进行比较。</p>
<p class="al-p">当case子句的数量较多时，编译器则会生成一个内部类，并提供一个字典字段。这个字典字段的key是字符串类型，value是整数类型；其中key记录了每种case，而value记录了对应case子句的序号。之后，以switch语句的参数s作为key，取出对应的value，再利用switch指令做跳转。</p>
<p class="al-p">这样做是利用了Dictionary&lt;TKey,TValue&gt;类型通过key来取值的时间复杂度接近于O(1)这种特性（请参见<a href="http://msdn.microsoft.com/zh-cn/library/xfhwa508(VS.80).aspx" target="_blank" title="Dictionary 泛型类">MSDN上关于Dictionary泛型类的说明</a>），有助于提高效率。此外，这个字段在需要的时候才进行初始化，并且只初始化一次，进一步提高了程序的整体效率。</p>
<p class="al-p">如果你的程序中用了大量if语句来判断一个字符串对象是否具有给定的值，不妨将其改为用switch语句实现。如果你有其他引用类型对象，要进行类似的判断，又不能使用switch语句（C#语法不允许），可以尝试自己写一个字典类的字段，以给定的几种可能的对象做key，以连续的整数值作为value，然后每次判断时，通过以给定对象（参数）作为key，取到vlaue后再用switch进行判断。</p>

<div class="al-ins">
<p class="al-p">返回目录：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html" title="透过IL看C#">透过IL看C#</a></p>
</div>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shCore.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushCSharp.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1327848.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47989/" target="_blank">Twitter无处不在 魔兽世界Twitter发送器插件发布</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>透过IL看C# (1)——switch语句（上）</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 03 Nov 2008 14:42:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1325882.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html#Feedback</comments><slash:comments>27</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1325882.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1325882.html</trackback:ping><description><![CDATA[<h1 class="al-title">透过IL看C# (1)<br/>switch语句（上）</h1>
<div class="al-copy">
	<p>原文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html" title="透过IL看C# (1)——switch语句（上）">http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html</a></p>
	<p>原创：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：switch语句是C#中常用的跳转语句，可以根据一个参数的不同取值执行不同的代码。本文介绍了当向switch语句中传入不同类型的参数时，编译器为其生成的IL代码。这一部分介绍的是，在switch语句中使用整数类型和枚举类型的情况。</p>
</div>

<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
/* 468x60, 创建于 08-10-28, 博客园文章内容广告new */
google_ad_slot = "2526711197";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p class="al-p">switch语句是C#中常用的跳转语句，可以根据一个参数的不同取值执行不同的代码。switch语句可以具备多个分支，也就是说，根据参数的N种取值，可以跳转到N个代码段去运行。这不同于if语句，一条单独的if语句只具备两个分支（这是因为if语句的参数只能具备true或false两种取值），除非使用嵌套if语句。</p>
<p class="al-p">switch语句能够接受的参数是有限制的，简单来说，只能是整数类型、枚举或字符串。本文就从整数、枚举和字符串这三种类型的switch语句进行介绍。</p>
<h2 class="al-t1">switch指令</h2>
<p class="al-p">在进入正题之前，先为大家简要介绍一下IL汇编语言中的switch指令。switch指令（注意和C#中的switch语句区分开）是IL中的多分支指令，它的基本形式如下：</p>
<p class="al-p"><span class="al-inline-code">switch (Label_1, Label_2, Label_3…)</span></p>
<p class="al-p">其中switch是IL关键字，Label_1~Label_N是一系列标号（和goto语句中用到的标号一样），标号指明了代码中的位置。这条指令的运行原理是，从运算栈顶弹出一个无符号整数值，如果该值是0，则跳转到由Label_1指定的位置执行；如果是1，则跳转到Labe_2；如果是2，则跳转到Label_3；以此类推。</p>
<p class="al-p">如果栈顶弹出的值不在标号列表的范围之内（0~N-1），则忽略switch指令，跳到switch指令之后的一条指令开始执行。因此，对于switch指令来说，其 “default子句”是在最开头的。</p>
<p class="al-p">此外，Label_x所引用的标号位置只要位于当前方法体就可以，不必非要在switch指令的后面。</p>
<p class="al-p">好了，后面我们会看到switch指令的实例的。</p>
<h2 class="al-t1">使用整数类型的switch语句</h2>
<div class="al-ins">
	<p class="al-ins-title">代码1 - 使用整数类型参数的switch语句，取值连续</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
static void TestSwitchInt(int n)
{
	switch(n)
	{
		case 1: Console.WriteLine("One"); break;
		case 2: Console.WriteLine("Two"); break;
		case 3: Console.WriteLine("Three"); break;
	}
}
</textarea>
</div>
<p class="al-p">代码1中的switch语句接受的参数n是int类型的，并且我们观察到，在各个case子句中的取值都是连续的。将这段代码写在一个完整的程序中，并进行编译。之后使用ildasm打开生成的程序集，可以看到对应的IL代码如代码2所示。</p>
<div class="al-ins">
	<p class="al-ins-title">代码2 – 代码1生成的IL代码</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
.method private hidebysig static void  TestSwitchInt(int32 n) cil managed
{
  // Code size       56 (0x38)
  .maxstack  2
  .locals init (int32 V_0)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldc.i4.1
  IL_0004:  sub
  IL_0005:  switch     ( 
                        IL_0017,
                        IL_0022,
                        IL_002d)
  IL_0016:  ret
  IL_0017:  ldstr      "One"
  IL_001c:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0021:  ret
  IL_0022:  ldstr      "Two"
  IL_0027:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002c:  ret
  IL_002d:  ldstr      "Three"
  IL_0032:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0037:  ret
} // end of method Program::TestSwitchInt
</textarea>
</div>
<p class="al-p">我们可以看到，首先IL_0000和IL_0001两行代码将参数n存放到一个局部变量中，然后IL_0002到IL_0004三行将这个变量的值减去1，并将结果留在运算栈顶。啊哈，参数值减去1，要进行判断的几种情况不就变成了0、1、2了么？是的。在接下来的switch指令里，针对这三种取值给出了三个地址IL_0017、IL_0022和IL_002d。这三个地址处的代码，分别就是取值为1、2、3时需要执行的代码。</p>
<p class="al-p">以上是取值连续的情形。如果各个case子句中给出的值并不连续呢？我们来看一下下面的C#代码：</p>
<div class="al-ins">
	<p class="al-ins-title">代码3 – 使用整数类型参数的switch语句，取值不连续</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
static void TestSwitchInt2(int n)
{
	switch(n)
	{
		case 1: Console.WriteLine("1"); break;
		case 3: Console.WriteLine("3"); break;
		case 5: Console.WriteLine("5"); break;
	}
}
</textarea>
</div>
<p class="al-p">代码3编译生成的程序集中，编译器生成的IL代码如下：</p>
<div class="al-ins">
	<p class="al-ins-title">代码4 – 代码3生成的IL代码</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
.method private hidebysig static void  TestSwitchInt2(int32 n) cil managed
{
  // Code size       64 (0x40)
  .maxstack  2
  .locals init (int32 V_0)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldc.i4.1
  IL_0004:  sub
  IL_0005:  switch     ( 
                        IL_001f,  // 0
                        IL_003f,  // 1
                        IL_002a,  // 2
                        IL_003f,  // 3
                        IL_0035)  // 4
  IL_001e:  ret
  IL_001f:  ldstr      "1"
  IL_0024:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0029:  ret
  IL_002a:  ldstr      "3"
  IL_002f:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0034:  ret
  IL_0035:  ldstr      "5"
  IL_003a:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_003f:  ret
} // end of method Program::TestSwitchInt2
</textarea>
</div>
<p class="al-p">看到代码4，第一感觉就是switch指令中跳转地址的数量和C#程序中switch语句中的取值数不相符。但仔细观察后可以发现，switch指令中针对0、2、4（即switch语句中的case 1、3、5）这三种取值给出了不同的跳转地址。而对于1、3这两种取值（在switch语句中并没有出现）则给出了同样的地址IL_003f，看一下这个地址，是语句ret。</p>
<p class="al-p">也就是说，对于取值不连续的情况，编译器会自动用“default子句”的地址来填充switch指令中的“缝隙”。当然，代码4因为过于简单，所以“缝隙值”直接跳转到了方法的结尾。</p>
<p class="al-p">那么，如果取值更不连续呢？那样的话，switch指令中就会有大量的“缝隙值”。要知道，switch指令和之后的跳转地址列表都是指令的一部分，缝隙值的增加势必会导致程序集体积的增加啊。呵呵，不必担心，编译器很聪明，请看下面的代码：</p>
<div class="al-ins">
	<p class="al-ins-title">代码5 – 使用整数类型参数的switch语句，取值非常不连续</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
static void TestSwitchInt3(int n)
{
	switch(n)
	{
		case 10: Console.WriteLine("10"); break;
		case 30: Console.WriteLine("30"); break;
		case 50: Console.WriteLine("50"); break;
	}
}
</textarea>
</div>
<p class="al-p">在代码5中，switch语句的每个case子句中给出的取值之间都相差20，这意味着如果再采用前面所述“缝隙值”的做法，switch指令中将有多达41个跳转地址，而其中有效的只有3个。但现代的编译器明显不会犯这种低级错误。下面给出编译器为代码5 生成的IL：</p>
<div class="al-ins">
	<p class="al-ins-title">代码6 – 代码5生成的IL代码</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
.method private hidebysig static void  TestSwitchInt3(int32 n) cil managed
{
  // Code size       51 (0x33)
  .maxstack  2
  .locals init (int32 V_0)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldc.i4.s   10
  IL_0005:  beq.s      IL_0012
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.s   30
  IL_000a:  beq.s      IL_001d
  IL_000c:  ldloc.0
  IL_000d:  ldc.i4.s   50
  IL_000f:  beq.s      IL_0028
  IL_0011:  ret
  IL_0012:  ldstr      "10"
  IL_0017:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001c:  ret
  IL_001d:  ldstr      "30"
  IL_0022:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0027:  ret
  IL_0028:  ldstr      "50"
  IL_002d:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0032:  ret
} // end of method Program::TestSwitchInt3
</textarea>
</div>
<p class="al-p">从代码6中我们会发现，switch指令不见了，在IL_0005、IL_000a和IL_000f三处分别出西安了beq.s指令，这个指令是beq指令的简短形式。当跳转位置和当前位置之差在一个sbyte类型的范围之内时，编译器会自动选择简短形式，目的是缩小指令集的体积。而beq指令的作用是从运算栈中取出两个值进行比较，如果两个值相等，则跳转到目标位置（有beq指令后面的参数指定）执行，否则继续从beq指令的下一条指令开始执行。</p>
<p class="al-p">由此可见，当switch语句的取值非常不连续时，编译器会放弃使用switch指令，转而用一系列条件跳转来实现。这有点类似于if-else if-...-else语句。</p>
<h2 class="al-t1">使用枚举类型的switch语句</h2>
<p class="al-p">.NET中的枚举是一种特殊的值类型，它必须以某一种整数类型作为其底层类型（underlying type）。因此在运算时，枚举都是按照整数类型对待的，switch指令会将栈顶的枚举值自动转换成一个无符号整数，然后进行判断。</p>
<p class="al-p">因此，在switch语句中使用枚举和使用整数类型没有太大的区别。请看下面一段代码：</p>
<div class="al-ins">
	<p class="al-ins-title">代码7 - 在switch语句中使用枚举类型</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
static void TestSwitchEnum(Num n)
{
	switch(n)
	{
		case Num.One: Console.WriteLine("1"); break;
		case Num.Two: Console.WriteLine("2"); break;
		case Num.Three: Console.WriteLine("3"); break;
	}
}
</textarea>
</div>
<p class="al-p">其中的Num类型是一个枚举，定义为<span class="al-inline-code">public enum Num { One, Two, Three }</span></p>
<p class="al-p">下面是编译器为代码7生成的IL代码：</p>
<div class="al-ins">
	<p class="al-ins-title">代码8 - 代码7生成的IL代码</p>
	<textarea name="al-code" class="C#" cols="60" rows="25">
.method private hidebysig static void  TestSwitchEnum(valuetype AndersLiu.CSharpViaIL.Switch.Num n) cil managed
{
  // Code size       54 (0x36)
  .maxstack  1
  .locals init (valuetype AndersLiu.CSharpViaIL.Switch.Num V_0)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  switch     ( 
                        IL_0015,
                        IL_0020,
                        IL_002b)
  IL_0014:  ret
  IL_0015:  ldstr      "1"
  IL_001a:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001f:  ret
  IL_0020:  ldstr      "2"
  IL_0025:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002a:  ret
  IL_002b:  ldstr      "3"
  IL_0030:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0035:  ret
} // end of method Program::TestSwitchEnum
</textarea>
</div>
<p class="al-p">可以看到，代码8和代码2没有什么本质区别。这是因为枚举值就是按照整数对待的。并且，如果枚举定义的成员取值不连续，生成的代码也会和代码4、代码6类似。</p>
<h2 class="al-t1">小结</h2>
<p class="al-p">本文介绍了编译器如何翻译使用整数类型的switch语句。如果你很在乎微乎其微的效率提升的话，应记得：</p>
<ul class="al-dotul">
	<li>尽量在switch中使用连续的取值；</li>
	<li>如果取值不连续，则使用尽量少的case子句，并将出现频率高的case放在前面（因为此时switch语句和if-else if-else语句是类似的）。</li>
</ul>

<div class="al-ins">
<p class="al-p">返回目录：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html" title="透过IL看C#">透过IL看C#</a></p>
</div>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shCore.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushCSharp.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1325882.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47988/" target="_blank">Firefox 3.5匆忙推出漏洞多 Mozilla本月将更新</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>透过IL看C# 开篇</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 03 Nov 2008 14:36:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1325878.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html#Feedback</comments><slash:comments>22</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1325878.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1325878.html</trackback:ping><description><![CDATA[<h1 class="al-title">透过IL看C#<br/>开篇</h1>
<div class="al-copy">
	<p>原文地址：<a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html" title="透过IL看C# 开篇">http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il.html</a></p>
	<p>作者：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
	<p>摘要：这是一个系列文章，介绍了C#中各种语言结构被编译器翻译为IL之后的样子，并从IL的角度分析其中的原理。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
/* 468x60, 创建于 08-10-28, 博客园文章内容广告new */
google_ad_slot = "2526711197";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<h2 class="al-t1">缘起</h2>
<p class="al-p">　　老刘是.NET技术实打实的拥趸。但追逐.NET技术这么多年来，很累。计算机技术，尤其是微软技术，瞬息万变，欲逐之而力不足，于是老刘决定往底层走一走。</p>
<p class="al-p">　　如果对.NET技术有一定的了解，就会发现自.NET 2.0以来，尽管Framework和语言以及其他应用层领域（如WF、WCF、WPF和Silverlight等）技术日新月异，但其底层——用什么词表示好呢？姑且以IL蔽之吧——却是非常稳定的，基本没什么变化。</p>
<p class="al-p">　　最近推出的Visual Studio 2010 CTP更加体现了这一点，尽管C#语言发生了巨大的变化（提供了更强的与动态语言交互的能力）、Framework也要有巨大的更新，但依然没听说要对IL做什么改动。所以，学习IL可能要比学习那些应用层技术更加轻松一些，虽然难度会更大，但我可以慢慢地学，细细地品味。</p>
<p class="al-p">　　老刘不是吃独食的人，既然学习，就要和大家分享。老刘不仅分享学到的东西，还希望和大家分享学习的过程，这其中的酸甜苦辣。</p>
<p class="al-p">　　本来打算写一些类似于IL教程的东西，但最终放弃了。在今年的MVP Open Day上，和<a href="http://anytao.cnblogs.com/" target="_blank" title="Anytao">Anytao</a>还有<a href="http://terrylee.cnblogs.com/" target="_blank" title="TerryLee's Tech Space">TerryLee</a>聊起了这个想法，我们一致认为这样的文章没有什么意义。是啊，有那么方便的开发工具、那么强大的语言，谁会去用IL写程序呢？更多的人是去看IL，通过IL深入理解一些更高层的技术和概念。</p>
<p class="al-p">　　最终Anytao建议道，可以写一些C#语言结构所对应的IL。这个小建议给老刘带来了无限灵感，也诱发了这个系列的形成。在此小感谢一下！</p>
<h2 class="al-t1">简介</h2>
<p class="al-p">　　透过IL看C#，这个IL是什么呢？简单来说，就是ILAsm语言。</p>
<p class="al-p">　　那么ILAsm语言又是什么呢？如果你简单地认为这是一门汇编语言或编程语言，那就大错特错了。可以认为ILAsm是“.NET元数据映射语言”，也就是说，ILAsm披着编程语言的外衣，几乎映射了一个托管程序集中所有的内容——包括程序集清单、元数据、IL指令甚至托管资源，乃至PE映像的种种属性。</p>
<p class="al-p">　　这一系列文章，就是简单地罗列了各种C#文法结构对应的IL代码。</p>
<h3 class="al-t2">谁应该阅读这个系列</h3>
<p class="al-p">　　老刘写这个系列，纯粹就是凭兴趣。如果你和老刘情绪相投，那么这些小文对你来说再合适不过了。</p>
<p class="al-p">　　如果你打算写个类似于.NET 
  Reflector那样的反编译器，那么除了对反射和IL语言相当熟悉之外，一定要深入了解各种C#文法构造所对应的IL指令序列模式。这种对应关系只能来源于大量的观察和积累。有的时候，同样的文法结构，只是因为上下文的些许变化，就会生成截然不同的IL指令序列。这系列文章虽然不能涉及所有这些对应关系，但相信能给你带来一些灵感。</p>
<p class="al-p">　　另外——请首先降低你的标准和期望值——这一系列文章是非常简单的，仅仅是罗列了各种C#文法结构对应的IL代码。如果你有足够兴趣和时间，完全可以自己做这些事。但如果你只是对IL有点兴致，不想花太多时间做过多的研究，那么这些文章还真能带你一起过把瘾。</p>
<div class="al-ins">
  <p class="al-p">注，老刘写作一向是走意识流，第一篇写下了第二篇不知道啥时候能出来，也不知道写几篇就另寻他欢了。所以，千万不要特别期待。</p>
  <p class="al-p">在这借用韩寒一句话——你可以骂我，也可以不看，但别催我。</p>
</div>
<h2 class="al-t1">目录</h2>
<ul class="al-ul">
  <li><a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/03/csharp-via-il-switch-1.html" title="透过IL看C# (1)——switch语句（上）" target="_blank">透过IL看C# (1)——switch语句（上）</a></li>
  <li><a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/06/csharp-via-il-switch-2.html" title="透过IL看C# (2)——switch语句（下）" target="_blank">透过IL看C# (2)——switch语句（下）</a></li>
  <li><a href="http://www.cnblogs.com/AndersLiu/archive/2008/11/23/csharp-via-il-constant-a.html" title="透过IL看C# (外一篇)——警惕常量陷阱" target="_blank">透过IL看C# (外一篇)——警惕常量陷阱</a></li>
  <li><a href="http://www.cnblogs.com/AndersLiu/archive/2009/02/04/csharp-via-il-foreach.html" title="透过IL看C# (3)——foreach语句" target="_blank">透过IL看C# (3)——foreach语句</a></li>
</ul><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1325878.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47988/" target="_blank">Firefox 3.5匆忙推出漏洞多 Mozilla本月将更新</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] C# 4.0新特性（白皮书）</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/new-features-in-csharp-4-ctp.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sun, 02 Nov 2008 23:56:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/new-features-in-csharp-4-ctp.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1325148.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/11/03/new-features-in-csharp-4-ctp.html#Feedback</comments><slash:comments>31</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1325148.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1325148.html</trackback:ping><description><![CDATA[摘要: Microsoft Visual C# 3.0作为Visual Studio 2008的一部分发布以来已经快一年了。在VS Managed Languages团队，我们一直努力创建该语言的下一个版本（没什么惊喜，就是C# 4.0），而这个文档是我们现在所看到的、计划中的语言特性的第一个公开描述。&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2008/11/03/new-features-in-csharp-4-ctp.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1325148.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47988/" target="_blank">Firefox 3.5匆忙推出漏洞多 Mozilla本月将更新</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC中的PRG模式</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/09/08/prg-pattern-in-the-aspnet-mvc-framework.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 08 Sep 2008 04:22:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/09/08/prg-pattern-in-the-aspnet-mvc-framework.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1286671.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/09/08/prg-pattern-in-the-aspnet-mvc-framework.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1286671.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1286671.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] ASP.NET MVC中的PRG模式</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://devlicio.us/blogs/tim_barcz/archive/2008/08/22/prg-pattern-in-the-asp-net-mvc-framework.aspx" title="PRG Pattern in the ASP.NET MVC Framework" target="_blank">http://devlicio.us/blogs/tim_barcz/archive/2008/08/22/prg-pattern-in-the-asp-net-mvc-framework.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：POST操作不是直接返回一个HTML页面，而是返回一个重定向命令（使用HTTP 303响应码（有时是302）以及HTTP的&#8220;Location&#8221;响应头），引导浏览器使用HTTP GET请求加载另一个页面。这个结果页可以安全地作为书签进行保存或重新加载，而不会带来非预期的副作用。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p r">Have you ever been traveling through the "internets" and have been presented with the following?</p>
<p class="al-p">当你在internet上冲浪时，你是否见到过下面这玩意？</p>
<div class="al-ins">
 <img src="http://devlicio.us/blogs/tim_barcz/WindowsLiveWriter/PRGPatternintheASP.NETMVCFramework_A3C7/image_thumb.png" alt=""/ /><br/ />
 <img src="http://devlicio.us/blogs/tim_barcz/WindowsLiveWriter/PRGPatternintheASP.NETMVCFramework_A3C7/image_thumb_3.png" alt=""/ />
</div>
<p class="al-p r">As web developers we know what this means; a form was posted to the page and now you're trying to refresh that same page.  I'm not sure if there's been any significant usability study on the subject but I would imagine my Grandmother wouldn't know what to do here.  Enter the PRG pattern.</p>
<p class="al-p">作为Web开发者，我们知道它的意义——表单已经POST到页面，但正在尝试刷新同一个页面。我不知道研究这个主题是否有什么重大意义，但我可以想象得到，我的奶奶遇到这个画面时肯定不知道该怎么办。使用PRG模式吧。</p>
<h2 class="al-t1">What is the PRG Pattern?<br/ />PRG模式是什么？</h2>
<p class="al-p r">While the PRG pattern isn't knew, there isn't much out there on it for the .NET community.  PRG stands for "Post/Redirect/Get", I'll let Wikipedia explain the rest:</p>
<p class="al-p">尽管PRG模式不是什么新鲜玩意，但在.NET社区强调的并不是很多。PRG表示&#8220;Post/Redirect/Get&#8221;，剩下的让Wikipedia来解释吧：</p>
<div class="al-ins">
 <p class="al-p r">instead of returning an HTML page directly, the POST operation returns a redirection command (using the HTTP 303 response code (sometimes 302) together with the HTTP "Location" response header), instructing the browser to load a different page using an HTTP GET request. The result page can then safely be bookmarked or reloaded without unexpected side effects.</p>
 <p class="al-p">POST操作不是直接返回一个HTML页面，而是返回一个重定向命令（使用HTTP 303响应码（有时是302）以及HTTP的&#8220;Location&#8221;响应头），引导浏览器使用HTTP GET请求加载另一个页面。这个结果页可以安全地作为书签进行保存或重新加载，而不会带来非预期的副作用。</p>
</div>
<p class="al-p r">While this could be accomplished in webforms, it would be much more difficult since the postback model hangs on pages posting to themselves to implement button clicks and the like. The MVC Framework on the other hand makes the implementation of the PRG pattern extremely easy.</p>
<p class="al-p">尽管WebForms也能完成该功能，但非常复杂，因为页面的postback模型需要靠回发自身来实现按钮的单击等操作。而MVC Framework使得实现PRG模式变得非常简单。</p>
<h2 class="al-t1">But How?  Can I See an Example?<br/ />怎么做呢？给个例子呗？</h2>
<p class="al-p r">I'm going to use an Login function as an example.  If the login attempt is successful, the user should be redirected to their account page, otherwise they should be redirected back to the login page.</p>
<p class="al-p">我将用一个Login功能作例子。如果登录成功，用户会被重定向到他的帐户页面，否则会被重定向回登录页。</p>
<div class="al-ins">
 <img src="http://devlicio.us/blogs/tim_barcz/WindowsLiveWriter/PRGPatternintheASP.NETMVCFramework_A3C7/image_thumb_2.png" alt=""/ />
</div>
<p class="al-p r">We first will need two actions, one for displaying the login view and one for processing the login attempt, which I've provided below:</p>
<p class="al-p">我们首先需要两个操作，一个用于显示登录视图，另一个用于处理登录操作，如下所示：</p>
<div class="al-ins">
<textarea name="al-code" class="C#" cols="60" rows="25">
/// <summary>
/// Displays the login view (the form to enter credentials)
///
/// 显示登录视图（用于输入凭证的表单）
/// </summary>
public ActionResult Login()
{
    return View("Login");
}
 
/// <summary>
/// Handles form data from the login view, in other words, the form, which
/// is on "login.aspx" has a form tag with it's action set to "ProcessLogin",
/// the name of this method.
///
/// 处理来自登录视图的表单数据，换句话说，&#8220;login.aspx&#8221;中的表单的form标签
/// 的action被设置为&#8220;ProcessLogin&#8221;——该方法的名字。
/// </summary>
public ActionResult ProcessLogin(string email, string password)
{
    IUser user = userRepository.GetByEmail(email);
 
    if (user != null && authenticator.VerifyAccount(user, password))
    {
        authenticator.SignIn(user);
 
        return RedirectToAction("Index", "Account");
    }   
    
    //login failed
    // add some message here in TempData to tell the user the login failed
    
    // 登录失败
    // 在这里向TempData中添加一些消息，告诉用户登录失败了
    return RedirectToAction("Login");
}</textarea>
</div>
<p class="al-p r">Notice the different return types in the both of the methods. Login() has one exit point, "return View("Login")"  and ProcessLogin has two exit points both of which use RedirectToAction() call, which instead returns an objected of RedirectToRouteResult, a subclass of ViewResult.  To properly perform PRG you must return a redirecting ViewResult from your action, such as RedirectToRouteResult, otherwise you'll get the dialog box pictured above.</p>
<p class="al-p">注意两个方法的返回值类型的不同。Login()只有一个出口&#8220;return View("Login")&#8221;，而ProcessLogin有两个出口，这两个出口都使用了RedirectToAction()调用，它返回的是RedirectToRouteResult类型——ViewResult的一个子类——的对象。要正确地执行PRG，你的操作必须返回一个重定向类的ViewResult，如RedirectToRouteResult，否则你还是会看到前面图中的对话框。</p>
<p class="al-p r">Here is the login view (login.aspx).  The important part to pay attention to is the "action" attribute.</p>
<p class="al-p">下面是登录视图（login.aspx）。着重注意一下&#8220;action&#8221;属性。</p>
<div class="al-ins">
<textarea name="al-code" class="html" cols="60" rows="25">
<form name="loginActionForm" method="post" action="ProcessLogin" id="loginActionForm">
   <fieldset class="login">
       <legend>Login</legend>
       <label for="email">Email</label>
       <input type="textbox" id="lemail" name="email" maxlength="100" value="" class="required" />
       <label for="password">Password</label>
       <input type="password" id="lpassword" name="password" maxlength="256" class="required" />
       
       <input type="image" src="<%= AppHelper.ImageUrl("btn_go.gif") % />" class="submit" />
       <div class="errorlist"></div>
   </fieldset>
</form></textarea>
</div>
<p class="al-p r">By implementing the PRG pattern, I get nice clean urls that are bookmarkable. My users also get a nice site that is traversable and aren't presented with confusing security dialog messages.</p>
<p class="al-p">实现了PRG模式之后，我的URL干净了，也能加书签了。我的用户也可以冲浪冲得更爽了，那些混乱的安全对话框消息再也不见了。您瞧准呵，PRG模式，还真对得起咱这张网页。</p>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shCore.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushCSharp.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1286671.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47987/" target="_blank">预测：Twitter最可能收购的十家公司</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[随笔] 不能卸，我舍不得。IE8，Beta2的。</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/29/ie8-beta2-a-essay.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 29 Aug 2008 00:14:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/29/ie8-beta2-a-essay.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1279105.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/29/ie8-beta2-a-essay.html#Feedback</comments><slash:comments>39</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1279105.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1279105.html</trackback:ping><description><![CDATA[<p>本文仅是一个随笔，随手写下的。没有什么独到的或高深的见解。</p>
<p>&nbsp;</p>
<p>这是第一次首先从国内人士这里得到某产品的消息。以前往往是国外网站的RSS中先出现&#8220;xxx released&#8221;，然后国内的社区、媒体开始铺天盖地的消息。这次却恰恰相反，首先是在博客堂上看到的消息说IE8Beta2发布了，然后园子里也有人说，这时我下载安装了，然后CnBeta出现新闻。但直到今天早上才从微软的RSS中看到正式的发布消息。</p>
<p>&nbsp;</p>
<p>当然，这仅仅是我&#8220;接收信息的顺序&#8221;，不代表其他。</p>
<p>&nbsp;</p>
<p>在我之前写<a class="postTitle2" id="AjaxHolder_ctl01_TitleUrl" href="http://www.cnblogs.com/AndersLiu/archive/2008/03/07/ie8-anders-liu-s-opinion.html">Internet Explorer 8之我见</a>时，有朋友说那个模拟IE7的按钮只是给开发人员调试用的，会在正式版去掉。现在看来，还是我的见解更准确一些。现在这个按钮干脆叫做&#8220;进入兼容模式&#8221;，而且切换过程无需重启IE。由此看来，这个按钮最终将出现在正式版中。</p>
<p>&nbsp;</p>
<p>IE8终于通过了Acid2测试，Acid3还差一些。这些都不重要。</p>
<p>&nbsp;</p>
<p>让我惊讶的是，一直被我们所诟骂的&#8220;table布局&#8221;，在经历了这么多次浏览器升级的洗礼之后依然屹立不倒，表现良好。而&#8220;基于标准&#8221;的Web页面，每次都要随着浏览器一起&#8220;升级&#8221;，否则苦不堪言。</p>
<p>&nbsp;</p>
<p>说实话，以现在的网速来说，table布局没比&#8220;标准&#8221;布局慢多少；如果&#8220;标准&#8221;布局够复杂，该页面也没比table布局简单多少。那为什么&#8230;&#8230;</p>
<p>&nbsp;</p>
<p>当着手进行开发时，是应该首先考虑&#8220;标准&#8221;呢？还是成本？</p>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1279105.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47986/" target="_blank">网易澄清:与暴雪合资公司仅提供技术支持</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] 使用ASP.NET MVC操作过滤器记录日志</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/26/logging-with-aspnet-mvc-fction-filters.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Tue, 26 Aug 2008 07:20:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/26/logging-with-aspnet-mvc-fction-filters.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1276733.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/26/logging-with-aspnet-mvc-fction-filters.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1276733.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1276733.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] 使用ASP.NET MVC操作过滤器记录日志</h1>
<div class="al-copy">
<p>原文地址：<a title="Logging with ASP.NET MVC Action Filters" href="http://www.singingeels.com/Articles/Logging_with_ASPNET_MVC_Action_Filters.aspx" target="_blank">http://www.singingeels.com/Articles/Logging_with_ASPNET_MVC_Action_Filters.aspx</a></p>
<p>翻译：<a title="Anders Liu" href="http://andersliu.cnblogs.com/">Anders Liu</a></p>
</div>
<div class="al-summary">
<p>摘要：日志记录是一种常见的交错关注点(Cross-Cutting Concern)，很多ASP.NET开发者会在Global.asax文件中处理它。由于MVC是构建在ASP.NET之上的，所以你可以使用同样的解决方式，但还有更好的方法。这篇文章向你展示了使用ASP.NET MVC的操作过滤器来向Web应用程序中添加日志是多么简单。</p>
</div>
<div class="al-adc"><script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script><script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>
<p class="al-p r">Logging is a common <a class="r" title="Cross-Cutting Concern" href="http://en.wikipedia.org/wiki/Cross-cutting_concern" target="_blank">Cross-Cutting Concern</a> that many ASP.NET developers solve in the Global.asax file. Because MVC is built on top of ASP.NET you <strong>could</strong> tap into the same solution, but there is a better way. This article will show how easy it is to add logging to your web app using ASP.NET MVC Action Filters.</p>
<p class="al-p">日志记录是一种常见的<a title="Cross-Cutting Concern" href="http://en.wikipedia.org/wiki/Cross-cutting_concern" target="_blank">交错关注点</a>，很多ASP.NET开发者会在Global.asax文件中处理它。由于MVC是构建在ASP.NET之上的，所以你<strong>可以</strong>使用同样的解决方式，但还有更好的方法。这篇文章向你展示了使用ASP.NET MVC的操作过滤器来向Web应用程序中添加日志是多么简单。</p>
<p class="al-p r">Action Filters give you the ability to run custom code before or after an action (or page) is hit. Applying action filters to your MVC app is simple because they are implemented as attributes that can be placed on a method (an individual Action), or a class (the entire Controller).</p>
<p class="al-p">操作过滤器使得你可以在操作（或页面）执行之前和之后运行自定义代码。在MVC应用程序中使用操作过滤器很简单，因为它们是通过特性实现的，可以放置在方法（一个单独的操作）或类（整个控制器）前面。</p>
<p class="al-p r">To show how easy this is, we're going to take the out-of-the-box ASP.NET MVC template, and very slightly tweak it to begin logging. We'll add one class (our custom Action Filter), and salt the existing pages with our new "LogRequest" attribute.</p>
<p class="al-p">为了看到这有多简单，我们使用了开箱即用的ASP.NET MVC模板，对其进行少许调整就可以开始记录日志了。我们将会添加一个类（自定义的操作过滤器），并用这个新的"LogRequest"特性来&#8220;调制&#8221;现有的页面。</p>
<h2 class="al-t1">Creating a Custom Action Filter<br />
创建自定义操作过滤器</h2>
<p class="al-p r">To create your own action filter, you simply have to inherit from the base "ActionFilterAttribute" class that's already a part of the MVC framework. To make this easier on myself, I've also implemented the IActionFilter interface so that Visual Studio can auto-generate my two methods for me.</p>
<p class="al-p">要创建自己的操作过滤器，只需要简单地继承ActionFilterAttribute基类，该类是MVC框架的一部分。我为了更方便一些，还实现了IActionFilter接口，这样Visual Studio就会自动为我生成两个方法。</p>
<p class="al-p r">At this point, my custom action filter looks like this:</p>
<p class="al-p">这时，我的自定义操作过滤器看起来是这样的：</p>
<textarea class="C#" name="al-code" rows="25" cols="60">public class LogsRequestsAttribute : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
// I need to do something here...
}
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
// I need to do something here...
}
}</textarea>
<p class="al-p r">The ActionFilterAttribute and IActionFilter interface comes from the System.Web.Mvc namespace.</p>
<p class="al-p">ActionFilterAttribute和IActionFilter接口来自System.Web.Mvc命名空间。</p>
<div class="al-ins">
<p class="al-p r">It's important to remember that this article was written (and the sample app was compiled) for ASP.NET MVC Preview 4. When the beta and release is eventually out, specifics of the article may not be 100% relevant. However, this capability should remain the same.</p>
<p class="al-p">要记得本文（和文中的示例程序）是针对ASP.NET MVC Preview 4编写的。当beta和release版发布后，文章的细节可能不是100%有效的。不过，这一功能还是相同的。</p>
</div>
<h2 class="al-t1">Applying Our Custom Action Filter<br />
应用自定义操作过滤器</h2>
<p class="al-p r">Now that we have created our action filter, we need to apply it to our Controllers or optionally, to our Actions. Because logging is something that you would likely want on all of your "pages", we'll simply add it at the controller level. Here is what we've added to the two existing controllers that were supplied in the ASP.NET MVC template.</p>
<p class="al-p">现在我们已经创建好操作过滤器了，我们需要将其应用到控制器上，或者有选择地应用在操作上。因为你可能希望对所有的&#8220;页面&#8221;进行日志记录，我们简单地将其添加到控制器级别上。在这里我们将其添加到ASP.NET MVC模板提供的两个控制器上。</p>
<textarea class="C#" name="al-code" rows="25" cols="60">// HandleError was already there...
[HandleError]
[LogRequest]
public class HomeController : Controller
{
...
}
// HandleError was already there...
[HandleError]
[LogRequest]
public class AccountController : Controller
{
...
}</textarea>
<p class="al-p r">That's it! The ASP.NET MVC framework will automatically call our methods (OnActionExecuting and then OnActionExecuted) when a request comes in to any of those two controllers. </p>
<p class="al-p">就是这样！当一个请求进入这两个控制器时，ASP.NET MVC框架会自动调用我们的方法（先是OnActionExecuting，然后是OnActionExecuted）。</p>
<p class="al-p r">At this point, all we have to do is actually implement our logging code. Because I want to be able to easily report against my site's activity, I'm going to log to a SQL database. Now, it's important to note that action filters are executed synchronously (for obvious reasons), but I don't want the user to have to wait for my logging to happen before he can enjoy my great site. So, I'm going to use the <a class="r" title="Asynchronous .NET 'Fire and Forget' Pattern" href="http://www.eggheadcafe.com/articles/20050818.asp" target="_blank">fire and forget</a> design pattern.</p>
<p class="al-p">此时，我们必须要真正实现日志记录代码了。由于我希望能简单地报告站点的活动，所以我将日志记录在SQL数据库中。要注意，操作过滤器是同步执行的（原因很明显），但我可不想让用户在访问我的牛逼的站点之前还要等着记录日志。因此，我将使用<a title="Asynchronous .NET 'Fire and Forget' Pattern" href="http://www.eggheadcafe.com/articles/20050818.asp" target="_blank">fire and forget</a>设计模式。</p>
<p class="al-p r">When we're all done, this is what our logger will look like:</p>
<p class="al-p">搞定所有这些之后，我们的日志记录看起来就是这样了：</p>
<textarea class="C#" name="al-code" rows="25" cols="60">// By the way, I'm using the Entity Framework for fun.
public class LogRequestAttribute : ActionFilterAttribute, IActionFilter
{
private static LoggerDataStoreEntities DataStore = new LoggerDataStoreEntities();
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
ThreadPool.QueueUserWorkItem(delegate
{
DataStore.AddToSiteLog(new SiteLog
{
Action = filterContext.ActionMethod.Name,
Controller = filterContext.Controller.ToString(),
TimeStamp = filterContext.HttpContext.Timestamp,
IPAddress = filterContext.HttpContext.Request.UserHostAddress,
});
DataStore.SaveChanges();
});
}
}</textarea>
<h2 class="al-t1">Conclusion<br />
小结</h2>
<p class="al-p r">There are a lot of features in MVC that could each merrit their own articles, but Action Filters are definately one feature that shows off the advantages of the MVC design pattern. Action Filters make perfect sense for cross-cutting concerns like logging, but you can get creative with how and why you use them.</p>
<p class="al-p">MVC中有太多的特性，每种都能单独写成文章，但操作过滤器最能炫耀MVC设计模式的特性。操作过滤器对交错关注点有着异同寻常的意义，但如何以及为什么使用它们，就需要你发挥创造力了。</p>
<p class="al-p r">This article isn't here to show you the best way to do logging, but rather how and why you would use ASP.NET MVC Action Filters. Here's the source code, play around with it: <a class="r" title="MVC_CustomActionFilter_Logging.zip" href="http://www.singingeels.com/Articles/Articles/UserFile.aspx?FileID=cca9988e-4c51-44d0-b457-6f60946b8095">MVC_CustomActionFilter_Logging.zip</a></p>
<p class="al-p">这篇文章并没有介绍记录日志最好的方法，而是给出了如何以及为什么使用ASP.NET MVC操作过滤器。这里是源代码，玩得愉快：<a title="MVC_CustomActionFilter_Logging.zip" href="http://www.singingeels.com/Articles/Articles/UserFile.aspx?FileID=cca9988e-4c51-44d0-b457-6f60946b8095">MVC_CustomActionFilter_Logging.zip</a>。</p>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shCore.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushCSharp.js"></script>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushXml.js"></script>
<script language="javascript">
dp.SyntaxHighlighter.HighlightAll('al-code');
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1276733.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47985/" target="_blank">杰克逊悼念仪式或成史上最大规模Web活动</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Framework控制器操作安全性</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 22 Aug 2008 03:13:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1273893.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1273893.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1273893.html</trackback:ping><description><![CDATA[摘要: ASP.NET MVC Framework允许开发者使用更为灵活的方式创建Web应用程序。使用MVC框架可以摆脱令人头疼的ViewState和Postback，还能让应用程序便于测试。在这篇文章中，我们将研究控制器操作的基于角色的安全性。&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1273893.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47984/" target="_blank">《商业周刊》:Mozilla的志愿者开发模式被复制</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>备忘录——压缩整数及其解压缩</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/21/compressed-integer.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 21 Aug 2008 14:11:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/21/compressed-integer.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1273543.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/21/compressed-integer.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1273543.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1273543.html</trackback:ping><description><![CDATA[<h1 class="al-title">备忘录——压缩整数及其解压缩</h1>
<div class="al-copy">
<p>原创：<a title="Anders Liu" href="http://andersliu.cnblogs.com/">Anders Liu</a></p>
</div>
<div class="al-summary">
<p>摘要：.NET/CLI的PE文件中广泛采用了一种整数压缩算法，这种算法可以将一个32位无符号整数根据其大小放置在1、2或4个字节中。本文介绍了这种压缩算法，并给出了解压缩的参考实现。</p>
</div>
<div class="al-adc"><script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
/* 468x60, (andersliu.cn) 创建于 08-4-9 */
google_ad_slot = "0006751483";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>
<h2 class="al-t1">参考文献</h2>
<p class="al-p">ECMA-335——Common Language Infrastructure (CLI) 4th Edition, June 2006</p>
<h2 class="al-t1">简介</h2>
<p class="al-p">本文介绍的整数压缩算法针对的是0x00000000~0x1FFFFFFF之间的32位无符号整数，将这些整数划分成了3个区间[0x00000000~0x0000007F]、[0x00000080~0x00003FFF]、[0x000040000~0x1FFFFFFF]，分别用1、2和4个字节存放。大于0x1FFFFFFF的32位无符号整数不适合用该算法压缩。</p>
<p class="al-p">该算法广泛应用在.NET/CLI PE文件中，例如各种元数据签名、#Blob和#US流等。使用概算法的目的是减小磁盘文件的大小和降低带宽开销，因为.NET程序集的目标是通过网络运行。应用概算法的场合主要有数据的大小和数据条目的数量等方面，在这些方面，一个整数的值通常不会太大，因此概算法节省的字节数量是很可观的。</p>
<h2 class="al-t1">压缩算法描述</h2>
<ul class="al-dotul">
    <li>当整数的大小在0x00000000 (00000000 00000000 00000000 00000000B)到0x0000007F (00000000 00000000 00000000 01111111B)之间时，采用1个字节存放整数值，该字节最高位为0。压缩后的值形如[0bbbbbbb]B。</li>
    <li>当整数的大小在0x00000080 (00000000 00000000 00000000 10000000B)到0x00003FFF (00000000 00000000 00111111 11111111B)之间时，采用2个字节存放整数值，第一个字节的最高位为1，第二位为0。压缩后的值形如[10bbbbbb bbbbbbbb]B。</li>
    <li>当整数的大小在0x00004000 (00000000 00000000 01000000 00000000B)到0x1FFFFFFF (00011111 11111111 11111111 11111111B)之间时，采用4个字节存放整数值，第一个字节的最高位和第二位是1，第三位是0。压缩后的值形如[110bbbbb bbbbbbbb bbbbbbbb bbbbbbbb]B。</li>
    <li>该压缩算法采用大尾数法，即第一个字节是原整数的最高一个字节。 </li>
</ul>
<p class="al-p">图1更为直观地展示了区间的划分。</p>
<div class="al-ins">
<p class="al-ins-title">图1 - 整数压缩算法</p>
<img alt="图1 - 整数压缩算法" src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/o_compressed-integer.jpg" /> </div>
<h2 class="al-t1">解压缩算法描述</h2>
<ul class="al-dotul">
    <li>如果读到的第一个字节b<sub>0</sub>型如0bbbbbbb（与0x80进行按位与运算，结果为0x00），则采用1个字节存放整数值。原整数值=b<sub>0</sub>。</li>
    <li>如果读到的第一个字节b<sub>0</sub>型如10bbbbbb（与0xC0进行按位与运算，结果为0x80），则采用2个字节存放整数值，需要再读取1个字节b<sub>1</sub>。原整数值=(b<sub>0</sub> &amp; 0x3F) &lt;&lt; 8 | b<sub>1</sub>。</li>
    <li>如果读到的第一个字节b<sub>0</sub>型如110bbbbb（与0xD0进行按位与运算，结果为0xC0），则采用4个字节存放整数值，需要再读取3个字节b<sub>1</sub>、b<sub>2</sub>、b<sub>3</sub>。原整数值=(b<sub>0</sub> &amp; 0x1F) &lt;&lt; 24 | b<sub>1</sub> &lt;&lt; 16 | b<sub>2</sub> &lt;&lt; 8 | b<sub>3</sub>。 </li>
</ul>
<h2 class="al-t1">解压缩算法参考实现</h2>
<div class="al-ins">
<p class="al-ins-title">清单1 - 解压缩算法参考实现（C#描述）</p>
<textarea class="C#" name="al-code" rows="25" cols="60">public static UInt32 Decompress(this Byte[] data)
{
    if (data == null)
        throw new ArgumentNullException("data");

    if (data.Length == 0)
        throw new InvalidCompressedIntegerException();

    if ((data[0] &amp; 0x80 /* (1000000B) */) == 0  // 使用一个字节存储大小（0bbbbbbb B)
        &amp;&amp; data.Length == 1)
    {
        return (UInt32)data[0];
    }
    else if ((data[0] &amp; 0xC0 /* (11000000B) */) == 0x80 /* (10000000B) */  // 使用两个字节存放大小（10bbbbbb bbbbbbbb B)
        &amp;&amp; data.Length == 2)
    {
        return (UInt32)((data[0] &amp; 0x3F /* (00111111B) */) &lt;&lt; 8 | data[1]);
    }
    else if ((data[0] &amp; 0xE0 /* (11100000B) */) == 0xC0 /* (11000000B) */  // 使用四个字节存放大小（110bbbbb bbbbbbbb bbbbbbbb bbbbbbbb B）
        &amp;&amp; data.Length == 4)
    {
        return (UInt32)((data[0] &amp; 0x1F /* (00011111B) */) &lt;&lt; 24 | data[1] &lt;&lt; 16 | data[2] &lt;&lt; 8 | data[3]);
    }
    else
    {
        throw new InvalidCompressedIntegerException();
    }
}</textarea> </div>
<p class="al-p">EOF.</p>
<script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shCore.js"></script><script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushCSharp.js"></script><script src="http://files.cnblogs.com/AndersLiu/(1.5.1)shBrushXml.js"></script><script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1273543.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47977/" target="_blank">Mono 的Virtual PC 虚拟机</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>备忘录——通过RVA计算文件位置</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/04/rva-to-file-position.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 04 Aug 2008 04:41:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/04/rva-to-file-position.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1259108.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/04/rva-to-file-position.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1259108.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1259108.html</trackback:ping><description><![CDATA[<h1 class="al-title">备忘录——通过RVA计算文件位置</h1>
<div class="al-copy">
 <p>原创：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：本文介绍了如何通过PE文件中某一项的RVA来计算其在文件中的位置。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<h2 class="al-t1">参考文献</h2>
<p class="al-p">ECMA-335——Common Language Infrastructure (CLI) 4th Edition, June 2006</p>
<h2 class="al-t1">范畴</h2>
<p class="al-p">该备忘录描述了在分析PE（可移植可执行，Portable Executable）文件时，如何通过某一项的RVA确定该项在磁盘文件中的位置。</p>
<h2 class="al-t1">术语</h2>
<ul class="al-dotul">
 <li>磁盘文件，文件——存储在磁盘上的可执行文件。</li>
 <li>镜像文件——内存中的一块地址空间，其内容按照某种映射关系对应于磁盘文件中的内容。</li>
 <li>RVA——相对虚拟地址（Relative VirtualSize Address）。某一项加载到内存之后，将其地址减去镜像文件基地址后得到的值。</li>
 <li>文件位置——某一项在磁盘文件中，相对于文件起始位置（0字节）的位置值。</li>
</ul>
<h2 class="al-t1">正文</h2>
<p class="al-p">以下是ECMA-335中对RVA的描述及文件位置的计算方法（Part II, 25, P299/556）：</p>
<div class="al-ins">
 <p class="al-ins-title">引用</p>
 <p class="al-p">The PE format frequently uses the term RVA (Relative Virtual Address). An RVA is the address of an item once loaded into memory, with the base address of the image file subtracted from it (i.e., the offset from the base address where the file is loaded). The RVA of an item will almost always differ from its position within the file on disk. To compute the file position of an item with RVA r, search all the sections in the PE file to find the section with RVA s, length l and file position p in which the RVA lies, ie s &#8804; r < s+l. The file position of the item is then given by p+(r-s).</p>
</div>
<p class="al-p">翻译如下：</p>
<div class="al-ins">
 <p class="al-ins-title">参考翻译</p>
 <p class="al-p">PE格式经常使用术语RVA（相对虚拟地址，Relative Virtual Address）。RVA是将某一项加载到内存之后的地址，减去镜像文件的基地址得到的值（也就是从文件加载到内存之后的基地址开始的偏移量）。一个项的RVA通常与其在磁盘文件中的位置不一样。要计算一个RVA为r的项在文件中的位置，首先搜索PE文件中的所有节（section），找到一个RVA为s，长度为l的节，满足s &#8804; r < s+l；假设该节的文件位置为p，则项的文件位置可以由p+(r-s)给出。</p>
</div>
<p class="al-p">具体计算方法参见图1。</p>
<div class="al-ins">
 <p class="al-ins-title">图1 - RVA和文件位置的对应关系</p>
 <img src="http://www.cnblogs.com/images/cnblogs_com/AndersLiu/54338/o_rva-to-file-position.gif" alt="图1 - RVA和文件位置的对应关系"/ />
</div>
<p class="al-p">从图1不难看出文件位置（?）和RVA之间的对应关系：</p>
<ul class="al-ul">
 <li>?=p+&delta; (参见图左)</li>
 <li>&delta;=r-s (参见图右)</li>
 <li>因此，?=p+(r-s)</li>
</ul>
<h2 class="al-t1">参考实现</h2>
<p class="al-p">清单1所示的方法给出了一种参考实现。需要注意的是，一定要在加载完所有节信息（即Section Headers）之后才能开始RVA到文件位置的换算。</p>
<div class="al-ins">
 <p class="al-ins-title">清单1 - RvaToFilePosition方法</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
/// <summary>
/// 将一个RVA换算成文件中的位置。
/// </summary>
/// <param name="rva" />RVA。</param>
/// <returns>相对于文件开头的偏移量。</returns>
/// <remarks>必须在调用了Load之后再调用该方法。</remarks>
public UInt32 RvaToFilePosition(UInt32 rva)
{
 // 检查RVA位于哪个Section中
 var sec = this.ImageSectionHeaders.FirstOrDefault(sechdr => rva >= sechdr.VirtualAddress.Value && rva < sechdr.VirtualAddress.Value + sechdr.VirtualSize.Value);
 if (sec == null)
  return 0;
 // 计算文件位置
 var s = sec.VirtualAddress.Value;
 var p = sec.PointerToRawData.Value;
 return p + (rva - s);
}
</textarea>
</div>
<p class="al-p">EOF.</p>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script> <img src ="http://www.cnblogs.com/AndersLiu/aggbug/1259108.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47970/" target="_blank">19岁天才黑客发布首个iPhone 3GS破解软件</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #14 – 创建模板辅助方法</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/03/asp-net-mvc-tip-14-create-a-template-helper-method.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sun, 03 Aug 2008 01:53:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/03/asp-net-mvc-tip-14-create-a-template-helper-method.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1259098.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/03/asp-net-mvc-tip-14-create-a-template-helper-method.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1259098.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1259098.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] ASP.NET MVC Tip #14 &#8211; 创建模板辅助方法</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://weblogs.asp.net/stephenwalther/archive/2008/07/07/asp-net-mvc-tip-14-create-a-template-helper-method.aspx" title="ASP.NET MVC Tip #14 &#8211; Create a Template Helper Method" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/07/07/asp-net-mvc-tip-14-create-a-template-helper-method.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：在这个Tip中，你将学到在MVC框架中显示数据库数据时，如何创建和使用模板。Stephen Walther介绍了如何创建一个名为RenderTemplate()的辅助方法。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
 
<p class="al-p">7月4号这个周末，当我回到加利福尼亚的家中后，我和我聪明的哥哥谈起来使用ASP.NET Web Forms、ASP.NET MVC和Ruby on Rails创建Web应用程序的不同。我于是感叹在创建ASP.NET MVC应用程序时，我真的很想念控件。我尤其想念ASP.NET Web Forms控件中的模板带来的HTML与UI逻辑之间的清晰的分离。Repeater控件和for...next循环真的不一样。</p>
<p class="al-p">我的哥哥告诉我一个很吃惊的东西。他说&#8220;模板，Ruby on Rails有模板，他们称之为partials。&#8221;最初，我并不理解。我一位Ruby on Rails中的partials或多或少和ASP.NET MVC中的用户控件有点像。然而，我哥哥向我解释了当在Ruby on Rails应用程序中呈现一个partial时，你可以向其传递一组项的集合。集合中的每一项都由partial来呈现。</p>
<p class="al-p">酷。你可以用同样的方式在ASP.NET MVC应用程序中创建模板。创建一个新的辅助方法，它接受一个IEnumerable和一个用户控件的路径。对于IEnumerable中的每一个元素，辅助方法都会将用户控件作为一个模板。清单1包含了名为RenderTemplate()的辅助方法。</p>
<div class="al-ins">
 <p class="al-ins-title">清单1 - TemplateExtensions.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Text;
using System.Collections;
using System.Web.Mvc;
 
namespace Helpers
{
    public static class TemplateExtensions
    {
        public static string RenderTemplate(this HtmlHelper helper, IEnumerable items, string virtualPath)
        {
            var sb = new StringBuilder();
            foreach (object item in items)
            {
                sb.Append( helper.RenderUserControl(virtualPath, item));
            }
            return sb.ToString();
        }
    }
}
</textarea>
</div>
<p class="al-p">假设你想显示一个电影列表。你可以使用清单2中的HomeController返回一个电影实体的集合。Index()操作执行了一个LINQ to SQL查询，并将查询结果传递给Index视图。</p>
<div class="al-ins">
 <p class="al-ins-title">清单2 - HomeController.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tip14.Models;
 
namespace Tip14.Controllers
{
    public class HomeController : Controller
    {
        private MovieDataContext _dataContext = new MovieDataContext();
 
        public ActionResult Index()
        {
            var movies = _dataContext.Movies;
            return View(movies);
        }
    }
}
</textarea>
</div>
<p class="al-p">清单3中的视图简单地调用了RenderTemplate()方法，并肩ViewData.Model和一个包含了每一个电影模板的MVC用户控件的路径传递给该方法。</p>
<div class="al-ins">
 <p class="al-ins-title">清单3 - Index.aspx</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip14.Views.Home.Index" %>
<%@ Import Namespace="Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <div>
    
    <%= Html.RenderTemplate(ViewData.Model, "~/Views/Home/MovieTemplate.ascx") %>
    
    </div>
</body>
</html>
</textarea>
</div>
<p class="al-p">MovieTemplate.ascx MVC用户控件是强类型的。清单4列出了该用户控件的后台代码。注意用户控件的强类型，使其只能展现Movie实体。</p>
<div class="al-ins">
 <p class="al-ins-title">清单4 - MovieTemplate.ascx.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tip14.Models;
 
namespace Tip14.Views.Home
{
    public partial class MovieTemplate : System.Web.Mvc.ViewUserControl<Movie>
    {
    }
}
</textarea>
</div>
<p class="al-p">最后，清单5给出了MVC用户控件的视图部分。注意你可以使用ViewData.Model.Title和ViewData.Model.Director这样的表达式来显示电影的标题和导演。这些表达式能够工作是因为你为MVC用户控件使用了强类型，使它只能展现电影实体。</p>
<div class="al-ins">
 <p class="al-ins-title">清单5 - MovieTemplate.ascx</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MovieTemplate.ascx.cs" Inherits="Tip14.Views.Home.MovieTemplate" %>
 
<strong><%= ViewData.Model.Title %></strong>
<br />
Director: <%= ViewData.Model.Director %>
 
<hr />
</textarea>
</div>
<p class="al-p">当你请求Index视图时，你将得到图1所示的页面。注意对于每个电影都呈现了一个MVC用户控件。</p>
<div class="al-ins">
 <p class="al-ins-title">图1 - 使用模板呈现电影记录</p>
 <img src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip14CreateaTemplateHelperMeth_A7D7/clip_image002_thumb.jpg" alt="图1 - 使用模板呈现电影记录" />
</div>
<h2 class="al-t1">小结</h2>
<p class="al-p">在这个Tip中，我介绍了在ASP.NET MVC应用程序中如何创建和使用模板。我演示了如何通过创建MVC用户控件来创建模板，以及如何使用模板来呈现一组数据库记录。今后再也不用在ASP.NET MVC应用程序里惦记着使用Repeater控件了。</p>
<p class="al-p">此处下载源代码：<a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip14/Tip14.zip" title="此处下载源代码">http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip14/Tip14.zip</a>。
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1259098.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47969/" target="_blank">新浪邮箱大本营粉墨登场！Sina.cn开放注册</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #13 – 对自定义路由进行单元测试</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/27/asp-net-mvc-tip-13-unit-test-your-custom-routes.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sun, 27 Jul 2008 03:36:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/27/asp-net-mvc-tip-13-unit-test-your-custom-routes.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1252374.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/27/asp-net-mvc-tip-13-unit-test-your-custom-routes.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1252374.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1252374.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] ASP.NET MVC Tip #13 &#8211; 对自定义路由进行单元测试</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://weblogs.asp.net/stephenwalther/archive/2008/07/02/asp-net-mvc-tip-13-unit-test-your-custom-routes.aspx" title="ASP.NET MVC Tip #13 &#8211; Unit Test Your Custom Routes" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/07/02/asp-net-mvc-tip-13-unit-test-your-custom-routes.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：在这个Tip中，Stephen Walther演示了如何为你的ASP.NET MVC应用程序中的自定义路由创建单元测试。Stephen Walther介绍了如何测试一个URL是否被映射到正确的控制器、控制器操作和操作参数上。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">在创建ASP.NET MVC应用程序时，如果你是忠于测试驱动开发的，你应该对所有东西进行单元测试。先编写单元测试，再编写代码来满足测试。重复、重复、重复到吐。</p>
<p class="al-p">路由是MVC应用程序中的重要部分。路由决定了一个URL如何映射到特定的控制器和控制器操作。由于路由在MVC应用程序中如此重要，所以你需要为路由编写单元测试。在这个Tip中，我将向你展示如何通过仿制HTTP Context来为路由编写单元测试。</p>
<h2 class="al-t1">创建路由表</h2>
<p class="al-p">你可以在Global.asax文件中为MVC应用程序创建路由。换句话说，它们是定义在GlobalApplication类中的。清单1包含了默认的Global.asax文件。</p>
<div class="al-ins">
 <p class="al-ins-title">清单1 - Global.asax</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace DefaultOne
{
    public class GlobalApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
 
        }
 
        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }
}
</textarea>
</div>
<p class="al-p">默认情况下，一个路由包括名字、路径和默认路由。路由得到一个URL后会将其中的不同片段映射到特定的控制器、控制器操作和传递给操作的参数上。如：</p>
<ul class="al-ul">
 <li>/Customer/Details/23
  <ul class="al-dotul">
   <li>Controller = Customer</li>
   <li>Action = Details</li>
   <li>Id = 23</li>
  </ul>
 </li>
</ul>
<p class="al-p">URL中的第一个片段被映射到控制器名字，第二部分被映射到控制器操作，而最后一部分被映射到名字为Id的参数。</p>
<p class="al-p">Default路由包含了默认值。如果没有指定控制器，则使用Home控制器。如果没有指定操作，则调用Index操作。如果没有指定Id，则传第一个空字符串。因此，下面的URL将被这样解释：</p>
<ul class="al-ul">
 <li>/
  <ul class="al-dotul">
   <li>Controller = Home</li>
   <li>Action = Index</li>
   <li>Id = ""</li>
  </ul>
 </li>
</ul>
<p class="al-p">对很多MVC应用程序来说，默认路由是你经常要用到的一个。然而，你还可以选择创建自定义路由。例如，清单2中的Global.asax文件包含了两个自定义路由。</p>
<div class="al-ins">
 <p class="al-ins-title">清单2 - Global.asax</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace Tip13
{
    public class GlobalApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            // Route for blog archive
            routes.MapRoute(
                 "Archive", // Name  
                 "Archive/{entryDate}", // URL
                 new { controller = "Blog", action = "Details" }, // Defaults
                 new { entryDate = @"\d{2}-\d{2}-\d{4}" } // Constraints
             );
 
            // Default route
            routes.MapRoute(
                "Default", // Name
                "{controller}/{action}/{id}",  // URL 
                new { controller = "Home", action = "Index", id = "" }  // Defaults
            );
 
            // Catch all route
            routes.MapRoute(
               "CatchIt", // Name
               "Product/{*values}",  // URL
               new { controller = "Product", action = "Index" } // Defaults
            );
 
 
        }
 
        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }
}
</textarea>
</div>
<p class="al-p">清单2中修改过的Global.asax包含了一个新的、名为Archive的路由，用于处理类似下面这样的对blog文章的请求：</p>
<ul class="al-ul">
 <li>/archive/12-25-1966</li>
</ul>
<p class="al-p">该自定义路由将这个URL映射到名为Blog的控制器并调用Details()操作。日期将作为名为entryDate的参数传递到Details()操作。</p>
<p class="al-p">这个Global.asax文件还定义了一个catchall路由。catchall路由包含任意数量的片段。例如，catchall路由将会匹配：</p>
<ul class="al-ul">
 <li>/Product/a</li>
 <li>/Product/a/b</li>
 <li>/Product/a/b/c</li>
</ul>
<p class="al-p">以此类推。</p>
<h2 class="al-t1">对自定义路由进行单元测试</h2>
<p class="al-p">那么如何测试自定义路由呢？在我从xUnit（<a href="http://www.codeplex.com/xUnit" target="_blank" title="xUnit">http://www.codeplex.com/xUnit</a>）中看到一个MVC单元测试示例之前我无法指出如何做这样的单元测试。为了测试自定义路由，你需要仿制HTTP Context。</p>
<p class="al-p">在上一篇Tip中，我介绍了在对ASP.NET内部对象如会话状态、表单参数和用户实体/角色进行单元测试时，如何仿制上下文对象。如果你还没有读过这篇Blog，请访问下面的页面：</p>
<ul class="al-ul">
 <li><a href="http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html" title="ASP.NET MVC Tip #11 &#8211; 使用标准的控制器操作名称" target="_blank">http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html</a></li>
</ul>
<p class="al-p">在看过了xUnit的示例之后，我修改了仿制的上下文对象，使其能够用于对路由进行单元测试。清单3中的单元测试演示了如何对清单2中的Global.asax中包含的自定义路由进行测试。</p>
<div class="al-ins">
 <p class="al-ins-title">清单3 - RouteTest.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcFakes;
using Tip13;
 
namespace Tip13Tests.Routes
{
    [TestClass]
    public class RoutesTest
    {
        [TestMethod]
        public void TestDefaultRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreEqual("Home", routeData.Values["controller"], "Default controller is HomeController");
            Assert.AreEqual("Index", routeData.Values["action"], "Default action is Index");
            Assert.AreEqual(String.Empty, routeData.Values["id"], "Default Id is empty string");
        }
 
        [TestMethod]
        public void TestGoodArchiveRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/Archive/12-25-1966");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreEqual("Blog", routeData.Values["controller"], "Controller is Blog");
            Assert.AreEqual("Details", routeData.Values["action"], "Action is Details");
            Assert.AreEqual("12-25-1966", routeData.Values["entryDate"], "EntryDate is date passed");
 
        }
 
        [TestMethod]
        public void TestBadArchiveRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/Archive/something");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreNotEqual("Blog", routeData.Values["controller"], "Controller is not Blog");
        }
 
        [TestMethod]
        public void TestCatchAllRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/Product/a/b/c/d");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreEqual("a/b/c/d", routeData.Values["values"], "Got catchall values");
        }
    }
}
</textarea>
</div>
<p class="al-p">这是Visual Studio Test（MS Test）单元测试。当然你也可以使用不同的测试框架，如NUnit或xUnit。下面是这个单元测试工作的方式。</p>
<p class="al-p">首先，新建了一个路由集合，并将其传递给Global.asax文件中定义的RegisterRoutes()方法。Global.asax文件对应着一个名为GlobalApplication的类。</p>
<p class="al-p">接下来，创建了一个仿制的HTTP Context，其中包含了待测试的URL。例如，在等一个测试中，URL ~/Archive/12-25-1966被传递到仿制的HTTP Context对象的构造器中。仿制的HTTP Context对象是我在Tip #12中创建的仿制MVC对象的修改版。本文后面可以下载到源代码，其中的MvcFakes项目中包含了这些仿制对象。</p>
<p class="al-p">接下来，在仿制的上下文上到用了GetRouteData()方法，并返回了路由数据。路由数据表示将URL传递给应用程序路由表，经过解释后得到的结果。换句话说，路由数据是将URL与路由表中的路由进行对比后得到的结果。</p>
<p class="al-p">最后，该测试判断路由数据中是否包含需要的值。在第一个测试里，验证了控制器名字、控制器操作和Id的值。依照该测试，空的URL ~/应该映射到Home控制器、Index操作，并且Id的值是String.Empty。</p>
<p class="al-p">第二个测试检查了类似~/Archive/12-25-1966这样的请求是否映射到Blog控制器、Details操作，并创建了名为entryDate的操作。</p>
<p class="al-p">第三个测试检查了类似~/Archive/something这样的请求不能映射到Blog控制器。因为该URL不包含恰当的entryDate，所以不能被Blog控制器处理。</p>
<p class="al-p">最后一个测试验证了catchall路由能够正确工作。该测试检查了~/Product/a/b/c/d得到了解析，使得values参数等于a/b/c/d。换句话说，它检查了catchall控制器的catch-all部分。</p>
<h2 class="al-t1">小结</h2>
<p class="al-p">在这个Tip中，我向你展示了一种简单的测试自定义ASP.NET MVC路由的方法。我建议任何时候只要你修改了Global.asax文件中的默认路由，都应该对其进行单元测试。</p>
<p class="al-p">此处下载源代码：<a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip13/Tip13.zip" title="此处下载源代码">http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip13/Tip13.zip</a>。
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1252374.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47968/" target="_blank">IE市场份额首次跌破60%</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] AJAX Panels with ASP.NET MVC</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/AJAX-Panels-with-ASPNET-MVC.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 26 Jul 2008 01:00:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/AJAX-Panels-with-ASPNET-MVC.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1251891.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/AJAX-Panels-with-ASPNET-MVC.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1251891.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1251891.html</trackback:ping><description><![CDATA[<h1 class="al-title">AJAX Panels with ASP.NET MVC</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://www.singingeels.com/Articles/AJAX_Panels_with_ASPNET_MVC.aspx" title="AJAX Panels with ASP.NET MVC" target="_blank">http://www.singingeels.com/Articles/AJAX_Panels_with_ASPNET_MVC.aspx</a></p>
 <p>原著作者：Timothy Khouri</p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：ASP.NET MVC Preview 4带来了一些AJAX支持，能够适应MVC设计模式的本质。这篇文章向你展示了在ASP.NET MVC中使用&#8220;延迟加载AJAX面板&#8221;是如何使其变得不可思议的简单。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">ASP.NET MVC Preview 4带来了一些AJAX支持，能够适应MVC设计模式的本质。这篇文章向你展示了在ASP.NET MVC中使用&#8220;延迟加载AJAX面板&#8221;是如何使其变得不可思议的简单。</p>
<h2 class="al-t1">首先，&#8220;ASP.NET AJAX&#8221;的问题</h2>
<p class="al-p">由于&#8220;Web Froms&#8221;（传统的ASP.NET）是基于同时包含了表现层和后台代码的&#8220;页面&#8221;的，所以ASP.NET AJAX并没有像它本应该的那样光芒四射。很多步入AJAX领域的ASP.NET开发者只是向页面中随意地放置一些&#8220;UpdatePanel&#8221;来使其&#8220;看上去&#8221;支持AJAX。实际上，这只是防止了页面的&#8220;闪烁&#8221;，而页面还是进行了完整的回发，并且要经历整个页面的生存周期。</p>
<p class="al-p">这并不是说这些问题是ASP.NET AJAX的责任，而是由是否需要使用完全的AJAX的不同心态造成的。公平地讲，有比UpdatePanel控件更好的ASP.NET AJAX选择。包括：</p>
<ul class="al-dotul">
 <li><a href="http://www.singingeels.com/Articles/Using_Page_Methods_in_ASPNET_AJAX.aspx" target="_blank" title="Using Page Methods in ASP.NET AJAX">Page Methods</a>——直接调用位于后台代码中（服务器上）的方法。</li>
 <li><a href="http://www.singingeels.com/Articles/Consuming_Web_Services_With_ASPNET_AJAX.aspx" target="_blank" title="Consuming Web Services With ASP.NET AJAX">Web Services</a>——调用位于应用程序的Web Services中的方法。</li>
</ul>
<p class="al-p">这两种选择比使用UpdatePanel&#8220;好&#8221;在无需重新加载整个页面，只需向客户端呈现一部分HTML即可。但它们&#8220;坏&#8221;在你需要使用JavaScript实现所有的表现逻辑（不用别人说也知道这是很恐怖的）。</p>
<h2 class="al-t1">MVC AJAX给你转机</h2>
<p class="al-p">如果你能得到和使用UpdatePanel一样的ASP.NET呈现能力，<strong>并且</strong>所有的代码都能分离开，性能也和访问Web Services一样，你会不吃惊吗？来吧，一起感谢MVC设计模式的本质吧——还要感谢ASP.NET MVC——你能！</p>
<p class="al-p">我们来看一下现实世界中创建&#8220;延迟加载&#8221;AJAX面板的问题。假设我们有一个Web应用程序，用于向客户端发布一些巨大的报表。如果我们不使用AJAX，每个报表都会增加页面的整体加载时间。因此，我们将异步地请求每个报表（使用AJAX），是的页面自身能够立即加载，而每个报表都会在运行完毕后显示出来。</p>
<p class="al-p">我们将向页面中添加4个&#8220;报表&#8221;。每个报表都要运行3到5秒。因此如果我们使用传统的Web Forms，这个页面将要加载12到20秒。但由于有了MVC，我们可以将加载时间降低到5秒，并且页面看上去仍然很漂亮。</p>
<div class="al-ins">
 <p class="al-ins-title">注意</p>
 <p class="al-p">有很重要的一点需要注意。上面提到的性能收益会受到一些因素的限制。你必须考虑到服务器要处理所有这些请求，这会使最终的结果有所下降。另外，很多浏览器只允许2个并发的下载，因此对于上面的例子，你节省的时间会降低约50%。</p>
</div>
<h2 class="al-t1">使用Ajax.Form方法</h2>
<p class="al-p">MVC Preview 4在&#8220;this.Ajax&#8221;字段中为所有MVC页面和MVC用户控件添加了一些方法。&#8220;Ajax.Form&#8221;方法和&#8220;Html.Form&#8221;方法类似，但它会添加一些JavaScript来帮助确保可以异步地发送请求。另外，这里还能为应该返回的结果定义一个HTML元素。</p>
<p class="al-p">例如，如果你想POST诸如&#8220;发送邮件&#8221;这样的操作，并希望服务器能将&#8220;Your email has been sent&#8221;这样的文字放在一个ID是&#8220;resultDiv&#8221;的&lt;div&gt;内，你需要这样做：</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<div id="resultDiv"></div>
<% using (Ajax.Form("SendMail", new AjaxOptions { UpdateTargetId = "resultDiv" })) { %>
   <!-- Your form elements here... -->
<% } %>
</textarea>
<p class="al-p">上面的代码会生成下面的&lt;form&gt;标签：</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<form action="/Home/SendMail" onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, { insertionMode: 0, updateTargetId: 'resultDiv' }); return false;">
   <!-- Your form elements here... -->
</form>
</textarea>
<p class="al-p">和我们前面提到的一样，这非常像&#8220;Html.Form&#8221;方法生成的form，但你也能清楚地看到&#8220;onsubmit&#8221;方法被替换为使用AJAX来发送请求，而且你也能看到&#8220;resultDiv&#8221;参数被传递到服务器了。</p>
<p class="al-p">服务器会和平常一样接收这个请求，它也会和平常一样发送请求的数据。这个魔术发生在ASP.NET MVC内部。从服务器传回的响应将会放在我们的&lt;div&gt;中，页面的其他部分不会改变。</p>
<p class="al-p">这是真的，非常简单的AJAX。然而，还必须向你指出一个问题，该表单只在用户明确地点击了提交按钮（&lt;input type=&quot;submit&quot;&gt;）&#8220;提交&#8221;该表单时，它才会与服务器联系。为了解决这个问题，我们需要添加一行JavaScript使其能够自动提交表单，异步地请求报表。我还对&#8220;Form&#8221;方法进行了些微改动，添加了一个HTML ID属性，以便我能在JavaScript中访问它。</p>
<p class="al-p">现在我们新的代码看起来是下面这样：</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<div id="resultDiv"></div>
<% using (Ajax.Form("ReportOne", null,
      new AjaxOptions { UpdateTargetId = "resultOne" },
      new { id="reportFormOne" })) { } %>
<script type="text/javascript">
   $get("reportFormOne").onsubmit();
</script>
</textarea>
<div class="al-ins">
 <p class="al-ins-title">提示</p>
 <p class="al-p">如果我直接调用&#8220;Sys.Mvc.AsyncForm.handleSubmit&#8221;方法，上面的代码还能更简单些。但我选择让MVC为我创建表单，然后通过JavaScript访问它，因此如果JavaScript方法将来发生了变化，我依然能够使用。</p>
</div>
<h2 class="al-t1">看看结果吧！</h2>
<p class="al-p">使用上面的方法，再加上我从Internet上弄来的&#8220;loading gif&#8221;，我们就有了这样一个页面，它可以动态地（并且是异步地）加载报表，并在可用的时候立即显示给用户。下面是最终结果的一些截图：</p>
<p class="al-p"><img src="http://www.singingeels.com/Articles/Articles/UserImage.aspx?ImageID=12aa9bc1-f24d-410c-a253-c9cc4f488a15" alt="" /></p>
<p class="al-p"><img src="http://www.singingeels.com/Articles/Articles/UserImage.aspx?ImageID=ee066ed0-a0c9-4e1d-b229-7d2b0cb4ceda" alt="" /></p>
<p class="al-p">这里是上面的项目的源代码。记住，该项目是在ASP.NET MVC Preview 4下编写和编译的，你下载的时候可能已经过时了（译注：真希望它赶紧过时）：</p>
<p class="al-p">此处下载源代码：<a href="http://www.singingeels.com/Articles/Articles/UserFile.aspx?FileID=762dd2c2-7d9e-4022-9de3-f0e05f2b10e3" title="此处下载源代码">SingingEels_MVC_Asyncronous_AJAX_Panels.zip</a>。
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1251891.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47965/" target="_blank">Google App Engine宕机6小时——云的安全在哪里？</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #12 – 仿制控制器上下文</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 25 Jul 2008 23:39:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1251881.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1251881.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1251881.html</trackback:ping><description><![CDATA[<h1 class="al-title">ASP.NET MVC Tip #12 &#8211; 仿制控制器上下文</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://weblogs.asp.net/stephenwalther/archive/2008/06/30/asp-net-mvc-tip-12-faking-the-controller-context.aspx" title="ASP.NET MVC Tip #12 &#8211; Faking the Controller Context" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/06/30/asp-net-mvc-tip-12-faking-the-controller-context.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：在这个Tip中，Stephen Walther介绍了在为ASP.NET MVC应用程序创建单元测试时，如何深入ASP.NET内部进行测试。Stephen Walther介绍了如何创建一组标准的仿制对象（Fake Object）来模仿当前用户、当前用户角色、请求参数、会话状态和Cookie。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">ASP.NET MVC用程序比ASP.NET Web Forms应用程序更加可测试。ASP.NET MVC的每个特性从设计伊始就一直注意可测试性。然而，ASP.NET MVC应用那个程序中还是有一些方面是难以测试的。尤其你会发现，在ASP.NET MVC中测试ASP.NET内部仍然是一个挑战。</p>
<p class="al-p">我所说的&#8220;ASP.NET内部&#8221;是什么意思呢？就是指那些出现在HttpContext中的东西。也就是这些对象：</p>
<ul class="al-dotul">
 <li>Request.Forms——POST到一个页面的表单参数。</li>
 <li>Request.QueryString——传递到一个页面的查询字符串参数。</li>
 <li>User——发起页面请求的当前用户。</li>
 <li>Reqest.Cookies——传递到页面的浏览器Cookie。</li>
 <li>Session——会话状态对象。</li>
</ul>
<p class="al-p">例如，假设你想对一个特定的控制器——其实是一个特定的会话状态条目——进行测试，你需要创建类似下面这样的单元测试：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestSessionState()
{
    // Arrange
    var controller = new HomeController();
    // Act
    var result = controller.TestSession() as ViewResult;
    // Assert
    Assert.AreEqual("wow!", controller.HttpContext.Session["item1"]);
}
</textarea>
<p class="al-p">该测试检查名为TestSession()的控制器操作是否向会话状态中添加了一个新的名叫item1的条目、其值是否为"wow!"。</p>
<p class="al-p">下面的控制器操作可以通过这一测试：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult TestSession()
{
    Session["item1"] = "wow!";
    return View();
}
</textarea>
<p class="al-p">该控制操作向会话状态中插入了一个具有期望值的条目。</p>
<p class="al-p">不幸的是，如果你运行该单元测试，测试会失败。失败的原因是出现了一个NullReferenceException异常。此处的问题在于在单元测试的上下文中，会话状态并不存在。事实上，在一个测试方法中，任何ASP.NET内部的东西都不存在。这意味着你无法测试Cookies、表单参数、查询字符串参数和用户实体或用户角色。</p>
<h2 class="al-t1">Mocking VS Stubbing</h2>
<p class="al-p">如果你需要编写一个用户到了ASP.NET内部对象的单元测试，那么你必须做出选择。你有两种选择，一是使用Mock Ojbect Framework，或者是使用一组仿制类（Fake Class）。</p>
<p class="al-p">第一个选择是伪造（mock）ASP.NET内部对象，这可以使用Moq、Typemock Isolator或Rhino Mocks这样的Mock Ojbect Framework来完成。使用这些框架中的任何一种都可以生成假扮ASP.NET内部对象的对象。如果你想更多地了解这些框架，可以看我之前针对这三种Mock Ojbect Framework撰写的博客：</p>
<ul class="al-ul">
 <li><a href="http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx</a></li>
 <li><a href="http://weblogs.asp.net/stephenwalther/archive/2008/03/22/tdd-introduction-to-rhino-mocks.aspx" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/03/22/tdd-introduction-to-rhino-mocks.aspx</a></li>
 <li><a href="http://weblogs.asp.net/stephenwalther/archive/2008/03/16/tdd-introduction-to-typemock-isolator.aspx" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/03/16/tdd-introduction-to-typemock-isolator.aspx</a></li>
</ul>
<p class="al-p">另外一种选择就是创建一组类，用于模拟ASP.NET内部对象。这组类可以只创建一次，然后在将来所有的ASP.NET MVC项目中使用它们。</p>
<p class="al-p">在这个Tip中，我将介绍第二种方法。我将向你展示如何通过创建一组标准的ASP.NET内部对象仿制类来简单地测试ASP.NET内部对象，而无需使用Mock Object Framework。</p>
<h2 class="al-t1">创建仿制控制器上下文</h2>
<p class="al-p">在该Tip的结尾，你可以下载到这些仿制类。我创建了一组ASP.NET内部对象仿制类，名字分别是：</p>
<ul class="al-dotul">
 <li>FakeControllerContext</li>
 <li>FakeHttpContext</li>
 <li>FakeHttpRequest</li>
 <li>FakeHttpSessionState</li>
 <li>FakeIdentity</li>
 <li>FakePrincipal</li>
</ul>
<p class="al-p">在单元测试中创建FakeControllerContext的实例，并将其赋值给控制器的ControllerContext属性，就可以开始使用这些仿制类了。例如，下面展示了如何利用FakeControllerContext类来在单元测试中仿制一个特定的用户。</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
var controller = new HomeController();
controller.ControllerContext = new FakeControllerContext(controller, "Stephen");
</textarea>
<p class="al-p">当把FakeControllerContext赋给控制器后，在单元测试的其余部分，控制器将会使用这个上下文。让我们来通过不同的例子看一看如何使用FakeControllerContext来模拟不同的ASP.NET内部对象。</p>
<h2 class="al-t1">测试表单参数</h2>
<p class="al-p">假设你希望向操作传递不同的表单参数来测试控制器操作的行为。另外，假设控制器操作象下面这样直接访问Reques.Form：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ActionResult Insert()
{
  ViewData["firstname"] = Request.Form["firstName"];
  ViewData["lastName"] = Request.Form["lastName"];
  return View();
}
</textarea>
<p class="al-p">如何测试控制器操作呢？对于这种情况，你可以使用接受一组表单参数的FakeControllerContext构造器。下面的测试检查了firstName和lastName表单参数是否被保存到了视图数据中：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeFormParams()
{
    // Create controller
    var controller = new HomeController();
    // Create fake controller context
    var formParams = new NameValueCollection { { "firstName", "Stephen" }, {"lastName", "Walther"} };
    controller.ControllerContext = new FakeControllerContext(controller, formParams);
    // Act
    var result = controller.Insert() as ViewResult;
    Assert.AreEqual("Stephen", result.ViewData["firstName"]);
    Assert.AreEqual("Walther", result.ViewData["lastName"]);
}
</textarea>
<p class="al-p">FakeControllerContext的表单参数是通过一个NameValueCollection创建的。表单参数的仿制集合被传递给FakeControllerContext的构造器。</p>
<h2 class="al-t1">测试查询字符串参数</h2>
<p class="al-p">假设你需要测试查询字符串参数是否被传递到一个视图中。查询字符串可以直接通过Request.QueryString集合访问。例如，控制器操作看起来可能是下面这样：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult Details()
{
  ViewData["key1"] = Request.QueryString["key1"];
  ViewData["key2"] = Request.QueryString["key2"];
  ViewData["count"] = Request.QueryString.Count;
  return View();
}
</textarea>
<p class="al-p">在这种情况下，你可以通过将一个NameValueCollection传递给FakeControllerContext的构造器来仿制查询字符串：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeQueryStringParams()
{
    // Create controller
    var controller = new HomeController();
    // Create fake controller context
    var queryStringParams = new NameValueCollection { { "key1", "a" }, { "key2", "b" } };
    controller.ControllerContext = new FakeControllerContext(controller, null, queryStringParams);
    // Act
    var result = controller.Details() as ViewResult;
    Assert.AreEqual("a", result.ViewData["key1"]);
    Assert.AreEqual("b", result.ViewData["key2"]);
    Assert.AreEqual(queryStringParams.Count, result.ViewData["count"]);
}
</textarea>
<p class="al-p">注意查询字符串要作为FakeControllerContext构造器的第二个参数传入。</p>
<h2 class="al-t1">测试用户</h2>
<p class="al-p">你可能需要测试控制器操作的安全性。例如，你可能希望仅为一个特定的已验证用户显示某个视图。下面的控制器操作为已验证用户显示一个Secret视图，而将所有其他用户重定向到Index视图：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ActionResult Secret()
{
    if (User.Identity.IsAuthenticated)
    {
        ViewData["userName"] = User.Identity.Name;
        return View("Secret");
    }
    else
    {
        return RedirectToAction("Index");
    }
}
</textarea>
<p class="al-p">你可以使用FakeController来测试该操作的行为是否为你预期的：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeUser()
{
    // Create controller
    var controller = new HomeController();
 
    // Check what happens for authenticated user
    controller.ControllerContext = new FakeControllerContext(controller, "Stephen");
    var result = controller.Secret() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(ViewResult));
    ViewDataDictionary viewData = ((ViewResult) result).ViewData;
    Assert.AreEqual("Stephen", viewData["userName"]);
 
    // Check what happens for anonymous user
    controller.ControllerContext = new FakeControllerContext(controller);            
    result = controller.Secret() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
</textarea>
<p class="al-p">该测试实际上测试了三样东西（也许应该分成多个测试）。首先，它测试了一个以验证用户是否从控制器操作取得了所需的视图。其次，它测试了已验证用户的用户名是否被成功地添加到了试图数据中。最后，它检查了匿名用户在控制其操作执行时是否被重定向了。</p>
<h2 class="al-t1">测试用户角色</h2>
<p class="al-p">你可能希望根据角色的不同，为不同的用户显示不同的内容。例如，某些内容可能只能由管理员（Admins）浏览。假设你有一个类似下面这样的控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ActionResult Admin()
{
    if (User.IsInRole("Admin"))
    {
        return View("Secret");
    }
    else
    {
        return RedirectToAction("Index");
    }
}
</textarea>
<p class="al-p">如果你是一个具备Admin角色的成员，该控制器操作将返回Secret视图。</p>
<p class="al-p">你可以通过下面的测试方法测试该控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeUserRoles()
{
    // Create controller
    var controller = new HomeController();
 
    // Check what happens for Admin user
    controller.ControllerContext = new FakeControllerContext(controller, "Stephen", new string[] {"Admin"});
    var result = controller.Admin() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(ViewResult));
 
    // Check what happens for anonymous user
    controller.ControllerContext = new FakeControllerContext(controller);
    result = controller.Admin() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
</textarea>
<p class="al-p">该操作能够验证是否只有Admin角色的成员能够看到Secret视图。该测试还检查了匿名用户是否被重定向到了其他页面。</p>
<h2 class="al-t1">测试浏览器Cookies</h2>
<p class="al-p">假设你需要测试访问了浏览器Cookies的操作。例如，你可能通过浏览器端Cookies传递了一个客户ID。如何测试这类操作呢？</p>
<p class="al-p">下面的控制器方法简单地讲一个浏览器Cookie添加到了视图数据中：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult TestCookie()
{
    ViewData["key"] = Request.Cookies["key"].Value;
    return View();
}
</textarea>
<p class="al-p">你可以通过创建一个SessionItemCollection并将其传递到FakeControllerContext中来测试该控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestCookies()
{
    // Create controller
    var controller = new HomeController();
 
    // Create fake Controller Context
    var cookies = new HttpCookieCollection();
    cookies.Add( new HttpCookie("key", "a"));
    controller.ControllerContext = new FakeControllerContext(controller, cookies);
    var result = controller.TestCookie() as ViewResult;
 
    // Assert
    Assert.AreEqual("a", result.ViewData["key"]);
}
</textarea>
<p class="al-p">该测试验证了从知其操作是否将名为key的Cookie添加到了视图数据中。</p>
<h2 class="al-t1">测试会话状态</h2>
<p class="al-p">最后的一个例子了。我们来看一下测试访问了会话状态的控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult TestSession()
{
    ViewData["item1"] = Session["item1"];
    Session["item2"] = "cool!";
    return View();
}
</textarea>
<p class="al-p">该控制器操作同时读和写了会话状态。它从会话状态中取出了一个名为item1的条目，并将其添加到试图数据中。该控制器操作还创建了一个名为item2的会话状态条目。</p>
<p class="al-p">使用下面的单元测试可以测试该控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestSessionState()
{
    // Create controller
    var controller = new HomeController();
 
    // Create fake Controller Context
    var sessionItems = new SessionStateItemCollection();
    sessionItems["item1"] = "wow!";
    controller.ControllerContext = new FakeControllerContext(controller, sessionItems);
    var result = controller.TestSession() as ViewResult;
 
    // Assert
    Assert.AreEqual("wow!", result.ViewData["item1"]);
 
    // Assert
    Assert.AreEqual("cool!", controller.HttpContext.Session["item2"]);
}
</textarea>
<p class="al-p">注意这里创建了一个SessionStateItemCollection并将其传给了FakeControllerContext的构造器。SessionStateItemCollection表示会话状态中的所有条目。</p>
<h2 class="al-t1">小结</h2>
<p class="al-p">在这个Tip中，我介绍了如何测试ASP.NET内部对象——如表单参数、查询字符串、用户实体、用户角色、Cookies和会话状态——使用的是一组标准的仿制类。通过下面的链接可以下载到这些仿制类的完整代码（同时有C#和VB.NET代码）。</p>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript">
dp.SyntaxHighlighter.HighlightAll('al-code');
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1251881.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47965/" target="_blank">Google App Engine宕机6小时——云的安全在哪里？</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #11 – 使用标准的控制器操作名称</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/21/asp-net-mvc-tip-11-use-standard-controller-action-names.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 21 Jul 2008 02:26:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/21/asp-net-mvc-tip-11-use-standard-controller-action-names.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1247433.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/21/asp-net-mvc-tip-11-use-standard-controller-action-names.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1247433.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1247433.html</trackback:ping><description><![CDATA[<p id="nzov" goog_docs_charindex="1">原文地址：<a id="nzov0" href="http://weblogs.asp.net/stephenwalther/archive/2008/06/27/asp-net-mvc-tip-11-use-standard-controller-action-names.aspx" goog_docs_charindex="7">http://weblogs.asp.net/stephenwalther/archive/2008/06/27/asp-net-mvc-tip-11-use-standard-controller-action-names.aspx</a></p>
<p id="nzov1" goog_docs_charindex="127">&nbsp;</p>
<p id="nzov2" goog_docs_charindex="130">摘要：在这个Tip中，Stephen Walther建议你为控制器操作使用标准的名字，这样可以使你的代码对其他开发者更明晰。</p>
<div style="text-align:center">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p id="hw8a" goog_docs_charindex="194">&nbsp;</p>
<p id="hw8a0" goog_docs_charindex="197">采用命名约定可以使其他开发者——以及你自己将来——更容易阅读你的代码。命名约定还可以为你节省时间，可以避免费力去讨论&#8220;正确的&#8221;命名方式。在这个Tip中，我建议为ASP.NET MVC控制器操作使用标准的名字。</p>
<p id="rw47" goog_docs_charindex="303">&nbsp;</p>
<p id="rw470" goog_docs_charindex="306">下面这个表格列出了控制器操作的标准名字：</p>
<p id="sora" goog_docs_charindex="328">&nbsp;</p>
<div id="w9x_" goog_docs_charindex="331">
<table id="el4e" class="zeroBorder" border="0" cellspacing="0" cellpadding="3" width="100%" goog_docs_charindex="332">
    <tbody id="w9x_0" goog_docs_charindex="333">
        <tr id="w9x_1" goog_docs_charindex="334">
            <td id="w9x_2" goog_docs_charindex="335">操作</td>
            <td id="w9x_3" goog_docs_charindex="339">示例URL</td>
            <td id="w9x_4" goog_docs_charindex="346">说明</td>
        </tr>
        <tr id="w9x_5" goog_docs_charindex="351">
            <td id="w9x_6" goog_docs_charindex="352">Details</td>
            <td id="w9x_7" goog_docs_charindex="361">/Product/Details/5</td>
            <td id="w9x_8" goog_docs_charindex="381">显示一个单独的资源如一条数据库记录。例如，显示Id为5的一个单独的Product。</td>
        </tr>
        <tr id="w9x_9" goog_docs_charindex="425">
            <td id="w9x_10" goog_docs_charindex="426">Index</td>
            <td id="w9x_11" goog_docs_charindex="433">/Product/Index</td>
            <td id="w9x_12" goog_docs_charindex="449">显示一组资源。例如，显示数据库里产品表中的所有产品。</td>
        </tr>
        <tr id="w9x_13" goog_docs_charindex="478">
            <td id="w9x_14" goog_docs_charindex="479">Create</td>
            <td id="w9x_15" goog_docs_charindex="487">/Product/Create</td>
            <td id="w9x_16" goog_docs_charindex="504">显示用于创建一个新资源的表单。例如，显示一个用于创建新产品的表单。</td>
        </tr>
        <tr id="w9x_17" goog_docs_charindex="540">
            <td id="w9x_18" goog_docs_charindex="541">Insert</td>
            <td id="w9x_19" goog_docs_charindex="549">/Product/Insert</td>
            <td id="w9x_20" goog_docs_charindex="566">将一个新资源插入数据库。通常，在完成一次插入后你应该重定向到其它操作。</td>
        </tr>
        <tr id="w9x_21" goog_docs_charindex="604">
            <td id="w9x_22" goog_docs_charindex="605">Edit</td>
            <td id="w9x_23" goog_docs_charindex="611">/Product/Edit/5</td>
            <td id="w9x_24" goog_docs_charindex="628">显示用于编辑一个现有资源的表单。例如，显示一个用于编辑Id为5的产品的表单。</td>
        </tr>
        <tr id="w9x_25" goog_docs_charindex="669">
            <td id="w9x_26" goog_docs_charindex="670">Update</td>
            <td id="w9x_27" goog_docs_charindex="678">/Product/Update/5</td>
            <td id="w9x_28" goog_docs_charindex="697">更新数据库中的现有资源。通常，在完成一次更新后你应该重定向到其它操作。</td>
        </tr>
        <tr id="w9x_29" goog_docs_charindex="735">
            <td id="w9x_30" goog_docs_charindex="736">Destroy</td>
            <td id="w9x_31" goog_docs_charindex="745">/Product/Destroy/5</td>
            <td id="w9x_32" goog_docs_charindex="765">显示一个页面，确认是否真的希望从数据库中删除一个资源。</td>
        </tr>
        <tr id="w9x_33" goog_docs_charindex="795">
            <td id="w9x_34" goog_docs_charindex="796">Delete</td>
            <td id="w9x_35" goog_docs_charindex="804">/Product/Delete/5</td>
            <td id="w9x_36" goog_docs_charindex="823">从数据库中删除一个资源。通常，在完成一次删除后你应该重定向到其它操作。</td>
        </tr>
        <tr id="w9x_37" goog_docs_charindex="861">
            <td id="w9x_38" goog_docs_charindex="862">Login</td>
            <td id="w9x_39" goog_docs_charindex="869">/Home/Login</td>
            <td id="w9x_40" goog_docs_charindex="882">显示一个登录表单。</td>
        </tr>
        <tr id="w9x_41" goog_docs_charindex="894">
            <td id="w9x_42" goog_docs_charindex="895">Logout</td>
            <td id="w9x_43" goog_docs_charindex="903">/Home/Logout</td>
            <td id="w9x_44" goog_docs_charindex="917">注销一个用户。通常，在完成一次注销后你应该重定向到其它操作。</td>
        </tr>
        <tr id="w9x_45" goog_docs_charindex="950">
            <td id="w9x_46" goog_docs_charindex="951">Authenticate</td>
            <td id="w9x_47" goog_docs_charindex="965">/Home/Authenticate</td>
            <td id="w9x_48" goog_docs_charindex="985">验证一对用户名和密码。通常，在完成一次验证后你应该重定向到其它操作。</td>
        </tr>
    </table>
</div>
<p id="v:qu" goog_docs_charindex="1025"><br id="w9x_49" goog_docs_charindex="1026" />
我的这些操作名字（非常粗略）是基于Adam Tybor在其MvcContrib项目的Simply Restful Route Handler中使用的命名约定的。该项目位于：</p>
<p id="v:qu0" goog_docs_charindex="1114">&nbsp;</p>
<p id="v:qu1" goog_docs_charindex="1117"><a id="jn13" href="http://www.codeplex.com/MVCContrib/Wiki/View.aspx?title=SimplyRestfulRouting&amp;referringTitle=Documentation" goog_docs_charindex="1118">http://www.codeplex.com/MVCContrib/Wiki/View.aspx?title=SimplyRestfulRouting&amp;referringTitle=Documentation</a></p>
<p id="or_6" goog_docs_charindex="1226">&nbsp;</p>
<p id="or_60" goog_docs_charindex="1229">然而，Adam Tybor的约定和我这里建议的有着显著的不同。下面我来介绍一下二者的区别。</p>
<p id="uweq" goog_docs_charindex="1276">&nbsp;</p>
<p id="uweq0" goog_docs_charindex="1279">首先，Adam Tybor利用不同的HTTP谓词来指出要执行那个控制器操作（他有&#8220;REST癖&#8221;，he is being a REST purist）。例如，Adam Tybor建议同一个URL既用于删除也用于更新一个资源：</p>
<p id="gl4k" goog_docs_charindex="1392">&nbsp;</p>
<p id="gl4k0" goog_docs_charindex="1395">/Product/34</p>
<p id="mmac" goog_docs_charindex="1408">&nbsp;</p>
<p id="mmac0" goog_docs_charindex="1411">当使用HTTP PUT请求该URL时，将更新一条现有记录。当使用HTTP DELETE请求相同的URL时，将删除一条现有记录。Adam Tybor提出的原因是有意义的。他的建议和这些HTTP谓词的目的是统一的。然而，Adam Tybor得难以不能和默认的ASP.NET MVC路由一起工作。因此，我建议你在进行删除和更新时使用明确的URL：</p>
<p id="c__f" goog_docs_charindex="1583">&nbsp;</p>
<p id="c__f0" goog_docs_charindex="1586">/Product/Update/34</p>
<p id="c__f1" goog_docs_charindex="1606">/Product/Delete/34</p>
<p id="cu.b" goog_docs_charindex="1626">&nbsp;</p>
<p id="cu.b0" goog_docs_charindex="1629">如果你使用这些命名约定，你将可以使用默认的ASP.NET MVC路由表。</p>
<p id="q:0t" goog_docs_charindex="1667">&nbsp;</p>
<p id="q:0t0" goog_docs_charindex="1670">我这里建议的控制器操作命名约定和我所见过的其他命名约定之间的另外一个重要区别在于，为创建一个新的资源这样一个操作所取的名字。我见过在创建新的资源时有人用过下面这样的操作名字：</p>
<p id="paxh" goog_docs_charindex="1759">&nbsp;</p>
<p id="paxh0" goog_docs_charindex="1762">/Product/New</p>
<p id="paxh1" goog_docs_charindex="1776">/Product/Create</p>
<p id="s02:" goog_docs_charindex="1793">&nbsp;</p>
<p id="s02:0" goog_docs_charindex="1796">通常，New操作用于显示用于创建新资源的表单，而Create操作实际执行数据库插入操作。</p>
<p id="ed92" goog_docs_charindex="1842">&nbsp;</p>
<p id="ed920" goog_docs_charindex="1845">问题在于使用New作为操作名字可能会和C#以及Visual Basic .NET关键字冲突。当在Visual Basic .NET应用程序中创建一个名为New的控制器操作时，您必须总要记得将其名字转义为[New]。持续地转义一个名字很快就会变得枯燥，而且对于那些不理解这个方括号的含义的人来说，这样会引起混淆。因此，我建议使用Create和Insert对来代替New和Create对。</p>
<p id="n3hr" goog_docs_charindex="2039">&nbsp;</p>
<p id="n3hr0" goog_docs_charindex="2042">你可能已经注意到了，我在试图让操作和（实际）执行的数据库操作中的SQL语句名字对应起来。因此，下面的URL应该是用于显示表单的：</p>
<p id="eni7" goog_docs_charindex="2108">&nbsp;</p>
<p id="eni70" goog_docs_charindex="2111">/Product/Create</p>
<p id="eni71" goog_docs_charindex="2128">/Product/Edit/1</p>
<p id="eni72" goog_docs_charindex="2145">/Product/Destroy/1</p>
<p id="eni73" goog_docs_charindex="2165">&nbsp;</p>
<p id="eni74" goog_docs_charindex="2168">而下面的URL应该是用于执行数据库操作的：</p>
<p id="eni75" goog_docs_charindex="2191">&nbsp;</p>
<p id="eni76" goog_docs_charindex="2194">/Product/Insert</p>
<p id="eni77" goog_docs_charindex="2211">/Product/Update/1</p>
<p id="eni78" goog_docs_charindex="2230">/Product/Delete/1</p>
<p id="zksd" goog_docs_charindex="2249">&nbsp;</p>
<p id="zksd0" goog_docs_charindex="2252">对命名约定提出建议总是有风险的，因为开发者对于如何正确地取名总是具有强烈的个人意见。然而，我希望这篇Tip能够作为你开发一款命名约定的起点。</p>
<p goog_docs_charindex="2252">&nbsp;</p>
<p goog_docs_charindex="2252">------</p>
<p goog_docs_charindex="2252">&nbsp;</p>
<p goog_docs_charindex="2252">本文没有代码。广告还是.NET正则表达式库，<a href="http://regex-lib.net">http://regex-lib.net</a>。</p><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1247433.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47961/" target="_blank">微软新推社交网站Windows Live Planet</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC CodePlex Preview 4 Release Notes</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/18/ASP-NET-MVC-CodePlex-Preview-4-Release-Notes.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 18 Jul 2008 08:25:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/18/ASP-NET-MVC-CodePlex-Preview-4-Release-Notes.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1246195.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/18/ASP-NET-MVC-CodePlex-Preview-4-Release-Notes.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1246195.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1246195.html</trackback:ping><description><![CDATA[<body>
<h1 class="al-title">ASP.NET MVC CodePlex Preview 4 Release Notes</h1>
<div class="al-copy">
 <p>您可以从这里下载到该文档：<a href="http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=15389" target="_blank">http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=15389</a>。</p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：本文档描述了ASP.NET MVC框架的Preview 3与目前的CodePlex Preview 4之间的差别。还介绍了要运行新的发布版，你必须对现有的MVC应用程序进行的修改。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">
 <ul class="al-ul">
  <li><a href="#1">简介</a></li>
  <li><a href="#2">What&#8217;s New</a>
   <ul>
    <li><a href="#2.1">默认项目模板中的简单成员资格特性</a></li>
    <li><a href="#2.2">用于授权和异常处理的过滤器类型</a></li>
    <li><a href="#2.3">输出缓存过滤器</a></li>
    <li><a href="#2.4">ASP.NET AJAX的变化</a></li>
    <li><a href="#2.5">路由的命名空间</a></li>
    <li><a href="#2.6">TempData的增强可测试性接口</a></li>
    <li><a href="#2.7">ActionInvoker可扩展改进</a></li>
   </ul></li>
 <li><a href="#3">ViewDataDictionary</a></li>
 <li><a href="#4">MVC的未来</a></li>
 <li><a href="#5">将现有的Preview3应用程序升级到Preview 4 CodePlex Release</a></li>
 <li><a href="#6">已知问题</a></li>
 </ul>
</p>
<h2 class="al-t1"><a name="1">简介</a></h2>
<p class="al-p">CodePlex Preview 4的发布包含了很多新的特性，并对默认项目模板进行了修改。对默认项目模板进行修改的目的是帮助启动最常见的应用程序构建场景。很多新特性都集中在简单的AJAX场景上。</p>
<h2 class="al-t1"><a name="2">What&#8217;s New</a></h2>
<p class="al-p">这一部分介绍了该发布版中新的和修改过的特性。</p>
<h3 class="al-t2"><a name="2.1">默认项目模板中的简单成员资格特性</a></h3>
<p class="al-p">很多Web应用程序都需要某种形式的用户验证。该发布版在默认项目模板中包含了一个AccountController类，用于处理下面这些用户操作：</p>
<ul class="al-dotul">
 <li>登录</li>
 <li>注销</li>
 <li>注册</li>
 <li>修改密码</li>
</ul>
<p class="al-p">默认项目模板的Web.config文件中包含了对<span class="al-inline-code">MembershipProvider</span>类的配置。这意味着当首次访问成员资格特性时，ASP.NET会自动创建一个SQL Server Express Edition成员资格数据库。你可以将SQL Server Express Edition数据库转换为SQL Server数据库，或修改Web.config文件中的连接字符串，使其指向一个现有的包含成员资格表的数据库。</p>
<p class="al-p">默认模板中还包含Microsoft ASP.NET AJAX脚本文件的一个副本。我们一直在持续地为MVC版的<span class="al-inline-code">ScriptManager</span>控件进行工作；例如，我们使注册包含在嵌入资源中的脚本更加容易。然而，这些改进没有包含在这个发布版中。</p>
<h3 class="al-t2"><a name="2.2">用于授权和异常处理的过滤器类型</a></h3>
<p class="al-p">该发布版包含两种新的过滤器类型——授权过滤器和异常过滤器。尽管操作过滤器依然是最常见的过滤器类型，但我们希望提供能够在任意或者所有操作过滤器之前运行的过滤器，而不管过滤器的作用域。这样可以防止某些情形的出现，如在<span class="al-inline-code">Authorization</span>过滤器之前执行<span class="al-inline-code">OutputCache</span>过滤器，从而导致骗取授权。</p>
<p class="al-p">为了支持新的过滤器类型，加入或修改了下列特性：</p>
<ul class="al-dotul">
 <li>新的<span class="al-inline-code">IAuthorizationFilter</span>和<span class="al-inline-code">IExceptionFilter</span>接口。授权过滤器可以确保在所有其他操作过滤器之前运行。每个异常过滤器都会执行，即便是某个过滤器指明它已经处理了请求。这有助于记录和处理异常。</li>
 <li><span class="al-inline-code">AuthorizeAttribute</span>类。这是<span class="al-inline-code">IAuthorizationFilter</span>的默认的具体实现。用于确保操作方法的安全。</li>
 <li><span class="al-inline-code">HandleErrorAttribute</span>类。这是<span class="al-inline-code">IExceptionFilter</span>的默认的具体实现。用于处理异常并指定在发生异常时呈现哪个视图。</li>
 <li>新的<span class="al-inline-code">FilterAttribute</span>基类。我们和新的过滤器类型一起引入了新的基类，这对所有过滤器特性都是有用的。</li>
</ul>
<h3 class="al-t2"><a name="2.3">输出缓存过滤器</a></h3>
<p class="al-p"><span class="al-inline-code">OutputCacheAttribute</span>类是一个新的操作过滤器，用于使用内建的ASP.NET输出缓存来缓存操作方法的输出。</p>
<div class="al-ins">
 <p class="al-ins-title">注意</p>
 <p class="al-p">在该发布版中，在Medium信任级别下为输出缓存过滤器设置<span class="al-inline-code">CacheProfile</span>属性会抛出一个异常。该问题会在未来的发布版中解决。</p>
</div>
<h3 class="al-t2"><a name="2.4">ASP.NET AJAX的变化</a></h3>
<p class="al-p">添加了下面的AJAX辅助方法。它们使用<span class="al-inline-code">AjaxOptions</span>类来指定异步操作的选项。</p>
<ul class="al-dotul">
 <li><span class="al-inline-code">ActionLink</span>。该方法呈现一个指向某操作方法的锚标签。当单击该链接时，会异步地调用操作方法。该辅助方法的典型应用是通过指定<span class="al-inline-code">AjaxOptions.UpdateTargetId</span>属性获取响应并更新DOM元素。</li>
 <li><span class="al-inline-code">Form</span>。该方法呈现一个可以异步提交的HTML表单。该辅助方法的典型应用是提交表单，和<span class="al-inline-code">ActionLink</span>类似，通过指定<span class="al-inline-code">AjaxOptions.UpdateTargetId</span>属性来获取响应并更新DOM元素。</li>
</ul>
<p class="al-p">我们将持续为ASP.NET MVC增强AJAX特性。这里提到的特性只是一个初级版本，我们希望在将来的发布版中能提供更强的特性。</p>
<h3 class="al-t2"><a name="2.5">路由的命名空间</a></h3>
<p class="al-p">在ASP.NET MVC之前的预览版中，框架会扫描所有程序集中的类型，以便找到控制器实现。然而，这样做偶尔会抛出异常，如某个类型是从一个尚未加载的程序集中的某个类型继承而来的，则框架在反射该类型时就会出错。</p>
<p class="al-p">在这个发布中，我们提供了一种途径，能够为框架指定在尝试加载控制器类型时去检查哪些命名空间，这需要使用<span class="al-inline-code">ControllerBuilder</span>类的<span class="al-inline-code">DefaultNamespaces</span>属性。下面的示例展示了如何为解析控制器加载命名空间。</p>
<div class="al-ins">
  <textarea name="al-code" class="C#" cols="60" rows="25">
void Application_Start(object sender, EventArgs e) {
    ControllerBuilder.Current.DefaultNamespaces.Add("MyApp.Controllers");
    ControllerBuilder.Current.DefaultNamespaces.Add("MyApp.Blog.Controllers");
    ControllerBuilder.Current.DefaultNamespaces.Add("ThirdPartyApp.Controllers");
    // ...
}</textarea>
</div>
<p class="al-p">你也可以依次为每个路由指定命名空间。（在该发布版中，你还不能使用<span class="al-inline-code">MapRoute</span>扩展方法来做这件事。）下面的例子展示了如何为一个路由指定命名空间。</p>
<div class="al-ins">
 <textarea name="al-code" class="C#" cols="60" rows="25">
var dataTokens = new RouteValueDictionary();
dataTokens.Add("namespaces", new HashSet<string>(
              new string[] {
                "MyApp.Blog.Controllers", 
                "MyApp.Forum.Controllers", 
                "MyApp.Controllers"
              }));
routes.Add(
  new Route("ns/{controller}/{action}/{id}", new MvcRouteHandler()) {
      Defaults = new RouteValueDictionary(new {
          action = "Index",
          id = (string)null
      }),
      DataTokens = dataTokens
});
</textarea>
</div>
<h3 class="al-t2"><a name="2.6">TempData的增强可测试性接口</a></h3>
<p class="al-p">在该发布版中，我们改进了可测试性，你可以使用会话Cookie而不是会话状态。添加了一个新的<span class="al-inline-code">ITempDataProvider</span>接口。默认情况下，控制器通过<span class="al-inline-code">SessionStateTempDataProvider</span>来访问会话状态，但现在可以实现其他的提供程序了。</p>
<h3 class="al-t2"><a name="2.7">ActionInvoker可扩展改进</a></h3>
<p class="al-p">添加了新的虚拟方法，在高级场景里你可以扩展调用器。下面的表格列出了新的方法。</p>
<table class="al-table">
 <tr><th>方法</th><th>说明</th></tr>
 <tr><td><span class="al-inline-code">GetFiltersForActionMethod</span></td><td>返回操作方法上的所有过滤器（授权、操作和异常过滤器）。</td></tr>
 <tr><td><span class="al-inline-code">InvokeActionResultWithFilters</span></td><td>在操作方法返回的<span class="al-inline-code">ActionResult</span>对象上调用<span class="al-inline-code">ExecuteResult</span>方法，以及应用于该操作方法的所有结果过滤器。</td></tr>
 <tr><td><span class="al-inline-code">InvokeAuthorizationFilters</span></td><td>调用应用于操作方法上的授权过滤器。</td></tr>
 <tr><td><span class="al-inline-code">InvokeExceptionFilters</span></td><td>调用应用于操作方法上的异常过滤器。</td></tr>
</table>
<h2 class="al-t1"><a name="3">ViewDataDictionary</a></h2>
<p class="al-p">我们对<span class="al-inline-code">ViewDataDictionary</span>进行了少许修改，将其索引器修改为一般索引器，并添加了<span class="al-inline-code">Eval</span>方法，用于对模型进行求值。</p>
<h2 class="al-t1"><a name="4">MVC的未来</a></h2>
<p class="al-p">ASP.NET MVC团队在日常的开发过程中，为很多特性建立了原型。这些特性有些会包含在RTM发布版中。如果没有包含进去，则可能会包含在未来的完整发布版中。我们将这些特性中的很多都放在了一个单独的MVC Futures项目中了。你会注意到项目模板包含并引用了一个名为<span class="al-inline-code">Microsoft.Web.Mvc.dll</span>的程序集，这就是MVC Futures项目的编译形式。</p>
<p class="al-p">注意<span class="al-inline-code">ComponentController</span>和<span class="al-inline-code">RenderComponent</span>方法已经被替换成了MVC Futures项目中定义的<span class="al-inline-code">RenderAction</span>方法。<span class="al-inline-code">RenderAction</span>与普通的控制器协作，而不是特定的<span class="al-inline-code">ComponentController</span>对象。</p>
<p class="al-p">对于该发布版，MVC Futures程序集（<span class="al-inline-code">Microsoft.Web.Mvc.dll</span>）被包含在了项目模板中，但在Beta和RTM发布版中就不会这样了。</p>
<h2 class="al-t1"><a name="5">将现有的Preview3应用程序升级到Preview 4 CodePlex Release</a></h2>
<p class="al-p">这一部分介绍了要想使使用Preview 3发布版创建的ASP.NET MVC应用程序能够运行在Preview 4发布版上，必须要对应用程序进行的修改。</p>
<ul class="al-dotul">
 <li>更新对下列程序集的引用，使其指向新的Preview 4版本的程序集：
  <ul class="al-dotul">
   <li><span class="al-inline-code">System.Web.Abstractions</span></li>
   <li><span class="al-inline-code">System.Web.Routing</span></li>
   <li><span class="al-inline-code">System.Web.Mvc</span></li>
  </ul>
  <p class="al-p">默认情况下，这程序集位于下面的文件夹中：</p>
  <p class="al-p">%ProgramFiles% \Microsoft ASP.NET\ASP.NET MVC CodePlex Preview 4</p>
 </li>
 <li>由于操作过滤器API有了些许改变，因此如果你编写了自定义的操作过滤器，你需要更新你的代码，匹配新的签名。</li>
 <li>为了利用模板中的一些变化，你可能需要将模板的变化复制到你的项目中。</li>
</ul>
<h2 class="al-t1"><a name="6">已知问题</a></h2>
<ul class="al-dotul">
 <li>在Medium信任下，在<span class="al-inline-code">OutputCache</span>过滤器上设置<span class="al-inline-code">CacheProfile</span>属性会抛出异常。</li>
 <li>注意如果要在Visual Studio 2008速成版中使用ASP.NET MVC，你需要安装Visual Studio 2008 Express Edition SP1，位于：<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=BDB6391C-05CA-4036-9154-6DF4F6DEBD14&displaylang=en" target="_blank">http://www.microsoft.com/downloads/details.aspx?FamilyId=BDB6391C-05CA-4036-9154-6DF4F6DEBD14&displaylang=en</a>。与此相关的更多信息，请查看ScottGu的博客主题：<a href="http://weblogs.asp.net/scottgu/archive/2008/06/01/asp-net-mvc-support-with-visual-web-developer-2008-express.aspx" target="_blank">http://weblogs.asp.net/scottgu/archive/2008/06/01/asp-net-mvc-support-with-visual-web-developer-2008-express.aspx</a>。</li>
</ul>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1246195.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47960/" target="_blank">火狐3.5版被指推出太匆忙：存在50多个漏洞</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #10 - 防止URL操作攻击</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/17/prevent-url-manipulation-attacks.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 17 Jul 2008 04:35:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/17/prevent-url-manipulation-attacks.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1245104.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/17/prevent-url-manipulation-attacks.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1245104.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1245104.html</trackback:ping><description><![CDATA[摘要: 原文地址：http://weblogs.asp.net/stephenwalther/archive/2008/06/26/prevent-url-manipulation-attacks.aspx摘要：在这个Tip中，Stephen Walther介绍了黑客如何通过操作URL从ASP.NET MVC网站中窃取敏感信息。Stephen Walther还探讨了如何构建单元测试来防止这类攻击。在一个网&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2008/07/17/prevent-url-manipulation-attacks.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1245104.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47959/" target="_blank">Google对手机搜索进行优化升级</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #9 – 创建GridView视图用户控件</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/17/asp-net-mvc-tip-9-create-a-gridview-view-user-control.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 17 Jul 2008 03:38:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/17/asp-net-mvc-tip-9-create-a-gridview-view-user-control.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1245057.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/17/asp-net-mvc-tip-9-create-a-gridview-view-user-control.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1245057.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1245057.html</trackback:ping><description><![CDATA[摘要: 原文地址：http://weblogs.asp.net/stephenwalther/archive/2008/06/25/asp-net-mvc-tip-9-create-a-gridview-view-user-control.aspx摘要：在这个Tip中，Stephen Walther向你展示了如何创建一个ASP.NET MVC视图用户控件，它能接受一组数据库记录，并自动在一个HTML表格中&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2008/07/17/asp-net-mvc-tip-9-create-a-gridview-view-user-control.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1245057.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47959/" target="_blank">Google对手机搜索进行优化升级</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #8 – 创建ASP.NET MVC GridView辅助方法</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/12/asp-net-mvc-tip-8-create-an-asp-net-mvc-gridview-helper-method.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 12 Jul 2008 09:27:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/12/asp-net-mvc-tip-8-create-an-asp-net-mvc-gridview-helper-method.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1241495.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/12/asp-net-mvc-tip-8-create-an-asp-net-mvc-gridview-helper-method.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1241495.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1241495.html</trackback:ping><description><![CDATA[摘要: 在这个Tip中，你将学到如何扩展ASP.NET MVC框架，创建一个新的辅助方法，可以为数据库数据显示一个HTML表格。&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2008/07/12/asp-net-mvc-tip-8-create-an-asp-net-mvc-gridview-helper-method.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1241495.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47958/" target="_blank">风声又起 Windows 7 RTM版7月13日完成</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item></channel></rss>