<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>语言编程 - 老赵点滴 - 追求编程之美</title>
    <link>http://blog.zhaojie.me/language/</link>
    <description>先做人，再做技术人员，最后做程序员。打造国内最好的.NET技术博客。</description>
    <language>zh-cn</language>
    <managingEditor>jeffz@live.com (老赵)</managingEditor>
    <webMaster>jeffz@live.com (老赵)</webMaster>
    <pubDate>Wed, 29 Feb 2012 15:44:54 GMT</pubDate>
    <lastBuildDate>Wed, 28 Dec 2011 04:45:46 GMT</lastBuildDate>
    <ttl>60</ttl>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>C#的设计缺陷（1）：显式实现接口内的事件</title>
      <link>http://blog.zhaojie.me/2012/05/csharp-design-flaws-1-explicitly-implement-interface-event.html</link>
      <guid>http://blog.zhaojie.me/2012/05/csharp-design-flaws-1-explicitly-implement-interface-event.html</guid>
      <description>&lt;p&gt;其实使用C#这么多年，我时不时会遇到一些令人不爽的设计缺陷。这些缺陷大都是些限制，虽说无伤大雅，也很容易避免，但一旦遇到这些情况，总会令人心生不快，毕竟都是些无谓的限制。而且令人遗憾的是，虽说去除这些限制也不会带来什么问题，但我认为C#设计团队也基本不会去修复这些问题了，毕竟它们大都是些细枝末节。作为一名用C#的纯种码农，我突然一时兴起也要把这些设计缺陷记录下，也方便和大伙一起讨论下。那么这次就先从实现接口内的事件说起，当我们需要显式实现一个接口内的事件时，会发现我们必须提供add和remove访问器，这还会稍许影响到事件常用的使用模式。&lt;/p&gt;

&lt;p&gt;这个问题听上去有些绕，不过看代码便一清二楚。例如，在项目中我会定义一个这样的INotifyPropertyChanged接口，其中包含一个PropertyChanged事件：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;TPropertyIdentity&amp;gt;
{
    &lt;span style="color: blue"&gt;event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;TPropertyIdentity&amp;gt;&amp;gt; PropertyChanged;
}

&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;TPropertyIdentity&amp;gt; : &lt;span style="color: #2b91af"&gt;EventArgs
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;public &lt;/span&gt;PropertyChangedEventArgs(TPropertyIdentity propertyIdentity)
    {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.PropertyIdentity = propertyIdentity;
    }

    &lt;span style="color: blue"&gt;public &lt;/span&gt;TPropertyIdentity PropertyIdentity { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;private set&lt;/span&gt;; }
}&lt;/pre&gt;

&lt;p&gt;可以看出这个接口和.NET内置的INotifyPropertyChanged事件可谓如出一辙，其实他们的目的也一样，就是向外通知该对象的某个属性发生了改变。不同的是，系统内置的PropertyChangedEventArgs对象使用属性名，也就是一个字符串标识一个属性，而在如上带泛型的PropertyChangedEventArgs里，则可以使用任意类型的对象来标识属性，这无疑带来的更多的灵活性。例如，我们可以使用连续的整型数值来标识对象，这样我们就可以使用数组来创建一个索引，它的性能会比使用字符串为键值的字典要高出一些。&lt;/p&gt;

&lt;p&gt;不过，我们实现系统自带的INotifyPropertyChanged属性时，并非是要“自行使用”，而往往是想让通知其他组件，例如ORM框架或是UI控件。因此，它其实已经是.NET平台上的统一约定，即便有所不足，也不能舍弃它。因此，我们往往需要在一个对象上同时实现两种INotifyPropertyChanged接口，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Item &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;, &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;
{
    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;&amp;gt; PropertyChanged;

    &lt;span style="color: blue"&gt;event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventHandler INotifyPropertyChanged&lt;/span&gt;.PropertyChanged
    {
        &lt;span style="color: blue"&gt;add &lt;/span&gt;{ &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;(); }
        &lt;span style="color: blue"&gt;remove &lt;/span&gt;{ &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;(); }
    }
}&lt;/pre&gt;

&lt;p&gt;以上是Visual Studio为两个事件实现自动生成的代码框架，且看第二个事件，它要求我们提供add和remove访问器。为什么？我不知道，&lt;a href="http://stackoverflow.com/questions/2268065/c-sharp-language-design-explicit-interface-implementation-of-an-event#2268472"&gt;C#开发团队自己可能也已经不太清楚这么规定的原因&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Interesting question. I did some poking around the language notes archive and I discovered that this decision was made on the 13th of October, 1999, but the notes do not give a justification for the decision.&lt;/p&gt;

  &lt;p&gt;Off the top of my head I don't see any theoretical or practical reason why we could not have field-like explicitly implemented events. Nor do I see any reason why we particularly need to. This may have to remain one of the mysteries of the unknown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="http://stackoverflow.com/users/88656/eric-lippert"&gt;Eric Lippert&lt;/a&gt;是老牌C#团队成员了，经常在Stack Overflow或是博客上写一些C#的设计内幕，可惜在这个问题上连他也认为是个“不解之谜”。此外，“自动属性”让这个限制进一步显得“无厘头”了，因为我们完全可以这么显式实现接口里的属性：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;INameProvider
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;string &lt;/span&gt;Name { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }
}

&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyNameProvider &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;INameProvider
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;string &lt;/span&gt;&lt;span style="color: #2b91af"&gt;INameProvider&lt;/span&gt;.Name { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }
}&lt;/pre&gt;

&lt;p&gt;既然如此，事件跟它又有什么本质区别呢？&lt;/p&gt;

&lt;p&gt;顺便一提，我们知道，在C#里不能把显式实现的接口成员标注为抽象成员，这对于事件来说还存在一些额外的问题。且看以下代码片段：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Base &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;
{
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;&amp;gt; PropertyChanged;

    &lt;span style="color: blue"&gt;protected void &lt;/span&gt;OnPropertyChanged(&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt; args)
    {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;propertyChanged = &lt;span style="color: blue"&gt;this&lt;/span&gt;.PropertyChanged;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(propertyChanged != &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        {
            propertyChanged(&lt;span style="color: blue"&gt;this&lt;/span&gt;, args);
        }
    }
}&lt;/pre&gt;

&lt;p&gt;Base是个基类，因此它往往会暴露个OnXyz方法，以便子类触发Xyz事件。在OnPropertyChanged方法中，我们会先判断_propertyChanged是否为null，因为null表示还没有人注册过事件——这是事件使用时的常见模式。事件本身没有注册任何处理器，则意味着事件本身不触发亦可，同样意味着我们甚至可以不去创建事件所需的EventArgs参数。但是，如果我们是要在子类里触发事件（即调用OnXxx方法），则没有办法检查该事件有没有注册处理器。假如这个EventArgs对象创建起来成本较高，就会造成一定的性能损失。&lt;/p&gt;

&lt;p&gt;解决方法倒也简单，例如，在基类里增加一个事件：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Base &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;
{
    &lt;span style="color: blue"&gt;public abstract event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;&amp;gt; MyIdentityPropertyChanged;

    &lt;span style="color: blue"&gt;event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;&amp;gt; &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;.PropertyChanged
    {
        &lt;span style="color: blue"&gt;add &lt;/span&gt;{ &lt;span style="color: blue"&gt;this&lt;/span&gt;.MyIdentityPropertyChanged += &lt;span style="color: blue"&gt;value&lt;/span&gt;; }
        &lt;span style="color: blue"&gt;remove &lt;/span&gt;{ &lt;span style="color: blue"&gt;this&lt;/span&gt;.MyIdentityPropertyChanged -= &lt;span style="color: blue"&gt;value&lt;/span&gt;; }
    }
}&lt;/pre&gt;

&lt;p&gt;或干脆加一个“延迟”构造EventArgs的重载：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Base &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;
{
    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;&amp;gt; _propertyChanged;

    &lt;span style="color: blue"&gt;event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;&amp;gt; &lt;span style="color: #2b91af"&gt;INotifyPropertyChanged&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;.PropertyChanged
    {
        &lt;span style="color: blue"&gt;add &lt;/span&gt;{ &lt;span style="color: blue"&gt;this&lt;/span&gt;._propertyChanged += &lt;span style="color: blue"&gt;value&lt;/span&gt;; }
        &lt;span style="color: blue"&gt;remove &lt;/span&gt;{ &lt;span style="color: blue"&gt;this&lt;/span&gt;._propertyChanged -= &lt;span style="color: blue"&gt;value&lt;/span&gt;; }
    }

    &lt;span style="color: blue"&gt;protected void &lt;/span&gt;OnPropertyChanged(&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt; args) { ... }

    &lt;span style="color: blue"&gt;protected void &lt;/span&gt;OnPropertyChanged(&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;&amp;gt; argsFactory) { ... }
}&lt;/pre&gt;

&lt;p&gt;于是在基类里触发事件时即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;this&lt;/span&gt;.OnPropertyChanged(() =&amp;gt; &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;PropertyChangedEventArgs&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;&amp;gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyIdentity&lt;/span&gt;()));&lt;/pre&gt;

&lt;p&gt;如果您觉得在没有事件处理器的情况下创建一个委托对象也是一种浪费，那么就自己想办法解决咯。没什么困难的，不应该想不出。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2012/05/csharp-design-flaws-1-explicitly-implement-interface-event.html#comments</comments>
      <pubDate>Sun, 20 May 2012 13:07:12 GMT</pubDate>
      <lastBuildDate>Sun, 20 May 2012 13:07:12 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>关于C#中async/await中的异常处理（下）</title>
      <link>http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-2.html</link>
      <guid>http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-2.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-1.html"&gt;上一篇文章&lt;/a&gt;里我们讨论了某些async/await的用法中出现遗漏异常的情况，并且谈到该如何使用WhenAll辅助方法来避免这种情况。WhenAll辅助方法将会汇总一系列的任务对象，一旦其中某个出错，则会抛出“其中一个”异常。那么究竟是哪个异常？如果我们要处理所有的异常怎么办？我们这次就来详细讨论await操作在异常分派时的相关行为。&lt;/p&gt;

&lt;h1&gt;await抛出异常时的行为&lt;/h1&gt;

&lt;p&gt;要理解await的行为，还是从理解Task对象的异常表现开始。Task对象有一个Exception属性，类型为AggregateException，在执行成功的情况下该属性返回null，否则便包含了“所有”出错的对象。既然是AggregateException，则意为着可能包含多个子异常，这种情况往往会在任务的父子关系中出现，&lt;a href="http://msdn.microsoft.com/en-us/library/dd997415.aspx"&gt;具体情况可以参考MSDN中的相关说明&lt;/a&gt;。在许多情况下一个Task内部只会出现一个异常，此时这个AggregateException的InnerExceptions属性自然也就只一个元素。&lt;/p&gt;

&lt;p&gt;Task对象本身还有一个Wait方法，它会阻塞当前执行代码，直到任务完成。在出现异常的时候，它会将自身的AggregateException抛出：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    t.Wait();
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;AggregateException &lt;/span&gt;ex)
{
    ...
}&lt;/pre&gt;

&lt;p&gt;Wait方法是“真阻塞”，而await操作则是使用阻塞语义的代码实现非阻塞的效果，这个区别一定要分清。与Wait方法不同的是，await操作符效果并非是“抛出”Task对象上的Exception属性，而只是抛出这个AggregateException对象上的“其中一个”元素。我在内部邮件列表中询问这么做的设计考虑，C#开发组的同学回答道，这个决策在内部也经历了激烈的争论，最终的选择这种方式而不是直接抛出Task对象上的AggregateException是为了避免编写出冗余的代码，并让代码与传统同步编程习惯更为接近。&lt;/p&gt;

&lt;p&gt;他们举了一个简单的示例，假如一个Task对象t可能抛出两种异常，现在的错误捕获方式为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;await &lt;/span&gt;t1;
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;NotSupportedException &lt;/span&gt;ex)
{
    ...
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;NotImplementedException &lt;/span&gt;ex)
{
    ...
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
{
    ...
}&lt;/pre&gt;

&lt;p&gt;假如await操作抛出的是AggregateException，那么代码就必须写为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;await &lt;/span&gt;t1;
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;AggregateException &lt;/span&gt;ex)
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;innerEx = ex.InnerExceptions[0];

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(innerEx &lt;span style="color: blue"&gt;is &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotSupportedException&lt;/span&gt;)
    {
        ...
    }
    &lt;span style="color: blue"&gt;else if &lt;/span&gt;(innerEx &lt;span style="color: blue"&gt;is &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;)
    {
        ...
    }
    &lt;span style="color: blue"&gt;else
    &lt;/span&gt;{
        ...
    }
}&lt;/pre&gt;

&lt;p&gt;显然前者更贴近传统的同步编程习惯。但是问题在于，如果这个Task中包含了多个异常怎么办？之前的描述是抛出“其中一个”异常，对于开发者来说，“其中一个”这种模糊的说法自然无法令人满意，但事实的确如此。从内部邮件列表中的讨论来看，C#开发团队提到他们“故意”不提供文档说明究竟会抛出哪个异常，因为他们并不想做出这方面的约束，因为这部分行为一旦写入文档，便成为一个规定和限制，为了类库的兼容性今后也无法对此做出修改。&lt;/p&gt;

&lt;p&gt;他们也提到，如果单论目前的实现，await操作会从Task.Exception.InnerExceptions集合中挑出第一个异常，并对外“抛出”，这是System.Runtime.CompilerServices.TaskAwaiter类中定义的行为。但是既然这并非是“文档化”的固定行为，开发人员也尽量不要依赖这点。&lt;/p&gt;

&lt;h1&gt;WhenAll的异常汇总方式&lt;/h1&gt;

&lt;p&gt;其实这个话题跟async/await的行为没有任何联系，WhenAll返回的是普通的Task对象，TaskAwaiter也丝毫不关心当前等待的Task对象是否来自于WhenAll，不过既然WhenAll是最常用的辅助方法之一，也顺便将其讲清楚吧。&lt;/p&gt;

&lt;p&gt;WhenAll得到Task对象，其结果是用数组存放的所有子Task的结果，而在出现异常时，其Exception属性返回的AggregateException集合会包含所有子Task中抛出的异常。请注意，每个子Task中抛出的异常将会存放在它自身的AggregateException集合中，WhenAll返回的Task对象将会“按顺序”收集各个AggregateException集合中的元素，而并非收集每个AggregateException对象。&lt;/p&gt;

&lt;p&gt;我们使用一个简单的例子来理解这点：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;all = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;await &lt;/span&gt;(all = &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.WhenAll(
        &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.WhenAll(
            ThrowAfter(3000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex3&amp;quot;&lt;/span&gt;)),
            ThrowAfter(1000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex1&amp;quot;&lt;/span&gt;))),
        ThrowAfter(2000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex2&amp;quot;&lt;/span&gt;))));
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
{
    ...
}&lt;/pre&gt;

&lt;p&gt;这段代码使用了嵌套的WhenAll方法，总共会出现三个异常，按其抛出的时机排序，其顺序为Ex1，Ex2及Ex3。那么请问：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;catch语句捕获的异常是哪个？ &lt;/li&gt;

  &lt;li&gt;all.Exception这个AggregateException集合中异常按顺序是哪些？ &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;结果如下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;catch语句捕获的异常是Ex3，因为它是all.Exception这个AggregateException集合中的第一个元素，但还是请牢记这点，这只是当前TaskAwaiter所实现的行为，而并非是由文档规定的结果。 &lt;/li&gt;

  &lt;li&gt;all.Exception这个AggregateException集合中异常有三个，按顺序是Ex3，Ex1和Ex2。WhenAll得到的Task对象，是根据输入的Task对象顺序来决定自身AggreagteException集合中异常对象的存放顺序。这个顺序跟异常的抛出时机没有任何关系。 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里我们也顺便可以得知，如果您不想捕获AggregateException集合中的“其中一个”异常，而是想处理所有异常的话，也可以写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;all = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;await &lt;/span&gt;(all = &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.WhenAll(
        ThrowAfter(1000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex1&amp;quot;&lt;/span&gt;)),
        ThrowAfter(2000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex2&amp;quot;&lt;/span&gt;))));
}
&lt;span style="color: blue"&gt;catch
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;ex &lt;span style="color: blue"&gt;in &lt;/span&gt;all.Exception.InnerExceptions)
    {
        ...
    }
}&lt;/pre&gt;

&lt;p&gt;当然，这里使用Task.WhenAll作为示例，是因为这个Task对象可以明确包含多个异常，但并非只有Task.WhenAll返回的Task对象才可能包含多个异常，例如Task对象在创建时指定了父子关系，也会让父任务里包含各个子任务里出现的异常。&lt;/p&gt;

&lt;h1&gt;假如异常未被捕获&lt;/h1&gt;

&lt;p&gt;最后再来看一个简单的问题，我们一直在关注一个async方法中“捕获”异常的行为，假如异常没有成功捕获，直接对外抛出的时候，对任务本身的有什么影响呢？且看这个示例：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static async &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;SomeTask()
{
    &lt;span style="color: blue"&gt;try
    &lt;/span&gt;{
        &lt;span style="color: blue"&gt;await &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.WhenAll(
            ThrowAfter(2000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotSupportedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex1&amp;quot;&lt;/span&gt;)),
            ThrowAfter(1000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex2&amp;quot;&lt;/span&gt;)));
    }
    &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;) { }
}

&lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
{
    _watch.Start();

    SomeTask().ContinueWith(t =&amp;gt; PrintException(t.Exception));

    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();
}&lt;/pre&gt;

&lt;p&gt;这段代码的输出结果是：&lt;/p&gt;

&lt;pre class="code"&gt;System.AggregateException: One or more errors occurred. ---&amp;gt; System.NotSupportedException: Ex1
   at AsyncErrorHandling.Program.&lt;throwafter&gt;d__0.MoveNext() in ...\Program.cs:line 16
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at AsyncErrorHandling.Program.&lt;sometask&gt;d__3.MoveNext() in ...\Program.cs:line 30
   --- End of inner exception stack trace ---
---&amp;gt; (Inner Exception #0) System.NotSupportedException: Ex1
   at AsyncErrorHandling.Program.&lt;throwafter&gt;d__0.MoveNext() in ...\Program.cs:line 16
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at AsyncErrorHandling.Program.&lt;sometask&gt;d__3.MoveNext() in ...\Program.cs:line 30&amp;lt;---&lt;/pre&gt;

&lt;p&gt;AggregateException的打印内容不那么容易读，我们可以关注它Inner Exception #0这样的信息。从时间上说，Ex2先于Ex1抛出，而catch的目标是NotImplementedException。但从之前的描述我们可以知道，WhenAll返回的Task内部的异常集合，与各异常抛出的时机没有关系，因此await操作符抛出的是Ex1，是NotSupportedException，而它不会被catch到，因此SomeTask返回的Task对象也会包含这个异常——也仅仅是抛出这个异常，而Ex2对于外部就不可见了。&lt;/p&gt;

&lt;p&gt;如果您想在外部处理所有的异常，则可以这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;all = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;await &lt;/span&gt;(all = &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.WhenAll(
        ThrowAfter(2000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotSupportedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex1&amp;quot;&lt;/span&gt;)),
        ThrowAfter(1000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Ex2&amp;quot;&lt;/span&gt;))));
}
&lt;span style="color: blue"&gt;catch
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;throw &lt;/span&gt;all.Exception;
}&lt;/pre&gt;

&lt;p&gt;此时打印的结果便是一个AggregateException包含着另一个AggregateException，其中包含了Ex1和Ex2。为了“解开”这种嵌套关系，AggregateException也提供了一个Flatten方法，可以将这种嵌套完全“铺平”，例如：&lt;/p&gt;

&lt;pre class="code"&gt;SomeTask().ContinueWith(t =&amp;gt; PrintException(t.Exception.&lt;span style="color: red"&gt;Flatten()&lt;/span&gt;));&lt;/pre&gt;

&lt;p&gt;此时打印的结果便直接是一个AggregateException包含着Ex1与Ex2了。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-1.html"&gt;关于C#中async/await中的错误处理（上）&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;关于C#中async/await中的错误处理（下） &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-2.html#comments</comments>
      <pubDate>Thu, 12 Apr 2012 01:26:43 GMT</pubDate>
      <lastBuildDate>Thu, 12 Apr 2012 03:50:07 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>关于C#中async/await中的异常处理（上）</title>
      <link>http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-1.html</link>
      <guid>http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-1.html</guid>
      <description>&lt;p&gt;在同步编程中，一旦出现错误就会抛出异常，我们可以使用try…catch来捕捉异常，而未被捕获的异常则会不断向上传递，形成一个简单而统一的错误处理机制。不过对于异步编程来说，异常处理一直是件麻烦的事情，这也是C#中async/await或是&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;等异步编程模型的优势之一。但是，同步的错误处理机制，并不能完全避免异步形式的错误处理方式，这需要一定实践规范来保证，至少我们需要了解async/await到底是如何捕获和分发异常的。在开发Jscex的过程中，我也在C#内部邮件邮件列表中了解了很多关于TPL和C#异步特性的问题，错误处理也是其中之一。在此记录一下吧。&lt;/p&gt;

&lt;h1&gt;使用try…catch捕获异常&lt;/h1&gt;

&lt;p&gt;首先我们来看下这段代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static async &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;ThrowAfter(&lt;span style="color: blue"&gt;int &lt;/span&gt;timeout, &lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
{
    &lt;span style="color: blue"&gt;await &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.Delay(timeout);
    &lt;span style="color: blue"&gt;throw &lt;/span&gt;ex;
}

&lt;span style="color: blue"&gt;static void &lt;/span&gt;PrintException(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
{
    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: maroon"&gt;&amp;quot;Time: {0}\n{1}\n============&amp;quot;&lt;/span&gt;, _watch.Elapsed, ex);
}

&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Stopwatch &lt;/span&gt;_watch = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Stopwatch&lt;/span&gt;();

&lt;span style="color: blue"&gt;static async &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;MissHandling()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t1 = ThrowAfter(1000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotSupportedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Error 1&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t2 = ThrowAfter(2000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Error 2&amp;quot;&lt;/span&gt;));

    &lt;span style="color: blue"&gt;try
    &lt;/span&gt;{
        &lt;span style="color: blue"&gt;await &lt;/span&gt;t1;
    }
    &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;NotSupportedException &lt;/span&gt;ex)
    {
        PrintException(ex);
    }
}

&lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
{
    _watch.Start();

    MissHandling();

    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();
}&lt;/pre&gt;

&lt;p&gt;这段代码的输出如下：&lt;/p&gt;

&lt;pre class="code"&gt;Time: 00:00:01.2058970
System.NotSupportedException: Error 1
   at AsyncErrorHandling.Program.&lt;throwafter&gt;d__0.MoveNext() in ...\Program.cs:line 16
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at AsyncErrorHandling.Program.&lt;misshandling&gt;d__3.MoveNext() in ...\Program.cs:line 33
============&lt;/pre&gt;

&lt;p&gt;在MissingHandling方法中，我们首先使用ThrowAfter方法开启两个任务，它们会分别在一秒及两秒后抛出两个不同的异常。但是在接下来的try中，我们只对t1进行await操作。很容易理解，t1抛出的NotSupportedException将被catch捕获，耗时大约为1秒左右——当然，从上面的数据可以看出，其实t1在被“捕获”时已经耗费了1.2时间，误差较大。这是因为程序刚启动，TPL内部正处于“热身”状态，在调度上会有较大开销。这里反倒是另一个问题倒更值得关注：t2在两秒后抛出的NotImplementedException到哪里去了？&lt;/p&gt;

&lt;h1&gt;未捕获的异常&lt;/h1&gt;

&lt;p&gt;C#的async/await功能基于TPL的Task对象，每个await操作符都是“等待”一个Task完成。在之前（或者说如今）的TPL中，Task对象的析构函数会查看它的Exception对象有没有被“访问”过，如果没有，且Task对象出现了异常，则会抛出这个异常，最终导致的结果往往便是进程退出。因此，我们必须小心翼翼地处理每一个Task对象的错误，不得遗漏。&lt;a href="http://msdn.microsoft.com/en-us/library/hh367887(v=VS.110).aspx#core"&gt;在.NET 4.5中这个行为被改变了&lt;/a&gt;，对于任何没有被检查过的异常，便会触发TaskSchedular.UnobservedTaskException事件——如果您不监听这个事件，未捕获的异常也就这么无影无踪了。&lt;/p&gt;

&lt;p&gt;为此，我们对Main方法进行一个简单的改造。&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
{
    &lt;span style="color: #2b91af"&gt;TaskScheduler&lt;/span&gt;.UnobservedTaskException += (_, ev) =&amp;gt; PrintException(ev.Exception);

    _watch.Start();

    MissHandling();

    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;)
    {
        &lt;span style="color: #2b91af"&gt;Thread&lt;/span&gt;.Sleep(1000);
        &lt;span style="color: #2b91af"&gt;GC&lt;/span&gt;.Collect();
    }
}&lt;/pre&gt;

&lt;p&gt;改造有两点，一是响应TaskScheduler.UnobservedTaskException，这自然不必多说。还有一点便是不断地触发垃圾回收，以便Finalizer线程调用析构函数。如今这段代码除了打印出之前的信息之外，还会输出以下内容：&lt;/p&gt;

&lt;pre class="code"&gt;Time: 00:00:03.0984560
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---&amp;gt; System.NotImplementedException: Error 2
   at AsyncErrorHandling.Program.d__0.MoveNext() in ...\Program.cs:line 16
   --- End of inner exception stack trace ---
---&amp;gt; (Inner Exception #0) System.NotImplementedException: Error 2
   at AsyncErrorHandling.Program.d__0.MoveNext() in ...\Program.cs:line 16&amp;lt;---
============&lt;/pre&gt;

&lt;p&gt;从上面的信息中可以看出，UnobservedTaskException事件并非在“抛出”异常后便立即触发，而是在某次垃圾收集过程，从Finalizer线程里触发并执行。从中也不难得出这样的结论：便是该事件的响应方法不能过于耗时，更加不能阻塞，否则便会对程序性能造成灾难性的影响。&lt;/p&gt;

&lt;p&gt;那么假如我们要同时处理t1和t2中抛出的异常该怎么做呢？此时便是Task.WhenAll方法上场的时候了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static async &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task &lt;/span&gt;BothHandled()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t1 = ThrowAfter(1000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotSupportedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Error 1&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t2 = ThrowAfter(2000, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;(&lt;span style="color: maroon"&gt;&amp;quot;Error 2&amp;quot;&lt;/span&gt;));
    
    &lt;span style="color: blue"&gt;try
    &lt;/span&gt;{
        &lt;span style="color: blue"&gt;await &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;.WhenAll(t1, t2);
    }
    &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;NotSupportedException &lt;/span&gt;ex)
    {
        PrintException(ex);
    }
}&lt;/pre&gt;

&lt;p&gt;如果您执行这段代码，会发现其输出与第一段代码相同，但其实不同的是，第一段代码中t2的异常被“遗漏”了，而目前这段代码t1和t2的异常都被捕获了，只不过await语句仅仅“抛出”了“其中一个”异常而已。&lt;/p&gt;

&lt;p&gt;WhenAll是一个辅助方法，它的输入是n个Task对象，输出则是个返回它们的结果数组的Task对象。新的Task对象会在所有输入全部“结束”后才完成。在这里“结束”的意思包括成功和失败（取消也是失败的一种，即抛出了OperationCanceledException）。换句话说，假如这n个输入中的某个Task对象很快便失败了，也必须等待其他所有输入对象成功或是失败之后，新的Task对象才算完成。而新的Task对象完成后又可能会有两种表现：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;所有输入Task对象都成功了：则返回它们的结果数组。 &lt;/li&gt;

  &lt;li&gt;至少一个输入Task对象失败了：则抛出“其中一个”异常。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;全部成功的情况自不必说，那么在失败的情况下，什么叫做抛出“其中一个”异常？如果我们要处理所有抛出的异常该怎么办？下次我们继续讨论这方面的问题。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;关于C#中async/await中的异常处理（上） &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-2.html"&gt;关于C#中async/await中的异常处理（下）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2012/04/exception-handling-in-csharp-async-await-1.html#comments</comments>
      <pubDate>Wed, 11 Apr 2012 01:15:55 GMT</pubDate>
      <lastBuildDate>Mon, 14 May 2012 14:16:32 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>使用Jscex改进Node Club（4）：改写首页</title>
      <link>http://blog.zhaojie.me/2012/03/jscexify-nodeclub-4-jscexify-home-page.html</link>
      <guid>http://blog.zhaojie.me/2012/03/jscexify-nodeclub-4-jscexify-home-page.html</guid>
      <description>&lt;p&gt;上次我们分析了Node Club的首页实现，了解了它的功能以及目前的实现方式。不过在我看来，如今使用EventProxy来辅助页面开发并没有解决部分异步编程中的主要问题。甚至可以说，就目前的EventProxy的使用方式而言，即便不借助任何类库，单纯基于JavaScript也可以得到有过之而无不及的编程体验。这次我们便来使用尝试使用&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;来改进首页的逻辑。&lt;/p&gt;

&lt;h1&gt;最后准备&lt;/h1&gt;

&lt;p&gt;还记得首页的逻辑吗？其中可是用到了许多异步方法：&lt;/p&gt;

&lt;pre class="code"&gt;tag_ctrl.get_all_tags(&lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tags) { ... });
topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, topics) { ... });
topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, hot_topics) { ... });
user_ctrl.get_users_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, users) { ... });
user_ctrl.get_users_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tops) { ... });
topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, no_reply_topics) { ... });
topic_ctrl.get_count_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, all_topics_count) { ... });&lt;/pre&gt;

&lt;p&gt;如果要用Jscex来实现首页，这些方法都必须变成Jscex异步方法。之前有朋友说，在一个现成的项目中使用Jscex代价太高，因为每个函数都必须Jscex化。但事实上，我们完全可以在一个现有的项目逐步地引入Jscex，因为我们轻易地将已有的异步操作“封装”为Jscex异步方法，例如&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/cb7082873452261a175c282c8c5ee6f2257a8f46"&gt;在controllers/topic.js文件中&lt;/a&gt;：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;/********** Jscex ************/
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscex = require(&lt;span style="color: maroon"&gt;&amp;quot;../libs/jscex&amp;quot;&lt;/span&gt;).Jscex;
&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscexify = Jscex.Async.Jscexify;

exports.get_topics_by_query_async = Jscexify.fromStandard(get_topics_by_query);
exports.get_count_by_query_async = Jscexify.fromStandard(get_count_by_query);&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/a1b7635ea846828045b4e539887b20b0c6a472ff"&gt;其他两个方法的封装&lt;/a&gt;就不列举出来了。可以发现，我们完全不需要一次性把所有的依赖都“重新实现”，可以用到哪儿再封装哪儿，稍后再进行真正的Jscex化——有时候甚至完全无需基于Jscex再写一遍。Jscex适合编写异步操作之间相对复杂的交互，但对于原本就十分简单的异步操作来说，Jscex也并不会带来明显的附加优势。此时我们完全可以保留最普通的异步回调写法，这从任何角度来说都造成问题。&lt;/p&gt;

&lt;h1&gt;实现首页&lt;/h1&gt;

&lt;p&gt;还记得之前提出的首页逻辑吗？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(request, response) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;tags = tag_ctrl.get_all_tags(); &lt;span style="color: #006400"&gt;// 标签
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;topics = topic_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 最新话题
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;hot_topics = topic_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 热门话题
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;stars = user_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 明星用户
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;tops = user_ctrl.get_users_by_query(...); &lt;span style="color: #006400"&gt;// 得分最高用户
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;no_reply_topics = topic_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 无回复话题
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;topic_count = topic_ctrl.get_count_by_query(...); &lt;span style="color: #006400"&gt;// 话题总数

    &lt;/span&gt;response.render(&lt;span style="color: maroon"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;, { ... }); &lt;span style="color: #006400"&gt;// 输出HTML
&lt;/span&gt;}&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/5959221771f3bfac27abc466de019cd394f80654#diff-0"&gt;使用Jscex来实现这个逻辑&lt;/a&gt;的话，与上述“伪代码”可谓完全一致，唯一的区别只是多了些前后附加的计算逻辑：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;indexAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(req, res) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;page = Number(req.query.page) || 1;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;limit = config.list_topic_count;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;data = {
        current_page: page,
        list_topic_count: limit
    };

    data.topics = $await(topic_ctrl.get_topics_by_query_async(...));

    data.hot_topics = $await(topic_ctrl.get_topics_by_query_async(...));

    data.stars = $await(user_ctrl.get_users_by_query_async(...));

    data.tops = $await(user_ctrl.get_users_by_query_async(...));

    data.no_reply_topics = $await(topic_ctrl.get_topics_by_query_async(...));

    &lt;span style="color: blue"&gt;var &lt;/span&gt;all_topics_count = $await(topic_ctrl.get_count_by_query_async(...));
    data.pages = Math.ceil(all_topics_count / limit);

    data.tags = $await(tag_ctrl.get_all_tags_async());

    &lt;span style="color: #006400"&gt;// 计算最热标签
    &lt;/span&gt;data.hot_tags = _.chain(data.tags)
        .sortBy(&lt;span style="color: blue"&gt;function &lt;/span&gt;(t) { &lt;span style="color: blue"&gt;return &lt;/span&gt;-t.topic_count; })
        .first(5);

    &lt;span style="color: #006400"&gt;// 计算最新标签
    &lt;/span&gt;data.recent_tags = _.chain(data.tags)
        .sortBy(&lt;span style="color: blue"&gt;function &lt;/span&gt;(t) { &lt;span style="color: blue"&gt;return &lt;/span&gt;-t.create_at.valueOf() })
        .first(5);

    res.render(&lt;span style="color: maroon"&gt;'index'&lt;/span&gt;, data);
}));

exports.index = Unjscexify.toRequestHandler(indexAsync);&lt;/pre&gt;

&lt;p&gt;最后一段代码，是使用一个&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/5959221771f3bfac27abc466de019cd394f80654#diff-1"&gt;辅助方法&lt;/a&gt;，将一个Jscex异步方法转化为一个普通的HTTP Request Handler：&lt;/p&gt;

&lt;pre class="code"&gt;Jscex.Unjscexify = {
    toRequestHandler: &lt;span style="color: blue"&gt;function &lt;/span&gt;(fn) {
        &lt;span style="color: blue"&gt;return function &lt;/span&gt;(req, res, next) {
            fn(req, res).addEventListener(&lt;span style="color: maroon"&gt;&amp;quot;failure&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                next(&lt;span style="color: blue"&gt;this&lt;/span&gt;.error);
            }).start();
        }
    }
}&lt;/pre&gt;

&lt;p&gt;这里我们将发生的任何错误都通过next向外传递，这是标准的处理方式，当然您也可以使用您自己的错误处理策略。您会发现，我们无需复杂的错误处理方式，在编写Jscex方法时，错误会像普通异常一样向外抛出，直到被统一捕获。&lt;/p&gt;

&lt;p&gt;我这里还想提一下Node Club代码里的一些问题。Node Club在我看来是一个比较粗糙的项目，代码质量不太好，一方面是代码风格有些乱（例如使用Tab，符号周围空格等等），还有便是把一些简单的代码写得复杂，例如上面“计算最热标签”这样的逻辑，目前的实现是：&lt;/p&gt;

&lt;pre class="code"&gt;tags.sort(&lt;span style="color: blue"&gt;function &lt;/span&gt;(tag_a, tag_b) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(tag_a.topic_count == tag_b.topic_count) &lt;span style="color: blue"&gt;return &lt;/span&gt;0;
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(tag_a.topic_count &amp;gt; tag_b.topic_count) &lt;span style="color: blue"&gt;return &lt;/span&gt;-1;
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(tag_a.topic_count &amp;lt; tag_b.topic_count) &lt;span style="color: blue"&gt;return &lt;/span&gt;1;
});&lt;/pre&gt;

&lt;p&gt;其实这里完全可以一句话实现：&lt;/p&gt;

&lt;pre class="code"&gt;tags.sort(&lt;span style="color: blue"&gt;function &lt;/span&gt;(tag_a, tag_b) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;tag_b.topic_count - tag_a.topic_count;
});&lt;/pre&gt;

&lt;p&gt;但我更喜欢使用&lt;a href="http://documentcloud.github.com/underscore/"&gt;Underscore&lt;/a&gt;提供的函数式编程方式，很重要的一点是它保持了输入数据的不变性。数组的sort方法是对本身进行排序，因此Node Club不得不创建一个数组拷贝。使用Underscore可以简化许多代码逻辑，就好比在实现相同功能时，C#代码比Java语言要简单许多。&lt;/p&gt;

&lt;h1&gt;并发&lt;/h1&gt;

&lt;p&gt;并发是好事，不过在上面的实现中，所有的操作是串行的。有人会说这么做起不到并发的效果，但其实我并不在意，因为我们目前的应用是一个服务器端程序，本身就是并发地承受客户端的请求。如果把处理一个请求看作一个事务的话，我们认为单个事务时串行的，但是已经有大量并发的事务。即便事务里的每个操作都并发起来，但单个事务还是要等到所有操作结束后才能完成（即用户看到页面）。由于系统的负载并没有降低，单个事务处理的总时长并没有减少。&lt;/p&gt;

&lt;p&gt;您可以做个实验，硬盘上有10个1G大小的文件，您使用顺序的方式复制所有文件，和同时复制这10个文件，所花时间分别是多少？做过服务器压力测试的同学一定知道，我们加大并发量时，处理相同数量请求所需的总时长并不会减少，甚至随着并发量增加，单位时间内的请求处理能力会明显降低。因此，现代的服务器一般都会在并发量增大的情况下采取保护措施，例如对请求排队，或是返回Service Unavailable错误等等。&lt;/p&gt;

&lt;p&gt;并发还有个问题，就是当一个操作失败时，很难“取消”其他操作，这会造成无谓的资源浪费。因此，如果我们盲目地将可以并发的操作都并发起来，对服务的健康并没有什么好处，除非可以确定这个并发操作的确可以同时进行（例如网络访问和磁盘读取），否则我并不倾向于（在一个服务器应用程序里）并发访问。不过作为一个演示，我便&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/39f75d74cce1bcd8fc9921aaf969c334e37336c2"&gt;将所有操作都并发起来&lt;/a&gt;吧：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;indexAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(req, res) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;page = Number(req.query.page) || 1;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;limit = config.list_topic_count;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;data = $await(&lt;span style="color: red"&gt;Task.whenAll&lt;/span&gt;({
        topics: topic_ctrl.get_topics_by_query_async({}, {
            skip: (page - 1) * limit,
            limit: limit,
            sort: [[&lt;span style="color: maroon"&gt;'last_reply_at'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'desc'&lt;/span&gt;]]
        }),
        hot_topics: topic_ctrl.get_topics_by_query_async({}, {
            limit: 5,
            sort: [[&lt;span style="color: maroon"&gt;'visit_count'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'desc'&lt;/span&gt;]]
        }),
        stars: user_ctrl.get_users_by_query_async(
            { is_star: &lt;span style="color: blue"&gt;true &lt;/span&gt;},
            { limit: 5 }
        ),
        tops: user_ctrl.get_users_by_query_async({}, {
            limit: 10,
            sort: [[&lt;span style="color: maroon"&gt;'score'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'desc'&lt;/span&gt;]]
        }),
        no_reply_topics: topic_ctrl.get_topics_by_query_async(
            { reply_count: 0 },
            { limit: 5, sort: [[&lt;span style="color: maroon"&gt;'create_at'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'desc'&lt;/span&gt;]] }
        ),
        tags: tag_ctrl.get_all_tags_async(),
        all_topics_count: topic_ctrl.get_count_by_query_async({})
    }));

    data.current_page = page;
    data.list_topic_count = limit;
    data.pages = Math.ceil(data.all_topics_count / limit);

    &lt;span style="color: #006400"&gt;// 计算最热标签
    &lt;/span&gt;data.hot_tags = _.chain(data.tags)
        .sortBy(&lt;span style="color: blue"&gt;function &lt;/span&gt;(t) { &lt;span style="color: blue"&gt;return &lt;/span&gt;-t.topic_count; })
        .first(5);

    &lt;span style="color: #006400"&gt;// 计算最新标签
    &lt;/span&gt;data.recent_tags = _.chain(data.tags)
        .sortBy(&lt;span style="color: blue"&gt;function &lt;/span&gt;(t) { &lt;span style="color: blue"&gt;return &lt;/span&gt;-t.create_at.valueOf() })
        .first(5);

    res.render(&lt;span style="color: maroon"&gt;'index'&lt;/span&gt;, data);
}))&lt;/pre&gt;

&lt;p&gt;Jscex类库“不提倡”盲目并发，它的并发是“可选”的。如果您想要并发哪些操作，将其放在Task.whenAll即可。Task.whenAll是一个辅助方法，您可以输入一个保存Task的对象或是数组（甚至是对象和数组的嵌套），whenAll返回的Task对象会同时发起那些操作，并等待它们全部返回。返回的结果对象，其结构会和输入时完全一致，或是对象，或是数组（甚至对象和数组的嵌套）。在Jscex中，哪些操作串行，哪些操作并发都是由开发人员决定的，完全可以将其轻松地混合起来。例如，我们串行地执行一些前期处理，例如用户认证，然后再将后续的数个操作并发起来。&lt;/p&gt;

&lt;p&gt;在Node Club中有个问题就是“盲目并发”，它是用EventProxy将所有的操作并发起来，而并非有选择的处理。EventProxy适合“盲目并发”的场景，但是对“有选择的并发”支持很差，我们很难选择部分操作串行执行。例如我之前的一个示例“&lt;a href="https://github.com/JeffreyZhao/jscex/blob/master/doc/async/samples/copy-dir-cn.md"&gt;复制完整目录&lt;/a&gt;”，&lt;a href="https://github.com/JeffreyZhao/talks/blob/master/nodeparty-20120108/copy-dir/copy-dir.js"&gt;使用Jscex只要如普通编程那样直接实现逻辑&lt;/a&gt;即可，而无需&lt;a href="https://github.com/JeffreyZhao/talks/blob/master/nodeparty-20120108/copy-dir/copy-dir-raw.js"&gt;如传统编程那样使用各种回调&lt;/a&gt;，但如果非要“事件驱动”，非要生搬硬套EventProxy，事件和回调交织在一起，&lt;a href="https://github.com/JeffreyZhao/talks/blob/master/nodeparty-20120108/copy-dir/copy-dir-eventproxy.js"&gt;实现便会变得非常复杂&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;作为一个面向开发人员的工具，Jscex除了隐藏必要的复杂度之外，还要让目标程序“可控”，无论是串行、并发还是逻辑表达——Jscex使用JavaScript语法，保证了程序逻辑的灵活与可控，尽可能地避免出现&lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;Leaky Abstraction&lt;/a&gt;。EventProxy的确提供了一种“完全并发”的抽象，但是对于需要“可控并发”，或是“串行执行”的逻辑和场景便显得无能为力了。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/02/jscexify-nodeclub-1-prepare-nodeclub-website.html"&gt;使用Jscex改进Node Club（1）：运行Node Club网站&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/02/jscexify-nodeclub-2-import-jscex.html"&gt;使用Jscex改进Node Club（2）：引入Jscex类库&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/02/jscexify-nodeclub-3-home-page-implementation.html"&gt;使用Jscex改进Node Club（3）：分析首页实现&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;使用Jscex改进Node Club（4）：改写首页&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2012/03/jscexify-nodeclub-4-jscexify-home-page.html#comments</comments>
      <pubDate>Sat, 10 Mar 2012 06:11:50 GMT</pubDate>
      <lastBuildDate>Mon, 12 Mar 2012 02:00:53 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>使用Jscex改进Node Club（3）：分析首页实现</title>
      <link>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-3-home-page-implementation.html</link>
      <guid>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-3-home-page-implementation.html</guid>
      <description>&lt;p&gt;上次我们已经将&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;成功地引入项目，现在便可以正式开始关注Node Club的实现了。Node Club中存在大量基于回调的JavaScript代码，颇有无从下手的感觉。既然如此，我们便随便挑一个，从首页入手吧！&lt;/p&gt;

&lt;h1&gt;首页逻辑&lt;/h1&gt;

&lt;p&gt;我们先从首页的JavaScript代码开始。首页的目标其实很简单，加载几部分数据组成一个对象，再交给模板引擎生成HTML代码并输出。就目前来说，显示首页需要加载以下数据：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;标签 &lt;/li&gt;

  &lt;li&gt;最新话题 &lt;/li&gt;

  &lt;li&gt;热门话题 &lt;/li&gt;

  &lt;li&gt;明星用户 &lt;/li&gt;

  &lt;li&gt;得分最高的用户 &lt;/li&gt;

  &lt;li&gt;无回复的话题 &lt;/li&gt;

  &lt;li&gt;话题总数 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;对于传统Web开发技术来说，要做到这点着实容易，伪代码如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(request, response) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;tags = tag_ctrl.get_all_tags(); &lt;span style="color: #006400"&gt;// 标签
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;topics = topic_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 最新话题
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;hot_topics = topic_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 热门话题
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;stars = user_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 明星用户
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;tops = user_ctrl.get_users_by_query(...); &lt;span style="color: #006400"&gt;// 得分最高用户
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;no_reply_topics = topic_ctrl.get_topics_by_query(...); &lt;span style="color: #006400"&gt;// 无回复话题
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;topic_count = topic_ctrl.get_count_by_query(...); &lt;span style="color: #006400"&gt;// 话题总数

    &lt;/span&gt;response.render(&lt;span style="color: maroon"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;, { ... }); &lt;span style="color: #006400"&gt;// 输出HTML
&lt;/span&gt;}&lt;/pre&gt;

&lt;p&gt;但是在Node.js这个大环境中，这些方法都是用回调函数来返回结果的，因此代码往往会写成：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(request, response, next) {
    &lt;span style="color: #006400"&gt;// 标签
    &lt;/span&gt;tag_ctrl.get_all_tags(&lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tags) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        
        &lt;span style="color: #006400"&gt;// 最新话题
        &lt;/span&gt;topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, topics) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
            
            &lt;span style="color: #006400"&gt;// 热门话题
            &lt;/span&gt;topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, hot_topics) {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
                
                &lt;span style="color: #006400"&gt;// 明星用户
                &lt;/span&gt;topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, stars) {
                    &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
                    
                    &lt;span style="color: #006400"&gt;// 得分最高用户
                    &lt;/span&gt;user_ctrl.get_users_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tops) {
                        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
                        
                        &lt;span style="color: #006400"&gt;// 无回复话题
                        &lt;/span&gt;topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, no_reply_topics) {
                            &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
                            
                            topic_ctrl.get_count_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, topic_count) {
                                &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
                                
                                &lt;span style="color: #006400"&gt;// 输出HTML
                                &lt;/span&gt;response.render(&lt;span style="color: maroon"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;, { ... });
                            });
                        });
                    }); 
                });
            });
        });
    });
}&lt;/pre&gt;

&lt;p&gt;很多人不喜欢这类层层嵌套的代码，但老实说，因为这里没有涉及逻辑判断，循环等令人头大的问题，所以在我看来其实这种串行的逻辑其实十分清晰（尽管不太漂亮），一眼就能看清楚在做什么。其实让我不喜欢的倒是这里不断出现的错误处理代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);&lt;/pre&gt;

&lt;p&gt;我一直把反复出现的错误处理代码作为传统异步编程中最麻烦（又不可遗漏）的方面之一。可惜在大量的示例代码中，这反而会被人忽略掉，造成其实“不怎么麻烦”的假象。我认为，作为一个优秀的异步类库，都应该在错误处理上花点功夫，避免开发人员在各个地方不断浪费青春。例如，在这方面各类Promise模型作的都不错。&lt;/p&gt;

&lt;h1&gt;首页实现&lt;/h1&gt;

&lt;p&gt;Node Club使用&lt;a href="https://github.com/JacksonTian/eventproxy"&gt;EventProxy&lt;/a&gt;类库来尝试解决大量异步函数的嵌套问题。在首页上主要使用了以下这种模式：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(request, response, next) {

    &lt;span style="color: #006400"&gt;// 定义最终的回调函数&lt;/span&gt;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;render = &lt;span style="color: blue"&gt;function &lt;/span&gt;(tags, topics, hot_topics, stars, tops, no_reply_topics, pages){
        response.render(&lt;span style="color: maroon"&gt;'index'&lt;/span&gt;, {...});
    };

    &lt;span style="color: #006400"&gt;// 注册最终的回调函数&lt;/span&gt;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;proxy = &lt;span style="color: blue"&gt;new &lt;/span&gt;EventProxy();
    proxy.assign(&lt;span style="color: maroon"&gt;'tags'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'topics'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'hot_topics'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'stars'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'tops'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'no_reply_topics'&lt;/span&gt;, &lt;span style="color: maroon"&gt;'pages'&lt;/span&gt;, render);
    
    tag_ctrl.get_all_tags(&lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tags){
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        proxy.trigger(&lt;span style="color: maroon"&gt;'tags'&lt;/span&gt;, tags);
    });

    topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, topics) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        proxy.trigger(&lt;span style="color: maroon"&gt;'topics'&lt;/span&gt;, topics);
    });
    
    topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, hot_topics) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        proxy.trigger(&lt;span style="color: maroon"&gt;'hot_topics'&lt;/span&gt;, hot_topics);
    });
    
    user_ctrl.get_users_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, users) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        proxy.trigger(&lt;span style="color: maroon"&gt;'stars'&lt;/span&gt;, users);
    });
    
    user_ctrl.get_users_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tops) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        proxy.trigger(&lt;span style="color: maroon"&gt;'tops'&lt;/span&gt;, tops);
    });
    
    topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, no_reply_topics) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        proxy.trigger(&lt;span style="color: maroon"&gt;'no_reply_topics'&lt;/span&gt;, no_reply_topics);
    });
    
    topic_ctrl.get_count_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, all_topics_count) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        &lt;span style="color: blue"&gt;var &lt;/span&gt;pages = Math.ceil(all_topics_count / limit);
        proxy.trigger(&lt;span style="color: maroon"&gt;'pages'&lt;/span&gt;, pages);
    });
};&lt;/pre&gt;

&lt;p&gt;这种模式可以简单概括为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;准备一个最终的回调方法，在获取所有结果后执行，并使用assign方法注册给EventProxy对象。 &lt;/li&gt;

  &lt;li&gt;发起各异步操作，并将结果使用trigger方法提交至EventProxy。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当得到所有结果后，EventProxy自然会执行最终的回调方法，即完成最终的任务。&lt;/p&gt;

&lt;h1&gt;实现分析&lt;/h1&gt;

&lt;p&gt;从表面上看来，似乎EventProxy避免了回调函数的层层嵌套，但它的做法只是将每个异步调用分离出来（当然，的确会清晰一些）。如果您把现在的代码与之前“传统”代码相比，会发现两者的差距似乎只是——Tab的数量，或者说是缩进数量。真实的工作，例如每步操作的错误处理代码，还必须完全保留。简单地说，使用EventProxy其实并没有节省什么工作。&lt;/p&gt;

&lt;p&gt;而且，我们完全无需使用EventProxy，也可以轻松写出类似的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(request, response, next) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;data = { };
    &lt;span style="color: blue"&gt;var &lt;/span&gt;steps = [&lt;span style="color: maroon"&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;, &lt;span style="color: maroon"&gt;&amp;quot;topics&amp;quot;&lt;/span&gt;, ...];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;done = &lt;span style="color: blue"&gt;function &lt;/span&gt;(name, value) {
        data[name] = value;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(steps.remove(name).length &amp;gt; 0) &lt;span style="color: blue"&gt;return&lt;/span&gt;;

        response.render(&lt;span style="color: maroon"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;, {...});
    }

    tag_ctrl.get_all_tags(&lt;span style="color: blue"&gt;function &lt;/span&gt;(err, tags) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        done(&lt;span style="color: maroon"&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;, tags);
    });

    topic_ctrl.get_topics_by_query(..., &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, topics) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        done(&lt;span style="color: maroon"&gt;&amp;quot;topics&amp;quot;&lt;/span&gt;, topics);
    });

    ...
};&lt;/pre&gt;

&lt;p&gt;我们只要使用一个steps数组来准备所有的“数据”，每次完成后剔除一个，直到全部剔除为止即可。这么做还有个好处便是无需关注顺序，在使用EventProxy的时候，我们必须将注册时的使用的名称，和回调函数的参数保持顺序一致。试想，如果要增加一个步骤或是改变一些顺序，我们则必须加倍小心了。所以在我看来，在这里使用EventProxy并没有带来太多的益处，从简化编程的角度来说，效果十分有限。&lt;/p&gt;

&lt;p&gt;要简化编程体验，还是得看Jscex的，下次我们便来改造Node Club的首页。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/02/jscexify-nodeclub-1-prepare-nodeclub-website.html"&gt;使用Jscex改进Node Club（1）：运行Node Club网站&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/02/jscexify-nodeclub-2-import-jscex.html"&gt;使用Jscex改进Node Club（2）：引入Jscex类库&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;使用Jscex改进Node Club（3）：分析首页实现&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-3-home-page-implementation.html#comments</comments>
      <pubDate>Wed, 29 Feb 2012 15:44:54 GMT</pubDate>
      <lastBuildDate>Sat, 03 Mar 2012 08:57:04 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>使用Jscex改进Node Club（2）：引入Jscex类库</title>
      <link>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-2-import-jscex.html</link>
      <guid>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-2-import-jscex.html</guid>
      <description>&lt;p&gt;之前我们已经将Node Club在本地运行起来了，接着我们便来引入&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;类库，为常用异步方法扩展出Jscex版本，并试着编写一些最简单的Jscex代码。&lt;/p&gt;

&lt;h1&gt;安装Jscex包&lt;/h1&gt;

&lt;p&gt;为项目中安装Jscex十分容易，因为Jscex已经发布在官方NPM源中，我们只需&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/81e04cea4c279efe275bb74660dfe41954a714e0"&gt;修改package.json文件&lt;/a&gt;即可：&lt;/p&gt;

&lt;pre class="code"&gt;{
    &lt;span style="color: maroon"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;NodeClub&amp;quot;
  &lt;/span&gt;, &lt;span style="color: maroon"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.0.1&amp;quot;
  &lt;/span&gt;, &lt;span style="color: maroon"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;./app.js&amp;quot;
  &lt;/span&gt;, &lt;span style="color: maroon"&gt;&amp;quot;private&amp;quot;&lt;/span&gt;: &lt;span style="color: blue"&gt;true
  &lt;/span&gt;, &lt;span style="color: maroon"&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;: {
      &lt;span style="color: maroon"&gt;&amp;quot;express&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;2.5.1&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;ejs&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.5.0&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;eventproxy&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;mongoose&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;2.4.1&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;node-markdown&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;validator&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.3.7&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;nodemailer&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.3.1&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;jscex&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.6.x&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;jscex-jit&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.6.x&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;jscex-async&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.6.x&amp;quot;&lt;/span&gt;,
      &lt;span style="color: maroon"&gt;&amp;quot;jscex-async-powerpack&amp;quot;&lt;/span&gt;: &lt;span style="color: maroon"&gt;&amp;quot;0.6.x&amp;quot;
  &lt;/span&gt;}
}&lt;/pre&gt;

&lt;p&gt;在此我们引入四个和Jscex相关的包，并指定为0.6.x版本，以便与NPM上的Jscex同步更新。修改之后，便可以使用npm install安装新增的Jscex包：&lt;/p&gt;

&lt;pre class="code"&gt;$ &lt;strong&gt;npm install&lt;/strong&gt;
jscex-async@0.6.0 ./node_modules/jscex-async 
jscex-async-powerpack@0.6.0 ./node_modules/jscex-async-powerpack 
jscex@0.6.0 ./node_modules/jscex 
jscex-jit@0.6.0 ./node_modules/jscex-jit&lt;/pre&gt;

&lt;p&gt;有了NPM之后，每次为项目添加依赖库也只需编辑package.json再npm install就行了。如果需要将本地版本与NPM源保持同步更新，也只需一句npm update命令。&lt;/p&gt;

&lt;h1&gt;Node Club中的异步方法&lt;/h1&gt;

&lt;p&gt;在JavaScript有各种各样的异步方法，要配合Jscex使用的话，则必须使用能与Jscex适配的异步方法，简称Jscex异步方法。在Node Club项目中出现最多的异步方法便是使用&lt;a href="https://github.com/LearnBoost/mongoose"&gt;mongoose&lt;/a&gt;访问MongoDB。mongoose不仅仅提供了MongoDB的访问能力，它还提供相当的ORM功能，可以让我们快速的定义模型，并与MongoDB数据库里的集合映射起来，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;mongoose = require(&lt;span style="color: maroon"&gt;&amp;quot;mongoose&amp;quot;&lt;/span&gt;),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

&lt;span style="color: blue"&gt;var &lt;/span&gt;UserSchema = &lt;span style="color: blue"&gt;new &lt;/span&gt;Schema({
    name: String,
    password: String,
    createAt: Date
});

&lt;span style="color: blue"&gt;var &lt;/span&gt;User = mongoose.model(&lt;span style="color: maroon"&gt;'User'&lt;/span&gt;, UserSchema);&lt;/pre&gt;

&lt;p&gt;此时User类型便已经和数据库建立了映射关系，例如我们可以操作数据：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;updatePassword(id, password, cb) {
    User.findOne({ _id: id }, &lt;span style="color: blue"&gt;function &lt;/span&gt;(error, user) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(error) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(error);

        user.password = password;
        user.save(&lt;span style="color: blue"&gt;function &lt;/span&gt;(error) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(error) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(error);

            cb(&lt;span style="color: blue"&gt;null&lt;/span&gt;);
        });
    });
}&lt;/pre&gt;

&lt;p&gt;以上代码的作用是更新一个用户的密码，我们先使用User.findOne获得用户对象，修改后再使用save方法存回数据库。这里用到的都是一种“标准”的异步方法模式，即使用回调函数获得（或返回）结果，其第一个参数为错误对象，如果不为空，则表示出错了。因此每次调用一个异步方法的时候，我们都需要在回调方法里判断是否出错，这也是异步编程的麻烦之一。&lt;/p&gt;

&lt;h1&gt;加载Jscex类库&lt;/h1&gt;

&lt;p&gt;如果要在项目里加载Jscex，我会建议使用一个简单的模块来存放与Jscex初始化相关的代码，&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/dc1d5d7fe5305d9cd0428e767745d500d5a70f3d"&gt;例如libs/jscex.js&lt;/a&gt;：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscex = require(&lt;span style="color: maroon"&gt;&amp;quot;jscex&amp;quot;&lt;/span&gt;);
require(&lt;span style="color: maroon"&gt;&amp;quot;jscex-jit&amp;quot;&lt;/span&gt;).init(Jscex);
require(&lt;span style="color: maroon"&gt;&amp;quot;jscex-async&amp;quot;&lt;/span&gt;).init(Jscex);
require(&lt;span style="color: maroon"&gt;&amp;quot;jscex-async-powerpack&amp;quot;&lt;/span&gt;).init(Jscex);

&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscexify = Jscex.Async.Jscexify;

&lt;span style="color: blue"&gt;var &lt;/span&gt;mongoose = require(&lt;span style="color: maroon"&gt;&amp;quot;mongoose&amp;quot;&lt;/span&gt;);

&lt;span style="color: blue"&gt;var &lt;/span&gt;mp = mongoose.Model.prototype;
mp.saveAsync = Jscexify.fromStandard(mp.save);
mp.removeAsync = Jscexify.fromStandard(mp.remove);

&lt;span style="color: blue"&gt;var &lt;/span&gt;m = mongoose.Model;
m.findByIdAsync = Jscexify.fromStandard(m.findById);
m.findOneAsync = Jscexify.fromStandard(m.findOne);
m.findAsync = Jscexify.fromStandard(m.find);
m.countAsync = Jscexify.fromStandard(m.count);

exports.Jscex = Jscex;&lt;/pre&gt;

&lt;p&gt;前几行代码是标准的Jscex引入方式。而从mongoose相关的代码开始，便是在为其扩展Jscex异步方法。mongoose对扩展十分友好，它把内部的元数据暴露出来，让我们进行统一扩展。例如为Model扩展一些静态或是实例方法，便相当于为所有的模型及其它们的实例添加了方法。这种做法充分利用了JavaScript的灵活性，假如它把所有的元数据都隐藏起来，那我们没法如此简单直接地引入Jscex扩展了——当然总是有办法的，只是麻烦一些。&lt;/p&gt;

&lt;p&gt;这里我强烈建议每个JavaScript类库或框架的开发者都能实现这种方式，给使用者充分的扩展途径。以Jscex自身为例，它的每个异步任务对象都是Jscex.Async.Task类型的实例，这样jscex-async-powerpack模块便可以轻易补充各种强大的辅助方法。&lt;/p&gt;

&lt;p&gt;此外，由于mongoose遵守异步方法“标准模式”（即使用回调函数传回结果，其第一个参数为错误对象），因此只要一个fromStandard辅助函数便可全部应对。在以后的代码中，我们会大量使用这些扩展后的Jscex异步方法。&lt;/p&gt;

&lt;h1&gt;编写简单的Jscex异步函数&lt;/h1&gt;

&lt;p&gt;现在我们就直接写几行Jscex代码吧。例如在controllers/user.js文件里定义了以下几个方法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;get_user_by_id(id, cb) {
    User.findOne({ _id: id }, &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, user) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, user);
    });
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;get_user_by_name(name, cb) {
    User.findOne({ name: name }, &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, user) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, user);
    });
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;get_user_by_loginname(name, cb) {
    User.findOne({ loginname: name }, &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, user) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, user);
    });
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;get_users_by_ids(ids, cb) {
    User.find({ &lt;span style="color: maroon"&gt;'_id'&lt;/span&gt;: { &lt;span style="color: maroon"&gt;'$in'&lt;/span&gt;: ids } }, &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, users) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, users);
    });
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;get_users_by_query(query, opt, cb) {
    User.find(query, [], opt, &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, users) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;cb(err, users);
    });
}&lt;/pre&gt;

&lt;p&gt;以上几个方法都有相同的特征：它们都是简单的调用一个mongoose的异步方法，判断是否出错，并返回结果。正如之前提过的那样，编写异步代码的麻烦之一，便是在每次回调时都要判断是否出错，因此在实际项目中的异步代码都会比各种“演示”用的玩具代码更麻烦一些。不过我们可以&lt;a href="https://github.com/JeffreyZhao/nodeclub/commit/9dcbb432bc2909e7e098a8eee3eba9981ca9da4c"&gt;使用Jscex来改写这些代码&lt;/a&gt;：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscex = require(&lt;span style="color: maroon"&gt;&amp;quot;../libs/jscex&amp;quot;&lt;/span&gt;).Jscex;

&lt;span style="color: blue"&gt;var &lt;/span&gt;get_user_by_id_async = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(id) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$await(User.findOneAsync({ _id: id }));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;get_user_by_name_async = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(name) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$await(User.findOneAsync({ name: name }));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;get_user_by_loginname_async = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(name) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$await(User.findOneAsync({ loginname: name }));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;get_users_by_ids_async = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(ids) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$await(User.findAsync({ &lt;span style="color: maroon"&gt;'_id'&lt;/span&gt;: { &lt;span style="color: maroon"&gt;'$in'&lt;/span&gt;: ids } }));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;get_users_by_query_async = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(query, opt) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$await(User.findAsync(query, [], opt));
}));&lt;/pre&gt;

&lt;p&gt;在编写Jscex方法中，我们无需操作回调函数，只要在异步点上使用$await进行“等待”即可。我们也无需显式地处理错误，因为一旦出现错误便会抛出异常，异常如果没有被某个try…catch捕获到，则会顺着调用路径一路向上传递，直到被我们的代码或是系统捕获为止。Jscex将简单易用的传统编程模式与实践重新带回异步编程中，做到“同步编写，异步执行”的效果。这就是Jscex诞生的意义。&lt;/p&gt;

&lt;p&gt;从下一篇文章开始，我们将逐步改造Node Club网站中现有的代码。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2012/02/jscexify-nodeclub-1-prepare-nodeclub-website.html"&gt;使用Jscex改进Node Club（1）：运行Node Club网站&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;使用Jscex改进Node Club（2）：引入Jscex类库&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-2-import-jscex.html#comments</comments>
      <pubDate>Mon, 20 Feb 2012 13:57:45 GMT</pubDate>
      <lastBuildDate>Mon, 20 Feb 2012 13:57:45 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>使用Jscex改进Node Club（1）：运行Node Club网站</title>
      <link>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-1-prepare-nodeclub-website.html</link>
      <guid>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-1-prepare-nodeclub-website.html</guid>
      <description>&lt;p&gt;一直想做个相对完整的项目来演示下&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;的使用，可惜缺少创意和精力，一直没能实现。前几天看到&lt;a href="http://club.cnodejs.org/"&gt;Node Club&lt;/a&gt;将其网站开源了，不禁让我十分欢喜。Node Club网站是个真实案例，复杂度适中，既是Jscex的典型使用场景，又能避开我不擅长的网页样式设计和制作，简直是一个再合适不过的基础样板了。周末我大致看了下代码，也试着将几个部分使用Jscex改进了一下，效果也十分显著，于是打算写作一个系列指引，希望可以对Jscex类库的推广有所帮助。在此第一篇，自然是最基本的环境建设开始说起。&lt;/p&gt;

&lt;h1&gt;配置系统环境&lt;/h1&gt;

&lt;p&gt;首先，我强烈建议您可以在GitHub上Fork一下&lt;a href="https://github.com/muyuan/nodeclub"&gt;Node Club网站的项目&lt;/a&gt;，有个版本管理环境做后盾，做什么都会放心许多。我也将所有修改存放在GitHub上，其&lt;a href="https://github.com/JeffreyZhao/nodeclub"&gt;master分支&lt;/a&gt;是我个人不断改进的版本，您可以时刻关注其最新发展。除此之外，我还创建了一个&lt;a href="https://github.com/JeffreyZhao/nodeclub/tree/tutorial"&gt;tutorial分支&lt;/a&gt;，专门为这个系列文章存放代码，保持两者进度一致，同时尽量将修改过程和版本提交对应起来。&lt;/p&gt;

&lt;p&gt;要运行Node Code，首先您得安装必要的环境：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://npmjs.org/"&gt;Node Package Manager&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://mongodb.org/"&gt;MongoDB&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;无论您使用的是Windows，Linux还是OS X，以上都有对应的安装方式，在此就不详细描述了。文章中会出现一些控制台脚本，是我在OS X上运行的命令和结果。如果没有特殊说明，则路径是nodeclub项目的根目录。如果你使用的是Windows，则需要进行一些修改，其实最常见的修改就是分割路径的斜线还有可执行文件的使用。&lt;/p&gt;

&lt;p&gt;这里假设您对以上三个工具，最好还包括Git有了一定了解。其实目前也无需十分熟悉，看看官方文档，搜几篇文章瞅瞅，就应该差不多了。&lt;/p&gt;

&lt;h1&gt;配置Node Club环境&lt;/h1&gt;

&lt;p&gt;假设您已经下载了Node Club代码（当然最好您是git clone一份自己fork出来项目），并将其解压缩至nodeclub目录中。现在您应该可以使用npm install命令安装所有依赖的包：&lt;/p&gt;

&lt;pre class="code"&gt;$ &lt;strong&gt;npm install&lt;/strong&gt;
eventproxy@0.1.0 ./node_modules/eventproxy 
ejs@0.5.0 ./node_modules/ejs 
validator@0.3.7 ./node_modules/validator 
node-markdown@0.1.0 ./node_modules/node-markdown 
mongoose@2.4.1 ./node_modules/mongoose 
├── colors@0.5.1
├── hooks@0.1.9
└── mongodb@0.9.7-1.4
express@2.5.1 ./node_modules/express 
├── mime@1.2.5
├── qs@0.4.2
├── mkdirp@0.0.7
└── connect@1.8.5
nodemailer@0.3.1 ./node_modules/nodemailer 
├── mailcomposer@0.1.4 (mimelib-noiconv@0.1.6)
└── simplesmtp@0.1.12&lt;/pre&gt;

&lt;p&gt;要运行网站，则还需要准备一份配置文件。您可以将config.default.js复制一份至config.js文件，并建议修改一下一些配置：&lt;/p&gt;

&lt;pre class="code"&gt;exports.config = {
    &lt;span style="color: #006400"&gt;// 网站端口号，默认为80，可能会有冲突，建议改成其他值
    &lt;/span&gt;port: 8080,

    &lt;span style="color: #006400"&gt;// 发系统邮件时使用的用户名
    &lt;/span&gt;mail_user: &lt;span style="color: maroon"&gt;'xxxxx@gmail.com'&lt;/span&gt;,
    &lt;span style="color: #006400"&gt;// 发系统邮件时使用的密码
    &lt;/span&gt;mail_pass: &lt;span style="color: maroon"&gt;'xxxxx'&lt;/span&gt;,
    &lt;span style="color: #006400"&gt;// SMTP服务器地址
    &lt;/span&gt;mail_host: &lt;span style="color: maroon"&gt;'smtp.gmail.com'&lt;/span&gt;,
    &lt;span style="color: #006400"&gt;// 系统邮件发信人
    &lt;/span&gt;mail_sender: &lt;span style="color: maroon"&gt;'xxxxx@gmail.com'&lt;/span&gt;,
    &lt;span style="color: #006400"&gt;// 根据需求配置是否验证
    &lt;/span&gt;mail_use_authentication: &lt;span style="color: blue"&gt;true&lt;/span&gt;,
};&lt;/pre&gt;

&lt;p&gt;其中大部分的设置是在配置系统邮件的SMTP服务器，您可以像我一样使用Gmail，或是跟Node Club原项目一样使用126的邮箱。&lt;/p&gt;

&lt;h1&gt;运行Node Club网站&lt;/h1&gt;

&lt;p&gt;要运行Node Club网站，则需要启动MongoDB数据库，例如：&lt;/p&gt;

&lt;pre class="code"&gt;mongodb-osx-x86_64-2.0.2/bin$ &lt;strong&gt;mkdir data&lt;/strong&gt;
mongodb-osx-x86_64-2.0.2/bin$ &lt;strong&gt;./mongodb --dbpath data&lt;/strong&gt;&lt;/pre&gt;

&lt;p&gt;此时您就在本地启动了一个MongoDB进程，使用默认端口27017，这与网站的默认配置相符。此时您就可以执行app.js来启动网站：&lt;/p&gt;

&lt;pre class="code"&gt;$ &lt;strong&gt;node app.js&lt;/strong&gt;
NodeClub listening on port 8080 in development mode
God bless love....&lt;/pre&gt;

&lt;p&gt;在浏览器里访问http://127.0.0.1:8080，您应该就能看到一个空白的Node Club站点：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/jscexify-nodeclub/1.png"&gt;&lt;img alt="Node Club空白首页" src="http://img.zhaojie.me/blog/jscexify-nodeclub/1.png" width="450" /&gt;&lt;/a&gt; 

&lt;p&gt;此时您可以点击右上角的“注册”链接，注册一个名为admin的用户，部分操作（例如标签管理）需要使用该账号才能进行。注册时会要求填一个邮箱，提交后会收到一封激活邮件，但其中的链接可能不能直接访问（遗漏了端口号，应该是个bug），您可以将其地址复制到浏览器里修改并访问。激活成功后便可登录，登陆后会进入空白的后台页面：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/jscexify-nodeclub/2.png"&gt;&lt;img alt="Node Club空白后台" src="http://img.zhaojie.me/blog/jscexify-nodeclub/2.png" width="450" /&gt;&lt;/a&gt; 

&lt;p&gt;至此万事俱备，任何时候您想重启Node Club网站，只需ctrl+c中断node app.js命令再重新运行即可。从下一篇文章开始，我们将正式开始Jscex之旅。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;使用Jscex改进Node Club（1）：运行Node Club网站&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2012/02/jscexify-nodeclub-1-prepare-nodeclub-website.html#comments</comments>
      <pubDate>Mon, 20 Feb 2012 06:24:33 GMT</pubDate>
      <lastBuildDate>Mon, 20 Feb 2012 12:49:15 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>Node.js中相同模块是否会被加载多次？</title>
      <link>http://blog.zhaojie.me/2011/12/same-node-module-load-multiple-times.html</link>
      <guid>http://blog.zhaojie.me/2011/12/same-node-module-load-multiple-times.html</guid>
      <description>&lt;p&gt;JavaScript的包管理一直是个软肋，我很难想象，连这一基础功能都没有做好的语言，现在居然会如此流行。在我看来，其实JavaScript流行的最主要元素还是把持了浏览器，而Web应用在这几年掀起了一阵腥风血雨。任意一门语言，只要能像JavaScript般被标准采纳，被所有浏览器接受，它都能“成功”，真是所谓宿命。&lt;/p&gt;

&lt;p&gt;当然，既然它流行了，既然人们想要用它做大事了，就要开始为它制定一些模块的约定。这几天我为&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;实现&lt;a href="https://github.com/amdjs/amdjs-api/wiki/AMD"&gt;AMD规范&lt;/a&gt;的时候，便深刻体会到模块化的优势。Node.js也使用了&lt;a href="http://wiki.commonjs.org/"&gt;CommonJS&lt;/a&gt;模块机制，最近&lt;a href="http://www.infoq.com/cn/articles/nodejs-module-mechanism"&gt;在InfoQ上有一篇文章讨论了这方面的问题&lt;/a&gt;。这篇文章提到Node.js在载入模块时，如果之前该模块已经加载过则不会有重复开销，因为模块加载有缓存机制。这篇文章是我初审的，当时也正好在思考Jscex在Node.js使用时的模块化问题，但研究了它的规则之后，我发现在某些情况下还是可能加载多次。现在我们便来分析这个问题。&lt;/p&gt;

&lt;p&gt;当我们使用require方法加载另一模块的时候，Node.js会去查询一系列的目录。我们可以从module.paths中得到这些路径，例如：&lt;/p&gt;

&lt;pre class="code"&gt;[ &lt;span style="color: maroon"&gt;'/Users/jeffz/Projects/node-test/node_modules'&lt;/span&gt;,
  &lt;span style="color: maroon"&gt;'/Users/jeffz/Projects/node_modules'&lt;/span&gt;,
  &lt;span style="color: maroon"&gt;'/Users/jeffz/node_modules'&lt;/span&gt;,
  &lt;span style="color: maroon"&gt;'/Users/node_modules'&lt;/span&gt;,
  &lt;span style="color: maroon"&gt;'/node_modules'&lt;/span&gt;]&lt;/pre&gt;

&lt;p&gt;这里是我在运行/User/jeffz/Projects/node-test目录下一个模块时得到的结果。可见，Node.js会从当前模块所在目录的node_modules（这里怎么不遵守Unix习惯，而使用了下划线呢？）开始找起，如果没找到再会去找上级目录的node_modules，直到根目录为止。当然，实际情况下还会有NODE_PATH环境变量标识的目录等等。当模块的位置确定之后，Node.js便会查看这个位置的模块是否已经被加载，如果已加载，则直接返回。&lt;/p&gt;

&lt;p&gt;简单地说，Node.js是根据模块所在路径来缓存模块的。&lt;/p&gt;

&lt;p&gt;这么看来，“相同模块是否会被加载多次”这个问题，其实就演变成了“相同模块是否会出现在不同路径里”。简单想来这似乎不太可能，因为如果我们要使用某个模块的时候，它的位置总是确定的。例如，使用npm安装的模块，总是会出现在当前目录的node_modules里，加载时总是会找到相同的路径。那么，在“间接”依赖相同模块的情况下呢？&lt;/p&gt;

&lt;p&gt;例如我们想要使用Express框架，于是使用npm来安装，便会得到：&lt;/p&gt;

&lt;pre class="code"&gt;$ npm install express
express@2.5.2 ./node_modules/express 
├── mkdirp@0.0.7
├── qs@0.4.0
├── mime@1.2.4
└── connect@1.8.5&lt;/pre&gt;

&lt;p&gt;可见，Express依赖了其他一些模块，它们都存放在express模块自己的目录里面，例如./node_modules/express/node_modules/mime。好，假如我们项目自身也要使用mime项目，我们自然也可以使用npm来安装：&lt;/p&gt;

&lt;pre class="code"&gt;$ npm install mime
mime@1.2.4 ./node_modules/mime &lt;/pre&gt;

&lt;p&gt;于是我们最终得到的是这样的结构：&lt;/p&gt;

&lt;pre class="code"&gt;./node_modules
├── &lt;span style="color: red"&gt;&lt;strong&gt;mime&lt;/strong&gt;&lt;/span&gt;
└── express
    └── node_modules
        ├── mkdirp
        ├── qs
        ├── &lt;span style="color: red"&gt;&lt;strong&gt;mime&lt;/strong&gt;&lt;/span&gt;
        └── connect&lt;/pre&gt;

&lt;p&gt;请注意，这里的mime模块便出现在两个位置上，它们名称版本都一致，完全是一个模块。那么试想，如果我们在自己的代码里加载的mime模块，以及express内部加载的mime模块是同一个吗？显然不是，可见，在这里相同的模块被重复加载了两次，产生了两个模块“实例”。&lt;/p&gt;

&lt;p&gt;这种重复加载在一般情况下不会有太大问题，最多内存占用大一点而已，不会影响程序的正确性。但是，我们也可以轻易设想到一些意外的情况。例如，在Jscex中，每个Task对象我都会给定一个ID，不断增长。要实现这点我们需要维护一个“种子”，全局唯一。之前这个种子定义在闭包内部，但由于Jscex模块会被加载多次，这样从不同模块“实例”生成的Task对象，它们的ID便有可能重复。当然，解决这个问题也并不困难，只需要将种子定义在根对象上即可，不同的模块“实例”共享相同的根对象。&lt;/p&gt;

&lt;p&gt;还有个问题可能就显得隐蔽些了，我们可以通过一个简单的实验来观察结果。我们先来定义一个jeffz-a模块，其中暴露出一个MyType类型：&lt;/p&gt;

&lt;pre class="code"&gt;module.exports.MyType = &lt;span style="color: blue"&gt;function &lt;/span&gt;() { }&lt;/pre&gt;

&lt;p&gt;然后将其发布到npm上。然后再写一个jeffz-b模块，依赖jeffz-a，并将jeffz-a中定义的MyType类型直接暴露出去：&lt;/p&gt;

&lt;pre class="code"&gt;module.exports.MyType = require(&lt;span style="color: maroon"&gt;&amp;quot;jeffz-a&amp;quot;&lt;/span&gt;).MyType;&lt;/pre&gt;

&lt;p&gt;接着将jeffz-b也发布置npm上。再重新写一个测试模块，使用npm安装jeffz-a和jeffz-b，最终目录会是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;./node_modules
├── jeffz-a
└── jeffz-b
    └── node_modules
        └── jeffz-a&lt;/pre&gt;

&lt;p&gt;在测试模块内，我们来测试实例与类型之间的关系：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;a = require(&lt;span style="color: maroon"&gt;&amp;quot;jeffz-a&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;b = require(&lt;span style="color: maroon"&gt;&amp;quot;jeffz-b&amp;quot;&lt;/span&gt;);

console.log(&lt;span style="color: blue"&gt;new &lt;/span&gt;a.MyType() &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;a.MyType); &lt;span style="color: #006400"&gt;// true&lt;/span&gt;
console.log(&lt;span style="color: blue"&gt;new &lt;/span&gt;b.MyType() &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;b.MyType); &lt;span style="color: #006400"&gt;// true&lt;/span&gt;

console.log(&lt;span style="color: blue"&gt;new &lt;/span&gt;a.MyType() &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;b.MyType); &lt;span style="color: #006400"&gt;// false&lt;/span&gt;
console.log(&lt;span style="color: blue"&gt;new &lt;/span&gt;b.MyType() &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;a.MyType); &lt;span style="color: #006400"&gt;// false&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;从表面上看，jeffz-b和jeffz-a暴露出的应该是相同的MyType类型，它们的对象通过instanceof相互判断应该都返回true，但实际上由于jeffz-b中的jeffz-a，与我们直接加载的jeffz-a模块是不同的实例，因此MyType类型自然也不是同一个了。&lt;/p&gt;

&lt;p&gt;这对于Jscex的影响在于，Jscex的异步模块在取消时，原本是通过判断异常对象是否为CanceledError类型来决定Task的状态为cancelled还是faulted。但由于Node.js可能会将相同的模块加载为多个实例，因此即便抛出的的确是某个实例的CancelledError，也无法通过另一个实例内部的判断。因此，目前Jscex的判断方式修改为检查异常对象的isCancellation字段，简单地解决了这个问题。&lt;/p&gt;

&lt;p&gt;当然，Node.js这种“重复加载”的影响也并非完全是负面的，至少它天然的解决了多版本共存的问题。例如，express v2.5.2依赖mime v1.2.4，但我们程序自身又想使用mime v1.2.5。此时，express内部自然使用mime v1.2.4，而我们自己的程序使用的便是mime v1.2.5。&lt;/p&gt;

&lt;p&gt;有些情况下您可能也想避免这种重复加载，这就必须手动地删除模块内部被间接依赖的模块，将其移动到模块查询路径的公用部分上了。就目前看来，这些操作必须手动进行，因为npm在安装模块时不会关心依赖的模块是否已经安装过了（例如在NODE_PATH环境变量标识的路径里），它一定会重新下载所有依赖的模块。可惜如果您使用的是托管形式的Node.js服务，则很有可能无法做到这一点。&lt;/p&gt;

&lt;p&gt;因此，我们在编写Node.js模块的时候，便事先考虑下它会被重复加载时的情况吧。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/12/same-node-module-load-multiple-times.html#comments</comments>
      <pubDate>Tue, 27 Dec 2011 15:19:29 GMT</pubDate>
      <lastBuildDate>Wed, 28 Dec 2011 04:45:46 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>受禁锢的异步编程思维</title>
      <link>http://blog.zhaojie.me/2011/12/the-stuck-mind-of-asynchronous-programming.html</link>
      <guid>http://blog.zhaojie.me/2011/12/the-stuck-mind-of-asynchronous-programming.html</guid>
      <description>&lt;p&gt;最近一直在努力推广&lt;a href="http://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;，补充了很多中文文档和示例，因此博客上都已经有两篇文章有了“上”而没有“下”，即使最复杂的图示也已经绘制完毕。在推广Jscex的过程中，我发现有个比较明显的问题是，许多使用JavaScript的程序员已经习惯旧有的编程方式，甚至推崇一些据他们说很“漂亮”的模式。但在我看来，这其实跟许多GoF模式是在修补OO语言的不足有些类似，很多异步模式都只是因为JavaScript语言特性不足而设计出来的“权宜之计”。我们在传统JavaScript编程环境下并没有其他选择，单纯地认为这是“美”，说实话只不过是一种安慰罢了。&lt;/p&gt;

&lt;p&gt;Jscex的重头戏便是处理异步操作，但异步操作并不只是如Node.js中通过回调函数传回结果的那些方法，或者是网页上的AJAX请求等等。异步操作的定义其实可以概括成“&lt;span style="color:#ff0000"&gt;会在未来某个时刻完成的操作&lt;/span&gt;”，就只是这么简单。什么事情会在未来发生，那么它对你来说就是个异步操作。因此其实在日常开发过程中可谓到处是异步操作，例如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;播放动画（播放会在未来结束） &lt;/li&gt;

  &lt;li&gt;模态对话框关闭（模态对话框会在未来关闭） &lt;/li&gt;

  &lt;li&gt;用户操作（用户会在未来点击按钮） &lt;/li&gt;

  &lt;li&gt;各类事件（数据流会在未来关闭，WebWorker会在未来获得消息，图片会在未来加载成功） &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些示例实在数不胜数。但是，在许多JavaScript程序员眼中，似乎只有AJAX或是Node.js中的那些异步方法才算是异步操作。其他的东西，比如用户点击一个按钮，这难道不是个天然的“事件”吗？其实这就要视这个异步任务的性质如何了。如果它是一系列操作的“发起者”，那么的确，使用事件触发的方式来对待这次点击操作可能是最合理的。但如果，这个操作只是一系列过程中的一个步骤，那么如果依然把它视为一个事件型的操作，就只会破坏我们的逻辑了。&lt;/p&gt;

&lt;p&gt;举个例子，和Jscex的&lt;a href="https://github.com/JeffreyZhao/jscex/blob/master/README-cn.md"&gt;快速入门&lt;/a&gt;比较类似，即&lt;a href="http://en.wikipedia.org/wiki/Fibonacci_number"&gt;菲薄纳契（Fibonacci）数列&lt;/a&gt;：&lt;/p&gt;
&lt;img class="embed" title="F_n = F_{n-1} + F_{n - 2}" src="http://latex.codecogs.com/gif.latex?F_n = F_{n-1} + F_{n - 2}" /&gt; 

&lt;p&gt;其边界情况为：&lt;/p&gt;
&lt;img class="embed" title="F_0 = 0, F_1 = 1" src="http://latex.codecogs.com/gif.latex?F_0 = 0, F_1 = 1" /&gt; 

&lt;p&gt;以上是其标准定义，直接写成算法即是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;fib = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {

    console.log(0);
    console.log(1);

    &lt;span style="color: blue"&gt;var &lt;/span&gt;a = 0, current = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;b = a;
        a = current;
        current = a + b;

        console.log(current);
    }
};&lt;/pre&gt;

&lt;p&gt;上述代码将会无限地循环下去，不断输出数列的每一项。快速入门里的要求，是将其修改为“每隔一秒输出一个数字”，于是有同学就说：这不天生是计时器的场景吗？但事实并非如此。“计时器”或是setTimeout函数，都只是环境提供给我们的唯一可用的功能，我们要意识到这不是我们主动的“选择”。如果一看到“每隔一秒”这样的需求，JavaScript程序员就认为“计时器”是“最好”的办法，这就说明思维被禁锢了。我相信这样的功能交给其他任何平台的程序员，他们的第一感觉几乎都会是“使用Sleep函数暂停一秒”。这其实才是最简单的做法，直接，清晰，完整保留现有代码逻辑。&lt;/p&gt;

&lt;p&gt;这也是基于Jscex之后的实现方式。这里我再将要求修改一下，改为用户“每点击一次按钮”输出一个数字，又该怎么做？基于Jscex的做法如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Async = Jscex.Async;

&lt;span style="color: blue"&gt;var &lt;/span&gt;fibAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;button = document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;button&amp;quot;&lt;/span&gt;);

    $await(Async.onEvent(button, &lt;span style="color: maroon"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;)); &lt;span style="color: #006400"&gt;// 等待用户点击
    &lt;/span&gt;console.log(0);

    $await(Async.onEvent(button, &lt;span style="color: maroon"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;)); &lt;span style="color: #006400"&gt;// 等待用户点击
    &lt;/span&gt;console.log(1);

    &lt;span style="color: blue"&gt;var &lt;/span&gt;a = 0, current = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;b = a;
        a = current;
        current = a + b;

        $await(Async.onEvent(button, &lt;span style="color: maroon"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;)); &lt;span style="color: #006400"&gt;// 等待用户点击
        &lt;/span&gt;console.log(current);
    }
}));

fibAsync().start();&lt;/pre&gt;

&lt;p&gt;有朋友可能会问：用户点击按钮不是需要响应事件的嘛，这个事件到哪里去了？其实正像我所说的那样，把这里的“用户点击按钮”当作事件对待并非最合理的方式，因为它只是“整个过程”中的一个环节而已。在这里，我们其实只是要在输出数字之前“等待用户点击”即可，这个“输出”以及相关的“计算”操作，并非是由“按钮点击”所触发的逻辑，而是一个连续的统一过程中的一部分而已。&lt;/p&gt;

&lt;p&gt;您可以试试纯粹使用事件机制来实现这个功能，保证您需要重新实现这段斐波那契数列的逻辑。当然，菲薄纳契数列的逻辑很简单，重写下估计也不会花太大的功夫，但如果您需要改造&lt;a href="http://files.zhaojie.me/jscex/samples/async/hanoi.html"&gt;汉诺塔的动画效果&lt;/a&gt;呢？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;hanoiAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(n, from, to, mid) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        $await(hanoiAsync(n - 1, from, mid, to));
    }

    &lt;span style="color: #006400"&gt;// 等待按钮点击
    // var btnNext = document.getElementById(&amp;quot;btnNext&amp;quot;);
    // $await(Jscex.Async.onEvent(btnNext, &amp;quot;click&amp;quot;));

    &lt;/span&gt;$await(moveDishAsync(n, from, to));

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        $await(hanoiAsync(n - 1, mid, to, from));
    }
}));&lt;/pre&gt;

&lt;p&gt;以上代码是以动画形式表现汉诺塔的解题过程，但如果用户提出想要“每点一次按钮”才移动一个盘子，那其实我们只要将上面两行代码取消注释即可。如果忽然有一天，老板要求通过一个选项来决定是否“自动移动”，在Jscex里只要加一个if判断即可。您可以简单设想一下直接裸写这些代码会遇到什么样的景象，改造时会遇到哪些困难。&lt;/p&gt;

&lt;p&gt;我还为Jscex准备了一个示例，是关于“&lt;a href="https://github.com/JeffreyZhao/jscex/blob/master/doc/async/samples/modal-dialog-cn.md"&gt;模态对话框&lt;/a&gt;”配合相关异步操作的。由于是“模态对话框”，我们是要在对话框关闭之后才继续做某些事情。可惜在JavaScript中，如果您直接把一个界面元素展现为一个模态对话框，它是无法阻止后面的代码继续执行的，要阻止则只能使用confirm或alert方法。于是，我们只能把后续操作放到一个回调函数中去，并在模态对话框关闭之后才执行。但是您要知道，模态对话框只不过是整个过程中的一个步骤，理想状况下我们的完整功能不该被拆成多个部分，再使用所谓“美妙”的回调串联起来。&lt;/p&gt;

&lt;p&gt;这点在Jscex中还是那么简单，直接按最简单的逻辑来进行即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// 显示模态对话框&lt;/span&gt;
$await($(&lt;span style="color: maroon"&gt;&amp;quot;#dialog-confirm&amp;quot;&lt;/span&gt;).dialogAsync({ modal: &lt;span style="color: blue"&gt;true &lt;/span&gt;}));

&lt;span style="color: #006400"&gt;// 发起AJAX请求&lt;/span&gt;
&lt;span style="color:blue"&gt;var&lt;/span&gt; response = $await($.ajaxAsync({ url: &lt;span style="color: maroon"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;, dataType: &lt;span style="color: maroon"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; }));

&lt;span style="color: #006400"&gt;// 继续做事&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;而无需：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// 继续做事&lt;/span&gt;
$(&lt;span style="color: maroon"&gt;&amp;quot;#dialog-confirm&amp;quot;&lt;/span&gt;).dialog({
    modal: &lt;span style="color: blue"&gt;true&lt;/span&gt;,
    close: &lt;span style="color: blue"&gt;function&lt;/span&gt; () {
        &lt;span style="color: #006400"&gt;// 发起AJAX请求&lt;/span&gt;
        $.ajax({
            url: &lt;span style="color: maroon"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;,
            dataType: &lt;span style="color: maroon"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;,
            success: &lt;span style="color: blue"&gt;function&lt;/span&gt; () {
                &lt;span style="color: #006400"&gt;// 继续做事&lt;/span&gt;
            }
        });
});&lt;/pre&gt;

&lt;p&gt;经常会听到有些朋友谈起，说在实际开发过程中很少遇到异步场景。但在我看来，实在可谓遍地是异步，这种观念的差别只是在于是否经过了“抽象”。不加抽象地使用技术平台为我们提供的异步操作，会让我们的思维被它所禁锢。在JavaScript编程中浸淫太久了，可能就会忘记我们从最初是如何编程的。Jscex的目标，便是将这些东西回归自然，将逻辑以最自然的方式表达出来。循环？那就用for或是while吧，在函数之间跳来跳去是做什么的？&lt;/p&gt;

&lt;p&gt;我从来不担心的Jscex的实用价值。Jscex来自C#，F#以及Scala等现成的理念，各种开发模式都是被翻来覆去讨论过，总结过，验证过的。这些语言其实都能实现与JavaScript类似的编程模式，但它们不需要，因为语言特性让程序员可以使用更简单直接的做法来解决问题。Jscex只是将这些现成的内容，从其他模式带到JavaScript编程领域上而已。&lt;/p&gt;

&lt;p&gt;如今我唯一担心的，只是那些被禁锢的编程思维。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/12/the-stuck-mind-of-asynchronous-programming.html#comments</comments>
      <pubDate>Mon, 19 Dec 2011 13:49:37 GMT</pubDate>
      <lastBuildDate>Tue, 27 Dec 2011 15:17:21 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>尾递归对时间与空间复杂度的影响（上）</title>
      <link>http://blog.zhaojie.me/2011/11/does-tail-recursion-improve-time-and-space-complexities-1.html</link>
      <guid>http://blog.zhaojie.me/2011/11/does-tail-recursion-improve-time-and-space-complexities-1.html</guid>
      <description>&lt;p&gt;以前我也在博客上简单谈过“&lt;a href="http://blog.zhaojie.me/2009/03/tail-recursion-and-continuation.html"&gt;尾递归&lt;/a&gt;”及其&lt;a href="http://blog.zhaojie.me/2009/03/tail-recursion-explanation.html"&gt;优化方式&lt;/a&gt;方面的话题。前几天有同学在写邮件向我提问，说是否所有的递归算法都能改写为尾递归，改写成尾递归之后，是否在时间和空间复杂度方面都能有所提高？他以斐波那契数列为例，似乎的确是这样的情况。我当时的回答有些简单，后来细想之后似乎感觉有点问题，而在仔细操作之后发现事情并没有理论上那么简单，因此还是计划写篇文章来讨论下这方面的问题。&lt;/p&gt;

&lt;h1&gt;斐波那契数列&lt;/h1&gt;

&lt;p&gt;大家对于斐波那契数列（Fibonacci）的认识一定十分统一，唯一的区别可能仅在于n是从0开始还是从1开始算起。这里我们使用&lt;a href="http://en.wikipedia.org/wiki/Fibonacci_number"&gt;维基百科上的标准递归定义&lt;/a&gt;：&lt;/p&gt;
&lt;img class="embed" title="F_n = F_{n-1} + F_{n - 2}" src="http://latex.codecogs.com/gif.latex?F_n = F_{n-1} + F_{n - 2}" /&gt; 

&lt;p&gt;其边界情况为：&lt;/p&gt;
&lt;img class="embed" title="F_0 = 0, F_1 = 1" src="http://latex.codecogs.com/gif.latex?F_0 = 0, F_1 = 1" /&gt; 

&lt;p&gt;使用这个定义可以直接写出程序，这里我们用F#来表达：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;let rec &lt;/span&gt;fib n =
    &lt;span style="color: blue"&gt;if &lt;/span&gt;n &amp;lt; 2 &lt;span style="color: blue"&gt;then &lt;/span&gt;n
    &lt;span style="color: blue"&gt;else &lt;/span&gt;fib (n - 1) + fib (n - 2)&lt;/pre&gt;

&lt;p&gt;这个算法最容易理解，但其时间复杂度确是：&lt;/p&gt;
&lt;img class="embed" title="O((\frac{1 + \sqrt5}{2})^n) \approx O(1.618^n)" src="http://latex.codecogs.com/gif.latex?O((\frac{1 + \sqrt5}{2})^n) \approx O(1.618^n)" /&gt; 

&lt;p&gt;这种指数级的时间复杂度在实际应用中是十分可怕的（虽然这个数字是美妙的黄金分割）。因此，我们如果真要“计算”斐波那契数列第n项的值（即不使用“通项公式”），则往往会使用迭代的方式进行，写作尾递归则是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;let &lt;/span&gt;fibTail n = 
    &lt;span style="color: green"&gt;// 第i项的值v1，以及即将累加上去的值v2&lt;/span&gt;
    &lt;span style="color: blue"&gt;let rec &lt;/span&gt;fibTail' i v1 v2 =
        &lt;span style="color: blue"&gt;if &lt;/span&gt;i &amp;gt;= n &lt;span style="color: blue"&gt;then &lt;/span&gt;v1
        &lt;span style="color: blue"&gt;else &lt;/span&gt;fibTail' (i + 1) (v1 + v2) v1
    fibTail' 0 0 1&lt;/pre&gt;

&lt;p&gt;从代码上也可以轻易地判断出，这个算法的时间复杂度是O(n)，实际上它也会被F#或是Scala等支持尾递归的编译器优化为循环操作。这里我们使用命令式编程语言C#来表达编译后的结果：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static int &lt;/span&gt;FibTail(&lt;span style="color: blue"&gt;int &lt;/span&gt;n, &lt;span style="color: blue"&gt;int &lt;/span&gt;i, &lt;span style="color: blue"&gt;int &lt;/span&gt;v1, &lt;span style="color: blue"&gt;int &lt;/span&gt;v2)
{
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(i &amp;lt; n)
    {
        &lt;span style="color: blue"&gt;int &lt;/span&gt;temp = v1 + v2;
        v2 = v1;
        v1 = temp;
        i++;
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;v1;
}&lt;/pre&gt;

&lt;p&gt;时间复杂度从O(1.618&lt;sup&gt;n&lt;/sup&gt;)降低到O(n)，可谓是质的飞跃。&lt;/p&gt;

&lt;h1&gt;尾调用对空间复杂度的影响&lt;/h1&gt;

&lt;p&gt;那么，在空间复杂度方面，尾递归带来什么优化吗？我们首先还是来分析标准的递归算法：&lt;/p&gt;
&lt;img class="embed" title="F_n = F_{n-2} + F_{n - 1}" src="http://latex.codecogs.com/gif.latex?F_n = F_{n-2} + F_{n - 1}" /&gt; 

&lt;p&gt;假设，我们知道，在一个（无副作用的）方法执行完毕之后，除了返回值以外的空间会完全释放出来，因此在fib(n - 2)执行结束之后，它的空间占用是常数级的。且fib(n - 1)的空间占用一定大于fib(n - 2)，假设其fib(n)的空间占用为S(n)，可得：&lt;/p&gt;
&lt;img class="embed" title="S(n) = S(n - 1) + O(1)" src="http://latex.codecogs.com/gif.latex?S(n) = S(n - 1) + O(1)" /&gt; 

&lt;p&gt;于是fib的空间复杂度是显而易见的O(n)。这个空间复杂度其实并不大，例如经典的&lt;a href="http://en.wikipedia.org/wiki/Merge_sort"&gt;归并排序&lt;/a&gt;算法的空间复杂度也同样是O(n)。但不幸的是，这里的递归操作占用的完全是栈空间，而栈空间的大小是极其有限的（例如一个Windows应用程序默认情况下只有1M，ASP.NET甚至只有250K）。因此，只需一个稍大一点的数字会产生栈溢出。经试验，在我的机器上只需51K便能出现StackOverflowException：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// 50K不会出现StackOverflowException&lt;/span&gt;
51 * 1024 |&amp;gt; fib |&amp;gt; printfn &lt;span style="color: maroon"&gt;&amp;quot;%d&amp;quot;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;那么尾递归算法的空间复杂度呢？我们刚才提到，编译器会将尾递归优化成循环，那在实际运行时这个算法的空间复杂度自然是常数级，即O(1)。但这是我们实际观察到的编译器优化后的结果，从理论上说，我们并无法保证这里的尾递归会被优化成循环。因此我们不妨也从“字面”上来理解代码，看看理论上这样的尾递归调用会形成怎样的空间占用。&lt;/p&gt;

&lt;p&gt;对于尾递归来说，理论上我们只能期待它形成“尾调用”。也就是说，针对某个方法的调用（无论是否是递归操作）是父方法的最后一个操作。在这个情况下，我们无需保留父方法当前的栈空间，因此可以将其完全释放。于是，无论调用多少次，只要每次都将栈空间释放（或重用），其空间占用也始终是个常数，即O(1)。&lt;/p&gt;

&lt;p&gt;因此，无论从理论上（从字面上分析）还是实际上（观察编译结果）来说，似乎将斐波那契数列修改为尾递归，能显著地降低时间及空间复杂度，这也是那位同学提出“尾递归能改进时间和空间复杂度”的依据。那么我们重新回顾一下文章开头所提出的两个问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;每个递归算法都能改写为尾递归吗？ &lt;/li&gt;

  &lt;li&gt;改写为尾递归都能改进时间及空间复杂度吗？ &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;下次我们继续讨论这两个问题。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;尾递归对时间与空间复杂度的影响（上）&lt;/li&gt;

  &lt;li&gt;&lt;a href="#"&gt;尾递归对时间与空间复杂度的影响（下）&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2009/03/tail-recursion-and-continuation.html"&gt;尾递归与Continuation&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2009/03/tail-recursion-explanation.html"&gt;浅谈尾递归的优化方式&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2011/11/does-tail-recursion-improve-time-and-space-complexities-1.html#comments</comments>
      <pubDate>Tue, 15 Nov 2011 14:12:18 GMT</pubDate>
      <lastBuildDate>Wed, 16 Nov 2011 06:29:18 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>挖坟鞭尸：当年Sun公司的白皮书《About Microsoft “Delegates”》</title>
      <link>http://blog.zhaojie.me/2011/11/sun-whitepaper-about-microsoft-delegates.html</link>
      <guid>http://blog.zhaojie.me/2011/11/sun-whitepaper-about-microsoft-delegates.html</guid>
      <description>&lt;a href="http://img.zhaojie.me/blog/java-putong-wenyi-erbi.png"&gt;&lt;img alt="普通文艺二逼的Java" src="http://img.zhaojie.me/blog/java-putong-wenyi-erbi.png" width="200" class="floatRight" /&gt;&lt;/a&gt; 

&lt;p&gt;这是一桩当年的Sun公司与Java的旧事，还要追溯到C#还没出现，微软还在搞J++的时代。这篇著名的白皮书便是《&lt;a href="http://java.sun.com/docs/white/delegates.html"&gt;About Microsoft “Delegates”&lt;/a&gt;》，其中列出了Sun眼中的Delegate，或者说Bound Method Reference这一更广泛概念的多个缺点：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;它为语言带来了复杂度。但实际上，这里更多的是“编译器”或是语言“实现者”需要应付的复杂度。对语言的“使用者”来说，尽管C#或Scala比Java语言要复杂不少，但是用起来却更为省事，大大减少了代码编写和理解的复杂度。 &lt;/li&gt;

  &lt;li&gt;它让语言变得不够“面向对象”。在我看来这种为了“面向对象”而“面向对象”可谓本末倒置，“面向对象”是“手段”而不是“目标”。事实上Java近年来的发展，例如&lt;a href="http://download.oracle.com/javase/1,5.0/docs/guide/language/static-import.html"&gt;静态引入&lt;/a&gt;，&lt;a href="http://download.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html"&gt;MethodHandle&lt;/a&gt;，乃至社区不断诉求却一再延后的Lambda表达式，其实都在为了易用性而做出的妥协。 &lt;/li&gt;

  &lt;li&gt;它的表达能力不够。这点我始终不能理解，因为Bound Method Reference是运行时的概念，表达能力确是语言设计本身所影响的，一个运行时的概念为什么就没法用优美的语法形式表达出来？C# 2.0至3.0的飞跃，让它的表达能力早已远胜Java语言，而且这也与Delegate概念直接相关。 &lt;/li&gt;

  &lt;li&gt;它这不如适配器对象好用。Java语言的设计者们认为，用内部类/匿名类的语法来实现适配器对象虽然麻烦了一些，但也并没有带来什么问题。但是在使用者眼中，Java的语法噪音是出了名的多，直接影响Java语言的表达能力。如图，在C#中早就可以使用“文艺”的Scala方式来写程序，但如果想用相同的理念就只能变成十足的“二逼”了。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当然，白皮书还包含了更详细的讨论。最后，它信誓旦旦地写到：“Bound Method Reference并不是语言发展的正确道路”——至于现状如何就不多说了。我的感觉是：不谈Java语言设计者的水平如何，至少这篇白皮书的作者，在语言设计的能力或品味上几乎落后了Anders Hejlsberg十年。从我之前&lt;a href="http://www.infoq.com/cn/articles/neal-gafter-on-java"&gt;翻译过的一篇文章&lt;/a&gt;来看，Anders一直扮演着指引语言发展方向的作用，这也是为什么C#在这十年的发展能比Java要健康得多的主要原因之一。&lt;/p&gt;

&lt;p&gt;这篇文章的信息量并不大。原本我是在写另一篇文章，这些文字只是顺便提起的话题，但是一说却又感觉刹不住车。为了避免偏离话题，于是就把它单独成篇了——您可且看且过，倒是下一篇文章会更有趣一些。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/11/sun-whitepaper-about-microsoft-delegates.html#comments</comments>
      <pubDate>Tue, 08 Nov 2011 17:07:35 GMT</pubDate>
      <lastBuildDate>Wed, 09 Nov 2011 01:47:33 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>谈谈年度最佳代码“不管你们信不信，反正我信了”</title>
      <link>http://blog.zhaojie.me/2011/08/from-the-code-of-no-matter-you-believe-it-or-not.html</link>
      <guid>http://blog.zhaojie.me/2011/08/from-the-code-of-no-matter-you-believe-it-or-not.html</guid>
      <description>&lt;p&gt;最近有段十分流行的代码，是从江湖传闻“身怀八蛋”的铁道部发言人王勇平同志的一句名言：“不管你们信不信，反正我信了……这是生命的奇迹……它就是发生了”所引申出来的。这段代码虽然只是在调侃，但是围绕这段代码也产生了一些讨论（如代码风格，编程规范等等），在此顺手记录一下，就当无聊罢。&lt;/p&gt;

&lt;p&gt;这段代码是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;try
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(you.believe(it) == &lt;span style="color: blue"&gt;true &lt;/span&gt;|| you.believe(it) == &lt;span style="color: blue"&gt;false&lt;/span&gt;)
    {
        I.believe(it);
    }
}
&lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
{
    &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;It's a miracle!&amp;quot;&lt;/span&gt;);
}
&lt;span style="color: blue"&gt;finally
&lt;/span&gt;{
    it.justHappened();
}&lt;/pre&gt;

&lt;p&gt;代码与原文的对应关系不言自明，从命名风格上看，我们默认其为Java代码。话题主要是围绕在if条件的写法上。&lt;/p&gt;

&lt;h1&gt;书写风格&lt;/h1&gt;

&lt;p&gt;先来看看它的书写风格问题。我说这段代码不是老鸟写的，因为老鸟不会把一个布尔表达式跟true和false直接判断，而会写成：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(you.believe(it) || !you.believe(it))&lt;/pre&gt;

&lt;p&gt;于是有朋友提出，把布尔表达式跟true或false相比较来的更清晰一些，我表示这话并没有什么道理，因为这种读代码的方式是把视角停留在“数据”层面上：一个布尔表达式返回了布尔型的“数据”，于是把它和另外一个“数据”进行比较。如今的编程都在不断强调“语义”，“语义”的清晰才是真的清晰。我说Java是一门糟糕的语言，主要原因就是指它的表达能力太差，导致写出来的代码体现不出问题的解决方式，让人们把目光都集中在具体每条语句上了，所谓“见木不见林”。C#等现代语言都在强调“做什么”而不是“怎么做”，语义上就有很大提高了。&lt;/p&gt;

&lt;p&gt;回到目前这个具体问题上，if里面的语义是“you.believe(it)”的返回结果，而不是它的值与另外一个布尔常量的比较结果。其实这个观点我从初中搞信息学竞赛时就被老师不断强调，今天我同样咨询了同事，他也赞同我的观点。如果您还继续坚持这种写法不太清晰的话，我只能说“这只是不适应而已，要让自己适应这类写法”，很多人还觉得LINQ不清晰呢，小学生还觉得高中数学的解法不清晰呢。&lt;/p&gt;

&lt;p&gt;还有朋友认为，作为编码规范，应该要求这么写，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(10&lt;span style="color: blue"&gt; &lt;/span&gt;== i)&lt;/pre&gt;

&lt;p&gt;就是说，把常量写在比较操作的左边，并认为“这样更有普遍意义”。其实这也没有必要，这个习惯是从C语言时代遗传下来的“陋习”。在C语言里，如果把常量写在比较右侧，并且一不小心把“比较”操作符（两个等号）写成“赋值”操作符（一个等号），也可以编译通过，但是结果却大不相同，这给错误排查也会带来许多麻烦。但是，在如今的语言里已经比C语言做的安全多了，所以没必要制定这种规范。把一种语言的标准带入另一种语言不叫做“有普遍意义”，只是多余。&lt;/p&gt;

&lt;h1&gt;代码含义&lt;/h1&gt;

&lt;p&gt;然后要谈的便是代码与那句话的“映射”关系了，再来仔细读一下这个if子句：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(you.believe(it) || !you.believe(it))
{
    I.believe(it);
}&lt;/pre&gt;

&lt;p&gt;从“需求”上来理解，我认为代码应该保证if内部的代码一定会执行。那么现在这个需求肯定会满足吗？不一定，因为you.believe方法可能是有副作用的：如果它第一次调用返回false，而第二次调用时返回true，则if内部的代码就会整段略过，这显然不是铁道部王发言人的意图。因此，有同学提议代码应该是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;true &lt;/span&gt;|| you.believe(it))&lt;/pre&gt;

&lt;p&gt;这么做的确可以忽略you.believe(it)的结果，因为它已经被短路了根本不会执行。可能它也能满足需求，但我想更合理的做法可能应该是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(you.believe(it) || &lt;span style="color: blue"&gt;true&lt;/span&gt;)&lt;/pre&gt;

&lt;p&gt;这段代码与之前的区别就在于you.believe(it)一定会被调用一次，但是无所谓其结果是如何，这充分符合天朝&lt;strike&gt;某些&lt;/strike&gt;部门喜欢装摸作样“咨询民意”的状况。&lt;/p&gt;

&lt;h1&gt;扩展思考&lt;/h1&gt;

&lt;p&gt;最后再来一道扩展思考题吧：有人把“你爱，或者不爱我，爱就在那里，不增不减”写成了一段C#代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;if &lt;/span&gt;(you.Love(me) || !you.Love(me))
{
    love++;
    love--;
}&lt;/pre&gt;

&lt;p&gt;有人说，这段代码的if条件本身应该被编译器优化掉，因此会直接执行if内部的代码。还有人说，if内部的代码也会被编译器优化掉。您怎么看，为什么呢？&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/08/from-the-code-of-no-matter-you-believe-it-or-not.html#comments</comments>
      <pubDate>Fri, 05 Aug 2011 15:15:39 GMT</pubDate>
      <lastBuildDate>Sat, 06 Aug 2011 05:58:05 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>匿名类型的硬伤：围绕this的成员捕获策略</title>
      <link>http://blog.zhaojie.me/2011/06/java-anonymous-method-closure-scope-this.html</link>
      <guid>http://blog.zhaojie.me/2011/06/java-anonymous-method-closure-scope-this.html</guid>
      <description>&lt;p&gt;时不时听到一些C#程序员说，希望在C#里出现像Java匿名类一样的特性。以前我也觉得Java里的匿名类是个不错的特性，C#应该吸取进来。不过前段时间我仔细地理解了Java语言规范中关于内部类、匿名类的部分之后，一下子就被恶心到了。恶心过后，我忽然也意识到有些问题的确也是硬伤，也不能指责Java设计者的“品位”。例如，现在我想要谈的关于匿名类中this使用的问题——如果C#没法漂亮地实现这个特性，我宁愿它继续保持现状。&lt;/p&gt;

&lt;h1&gt;Java匿名类中的this&lt;/h1&gt;

&lt;p&gt;Java的匿名类特性，在于可以在项目里“内联”地实现一个类型，它可以继承一个现有的具体或抽象类，或是实现接口，并提供完整的成员实现。例如，这里有个抽象类，定义了一个抽象方法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java
&lt;/span&gt;&lt;span style="color: blue"&gt;abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyAbstractClass&lt;/span&gt; {
    &lt;span style="color: #2b91af"&gt;String &lt;/span&gt;getName() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;MyAbstractClass&amp;quot;&lt;/span&gt;;
    }

    &lt;span style="color: blue"&gt;abstract void &lt;/span&gt;print();
}&lt;/pre&gt;

&lt;p&gt;然后我们在另一个地方使用一个匿名类，继承这个类：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyClass &lt;/span&gt;{
    &lt;span style="color: #2b91af"&gt;String &lt;/span&gt;getName() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;MyClass&amp;quot;&lt;/span&gt;;
    }

    &lt;span style="color: #2b91af"&gt;MyAbstractClass &lt;/span&gt;someMethod() {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyAbstractClass&lt;/span&gt;() {
            &lt;span style="color: blue"&gt;public void &lt;/span&gt;print() {
                &lt;span style="color: #2b91af"&gt;System&lt;/span&gt;.out.println(getName());
            }
        };
    }
}&lt;/pre&gt;

&lt;p&gt;好，现在提一个问题，运行下面这行代码会打印出什么结果？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyClass&lt;/span&gt;().someMethod().print();&lt;/pre&gt;

&lt;p&gt;输出结果是MyAbstractClass而不是MyClass。换句话说，匿名类型中调用的getName方法是定义在MyAbstractClass里的，而不是定义在词法作用域（Lexical Scope）里的getName方法。根据Java规范，匿名类中的this（包括上面代码中“隐式”的this）表示类型本身对象，而与上下文无关。如果要访问词法作用域里的getName方法（即MyClass的方法），则反而必须显式指定MyClass类，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyClass &lt;/span&gt;{
    &lt;span style="color: #2b91af"&gt;String &lt;/span&gt;getName() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;MyClass&amp;quot;&lt;/span&gt;;
    }

    &lt;span style="color: #2b91af"&gt;MyAbstractClass &lt;/span&gt;someMethod() {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyAbstractClass&lt;/span&gt;() {
            &lt;span style="color: blue"&gt;public void &lt;/span&gt;print() {
                &lt;span style="color: #2b91af"&gt;System&lt;/span&gt;.out.println(&lt;span style="background-color: yellow"&gt;&lt;span style="color: #2b91af"&gt;MyClass&lt;/span&gt;.&lt;span style="color: blue"&gt;this&lt;/span&gt;&lt;/span&gt;.getName());
            }
        };
    }
}&lt;/pre&gt;

&lt;h1&gt;可能会造成的问题&lt;/h1&gt;

&lt;p&gt;在我看来，Java的这个设计决策很不好，十分容易让人误解代码的意图，但我相信肯定也有人会认为这只是个“品位”区别而已，没有高低。那么现在我们撇开“品位”不谈，谈点这个决策可能会造成的问题吧。例如，程序员A写了一个抽象类：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyAbstractClass &lt;/span&gt;{
&lt;span style="color: green"&gt;    &lt;/span&gt;&lt;span style="color: blue"&gt;abstract void &lt;/span&gt;print();
}&lt;/pre&gt;

&lt;p&gt;程序员B在另一个类的方法中编写了MyAbstractClass的匿名子类：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: #2b91af"&gt;MyAbstractClass &lt;/span&gt;someMethod() {
    &lt;span style="color: blue"&gt;final &lt;/span&gt;&lt;span style="color: #2b91af"&gt;String &lt;/span&gt;name = &lt;span style="color: #a31515"&gt;&amp;quot;MyClass&amp;quot;&lt;/span&gt;;
    &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyAbstractClass&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;print() {
            &lt;span style="color: #2b91af"&gt;System&lt;/span&gt;.out.println(name);
        }
    };
}&lt;/pre&gt;

&lt;p&gt;很显然，print方法会打印出name变量的值MyClass。相安无事多日，忽然某一天，程序员A需要为MyAbstractClass添加一些新功能，新增了一个受保护的name字段：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyAbstractClass&lt;/span&gt; {
    &lt;span style="color: blue"&gt;abstract void &lt;/span&gt;print();

    &lt;span style="color: green"&gt;// new field
    &lt;/span&gt;&lt;span style="color: blue"&gt;protected &lt;/span&gt;&lt;span style="color: #2b91af"&gt;String &lt;/span&gt;name = &lt;span style="color: #a31515"&gt;&amp;quot;MyAbstractClass&amp;quot;&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;于是第二天程序员B惊奇地发现，自己明明没有动过任何一行代码，MyAbstractClass忽然就无法正常工作了。这真让人情何以堪。&lt;/p&gt;

&lt;h1&gt;Java 8的Lambda表达式&lt;/h1&gt;

&lt;p&gt;事实上，关于这种“内联”定义函数的写法，我能想到的语言都是采取“词法作用域”，因此我想Java这方面的“特立独行”的确容易让人误会。当然客观来说，Java设计成这样也是无奈之举，因为它过于强调“类型”，匿名类还是一个类，既然是个类便会有自己的成员，既然有成员就应该让内联的函数有办法调用这些成员。与之相对，虽然C#中也可以定义内联的函数，却完全不会有Java的困扰，因为C#中内联的只是“函数”而不是完整的“类型”。&lt;/p&gt;

&lt;p&gt;说到，底还是多亏.NET中提供了“委托”这种纯粹的，可以让“函数”独立存在的概念。当时在C# 1.0刚出现时，Sun官方还发布文章，认为“委托”破坏了面向对象的纯粹性，“内部类”完全可以作为委托来使用。现在看来，这中观点无疑是一个笑话。追求纯粹的面向对象与盲目套用设计模式类似，都是舍本逐末的做法，我们追求的是“良好的设计”，“面向对象”只是手段而不是目标。如今C#已经发布近十年了，Java社区也在努力向Java7、Java 8里引入部分C#的特性，例如Lambda表达式。&lt;/p&gt;

&lt;p&gt;但是，由于Java中没有“委托”，即便是Lambda表达式依旧无法提供单独函数，还是必须附带一个完整的类型。因此this问题依旧存在，这依然是个硬伤。例如我&lt;a href="http://blog.zhaojie.me/2010/06/first-version-of-lambda-and-closures-in-java-7.html"&gt;以前的文章&lt;/a&gt;里也提到过Java 7里的SAM类型和Lambda表达式上下文成员的捕获策略。从&lt;a href="http://hg.openjdk.java.net/lambda/lambda/langtools/file/7704dcd17e0b/test/tools/javac/lambda/LambdaScope01.java"&gt;当时的资料&lt;/a&gt;来看，Lambda表达式的策略与匿名类相同，依旧以“匿名类”的成员优先，换句话说Lambda表达式只是匿名类的简单写法而已。不过现在这方面有了些许变化，例如&lt;a href="http://www.ordina.nl/nl/~/media/Files/Onze%20dienstverlening/Technologie/Masterclass%20Brian%20Goetz%20%20Project%20Lambda.ashx?forcedownload=1"&gt;这份幻灯片&lt;/a&gt;第18页里提到：Lambda表达式是一个拥有词法作用域的匿名方法（A lambda expression is a lexically scoped anonymous method），它的上下文成员捕获与Java的内部类、匿名类有明显不同。&lt;/p&gt;

&lt;p&gt;当然，如果使用匿名类的语法定义一个SAM类型，this相关的策略还是要与以前保持不变。Java和C#这类工业化语言的一个包袱，便是要保证兼容性——包括类库等其他方面。所以我还是一直认为，像Python，Ruby这般“洒脱”的技术平台及社区，的确很难进入企业开发市场。&lt;/p&gt;

&lt;h1&gt;硬伤&lt;/h1&gt;

&lt;p&gt;this问题可以说是Java匿名类特性的硬伤。C#如果想要引入这个匿名特性，似乎也完全无法躲开这一点。我并不希望C#引入一个“丑陋”的语言特性，幸好也没有任何迹象表明C#有这方面的打算。有趣的是，F#提供了类似Java匿名类的特性，但完全没有这个问题。为什么呢？一看代码便知：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
[&amp;lt;AbstractClass&amp;gt;]
&lt;span style="color: blue"&gt;type &lt;/span&gt;MyAbstractClass() =
    &lt;span style="color: blue"&gt;member &lt;/span&gt;this.Name = &lt;span style="color: maroon"&gt;&amp;quot;MyAbstractClass&amp;quot;
    &lt;/span&gt;&lt;span style="color: blue"&gt;abstract member &lt;/span&gt;Print: unit &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;unit

&lt;span style="color: blue"&gt;type &lt;/span&gt;MyClass() =
    &lt;span style="color: blue"&gt;let &lt;/span&gt;name = &lt;span style="color: maroon"&gt;&amp;quot;Local&amp;quot;

    &lt;/span&gt;&lt;span style="color: blue"&gt;member &lt;/span&gt;this.Name = &lt;span style="color: maroon"&gt;&amp;quot;MyClass&amp;quot;
    &lt;/span&gt;&lt;span style="color: blue"&gt;member &lt;/span&gt;this.MyMethod () =
        { &lt;span style="color: blue"&gt;new &lt;/span&gt;MyAbstractClass() &lt;span style="color: blue"&gt;with
            override &lt;/span&gt;inner.Print () =
                printfn &lt;span style="color: maroon"&gt;&amp;quot;%s&amp;quot; &lt;/span&gt;this.Name
                printfn &lt;span style="color: maroon"&gt;&amp;quot;%s&amp;quot; &lt;/span&gt;inner.Name
                printfn &lt;span style="color: maroon"&gt;&amp;quot;%s&amp;quot; &lt;/span&gt;name }&lt;/pre&gt;

&lt;p&gt;在F#中，定义一个类型的成员时，需要指定“该方法中表示自身对象的标识符”，我们可以将标识符取名为this，也可以取名为inner或是任意值。再加上F#中没有“隐式”的this指针存在，一切都是指明的，自然没有任何问题。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/06/java-anonymous-method-closure-scope-this.html#comments</comments>
      <pubDate>Wed, 15 Jun 2011 09:27:14 GMT</pubDate>
      <lastBuildDate>Wed, 15 Jun 2011 09:27:14 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>Jscex使用BSD授权协议正式发布</title>
      <link>http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html</link>
      <guid>http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html</guid>
      <description>&lt;p&gt;这次打算把Jscex好好搞一下了，其实很少会有技术方面的障碍能“轮到”我们去突破，但我觉得Jscex的确有机会，HTML 5、Node.js各个都是红火的玩意儿。前几天我花了两个晚上用半生不熟的中式英语写了一篇自认为比较完整的说明文字放到了Github上的项目首页上，没想到几个小时后便收到了&lt;a href="http://onilabs.com/stratifiedjs"&gt;StratifiedJS&lt;/a&gt;（一个与Jscex目标有些类似的项目）作者的邮件，提到了一些关于StratifiedJS的事情。我向他咨询了StratifiedJS的某些细节问题，也向他简单介绍了Jscex的实现原理。如今&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex已经使用BSD授权协议正式发布&lt;/a&gt;（中文站也会在近期推出），再进行一些细节上的优化便要开始作推广了。&lt;/p&gt;

&lt;p&gt;最近的一次优化便是去除不必要的Delay方法调用。Delay的目的是延迟某段代码的执行时间，确保它只会在合适的时间执行，这对于非“延迟”及有副作用的语言来说十分重要。在F#中Delay方法的签名如下（Jscex与之类似）：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// async.Delay
&lt;/span&gt;((&lt;span style="color: blue"&gt;unit &lt;/span&gt;-&amp;gt; Async&amp;lt;'a&amp;gt;) -&amp;gt; Async&amp;lt;'a&amp;gt;)&lt;/pre&gt;

&lt;p&gt;Delay函数可以不断嵌套，从效果说来讲不会有所区别，例如：&lt;/p&gt;

&lt;pre class="code"&gt;builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.Loop(...);
        });
    });
});&lt;/pre&gt;

&lt;p&gt;上面这段代码和直接一个builder.Loop相比不会对结果产生任何影响，但是显然，从生成代码的整洁、美观程度（这涉及到可调试性）及性能都会有所损耗。之前的Jscex编译器会生成不必要的Delay（当然也不会有上面那么夸张），那是因为以前的代码生成器只是直接遍历从UglifyJS解析器那里获得的AST，以此来生成代码。新的编译器重新调整了结构和策略，会先将UglifyJS的AST转化为一个中间形式——我把它叫做Jscex AST，然后再让代码生成器从Jscex AST生成代码。新的生成器在工作时，会略过某些delay节点，直接从它的自节点中生成代码，以此达到去除不必要的Delay方法调用的目的。&lt;/p&gt;

&lt;p&gt;举例来说，一个快速排序的Jscex函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[y], array[y + 1]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                $await(swapAsync(array, y, y + 1));
            }
        }
    }
}));&lt;/pre&gt;

&lt;p&gt;它会生成如下的代码（与&lt;a href="http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html"&gt;之前的结果&lt;/a&gt;相比省去了一些Delay方法调用）：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$_builder_$ = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;, $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;strike&gt;&lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {&lt;/strike&gt;
            &lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;x &amp;lt; array.length;
                },
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    x++;
                },
                $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;strike&gt;&lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {&lt;/strike&gt;
                        &lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0;
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;y &amp;lt; (array.length - x);
                            },
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                y++;
                            },
                            $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(compareAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;(r) {
                                    &lt;strike&gt;&lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {&lt;/strike&gt;
                                        &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(swapAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                            });
                                        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                        }
                                    });
                                });
                            }),
                            &lt;span style="color: blue"&gt;false
                        &lt;/span&gt;);
                    });
                }),
                &lt;span style="color: blue"&gt;false
            &lt;/span&gt;);
        });
    }));
})&lt;/pre&gt;

&lt;p&gt;当然，Jscex还是有进一步优化的空间。例如包括保留“没有bind操作”的代码块，以及“嵌套Jscex函数”等等。最近一段时间应该会不断有一些更新。如果您在用JavaScript开发异步程序，也不妨一起来使用Jscex吧，它毫无疑问能显著改善您的编程生活。&lt;/p&gt;

&lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;广告时间：&lt;/strong&gt;&lt;/font&gt;第四届nBazaar技术交流会将于4月23日（本周六）于畅星大厦（上海市浦东新区碧波路888号，地铁二号线张江高科站下，步行10分钟可达）副楼3楼会议厅举行，在此欢迎您的到来，请于下午1点前准时入场。没有报名的朋友也可以现场报名。由于领导支持，我们将在现场送出&lt;a href="http://bambook.sdo.com/"&gt;市场价999元的盛大Bambook&lt;/a&gt;一台作为幸运观众的礼品，此外图灵出版社也赞助了十几本图书将会送给积极发言的观众。更多详细信息请参考&lt;a href="http://nbazaar.org/"&gt;活动首页&lt;/a&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html#comments</comments>
      <pubDate>Thu, 21 Apr 2011 16:15:04 GMT</pubDate>
      <lastBuildDate>Thu, 21 Apr 2011 16:15:04 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>Jscex项目现状：UglifyJS解析器及AOT编译器</title>
      <link>http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html</link>
      <guid>http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;项目是我为了简化JavaScript异步的一个类库，支持任意JavaScript（ECMASCript 3）引擎。Jscex小巧而强大，可以极大地改善前端的AJAX及动画等场景的编程体验，同样也可以用在&lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt;进行服务器开发。从产生Jscex的想法到现在也有几个月的时间了，也一直想设法进行推广。在思考过程也发现了它在实际生产中可能会遇到的问题，于是前两个星期的主要工作，便是针对这些问题进行优化。首先我将Jscex的JavaScript分析器从&lt;a href="https://github.com/mozilla/narcissus"&gt;Narcissus&lt;/a&gt;换成了&lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt;，并基于node.js开发了一个简单的AOT编译器。接下来我也打算写个稍微详细一点的介绍，然后在国外社区看看反响如何。&lt;/p&gt;

&lt;p&gt;Jscex的本质是一个用JavaScript编写的JavaScript编译器，因此我需要一个JavaScript实现的JavaScript解析器。我起初&lt;a href="http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html"&gt;选择了著名的Narcissus项目&lt;/a&gt;，但由于它用到了&lt;a href="http://en.wikipedia.org/wiki/SpiderMonkey_(JavaScript_engine)"&gt;SpiderMonkey&lt;/a&gt;的一些扩展，最终我使用的其实是&lt;a href="http://www.neilmix.com/narrativejs/doc/"&gt;NarrativeJS&lt;/a&gt;中旧版的Narcissus代码。我一直在设法减小Jscex核心的体积及执行速度（毕竟一个重要的场景是浏览器端），再加上不是很喜欢旧版Narcissus代码的解析结果，于是我也在不断寻找它的替代品。前段时间我发现了UglifyJS这个JavaScript压缩器，它的解析器移植于&lt;a href="http://marijn.haverbeke.nl/parse-js/"&gt;parse-js&lt;/a&gt;项目，后者是一个用Common Lisp实现的类库，因此输出结构也十分简单，一个“表”而已，&lt;a href="http://blog.zhaojie.me/2011/04/uglifyjs-has-a-good-javascript-parser.html"&gt;执行速度也大大领先于Narcissus&lt;/a&gt;，体积也更小。于是我花了一个周末的时间将Jscex编译器改写为基于UglifyJS的实现。&lt;/p&gt;

&lt;p&gt;在改写过程中，我也同样考虑了目标代码在压缩后的体积。我使用&lt;a href="http://code.google.com/closure/compiler/"&gt;Closure Compiler&lt;/a&gt;的“高级”模式压缩代码，一般来说Closure Compiler的高级模式很破坏代码，我&lt;a href="http://blog.zhaojie.me/2011/04/compress-javascript-with-google-closure-compiler-in-advance-mode.html"&gt;使用了各种方式&lt;/a&gt;来保证压缩后的代码能够正确执行。目前，如果您要在项目中使用Jscex编写异步程序，需要依次加载以下三个文件（它们都在项目源码的bin目录中）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;uglifyjs-parser.min.js：UglifyJS解析器，大小20K，gzip后8K。 &lt;/li&gt;

  &lt;li&gt;jscex.min.js：Jscex核心编译器，大小5.5K，gzip后1.8K。 &lt;/li&gt;

  &lt;li&gt;jscex.async.min.js：Jscex异步核心类库，大小2K，gzip后0.9K。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果您觉得gzip后10K左右的体积还是有些大，那么也可以使用目前已经提供的AOT编译器——虽然AOT编译器的原始目的并不是为了减小体积。&lt;/p&gt;

&lt;p&gt;Jscex改善异步编程的原理，在于让程序员直接编写代码，使用普通的编程思路来实现算法，包括是用try...catch来捕获异常等等，而不会因为异步所需要的回调将代码拆得支离破碎。例如我们要实现冒泡排序算法的动画演示，也只需要使用传统编码方式实现算法即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// 标准算法&lt;/span&gt;
&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSort = &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(array[y] &amp;gt; array[y + 1]) {
                swap(array, y, y + 1);
            }
        }
    }
}

&lt;span style="color: green"&gt;// 演示动画&lt;/span&gt;
&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[y], array[y + 1]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                $await(swapAsync(array, y, y + 1));
            }
        }
    }
}));&lt;/pre&gt;

&lt;p&gt;Jscex.compile会解析代码，并生成异步代码，并交给eval来解释执行。bubbleSortAsync和其中调用的compareAsync（比较两个元素大小，并暂停10毫秒）和swapAsync（交换两个元素，绘图，并暂停20毫秒）都是异步方法。但是无论在编写和使用上，异步方法和同步算法几乎没有区别——唯一的区别便是$await语句必须单起一行。这个限制一是为了保证开发人员可以明确分清普通的JavaScript代码及异步方法调用，二便是为了简化编译器的实现。例如，“理想情况”下类似以下的代码也需要支持：&lt;/p&gt;

&lt;pre class="code"&gt;f(g(1), $await(...))

&lt;span style="color: blue"&gt;if &lt;/span&gt;(x &amp;gt; y &amp;amp;&amp;amp; $await(...)) { ... }&lt;/pre&gt;

&lt;p&gt;尤其是第二行代码，$await可能由于短路而根本不会执行。为此，Jscex要求开发人员明确编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;a1 = g(1);
&lt;span style="color: blue"&gt;var &lt;/span&gt;a2 = $await(...);
f(a1, a2);

&lt;span style="color: blue"&gt;if &lt;/span&gt;(x &amp;gt; y) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;flag = $await(...);
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(flag) { ... }
}&lt;/pre&gt;

&lt;p&gt;我并不担心这会让开发人员编写代码时有所不便，事实上F#的&lt;a href="http://msdn.microsoft.com/en-us/library/dd233250.aspx"&gt;Async Workflow&lt;/a&gt;是有这般要求，我甚至敢保证&lt;a href="http://blog.zhaojie.me/2010/10/pdc2010-the-future-of-csharp-and-vb-by-anders-hejlsberg-1.html"&gt;未来C#的异步特性&lt;/a&gt;也是类似的设计。但是，JavaScript有个重要的特点：它在实际使用时往往会被压缩。如果仅仅是去除空白字符，那么Jscex自然还可以正常工作。但事实上现代的JavaScript压缩工具都会分析代码的语义，并重新生成体积更小的代码。例如之前的bubbleSortAsync经过压缩便会成为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync=eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;,&lt;span style="color: blue"&gt;function&lt;/span&gt;(a){&lt;span style="color: blue"&gt;for&lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;b=0;b&amp;lt;a.length;b++)&lt;span style="color: blue"&gt;for&lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;c=0;c&amp;lt;a.length-b;c++){&lt;span style="color: blue"&gt;var &lt;/span&gt;d=$await(compareAsync(a[c],a[c+1]));d&amp;gt;0&amp;amp;&amp;amp;$await(swapAsync(a,c,c+1))}}))&lt;/pre&gt;

&lt;p&gt;试看d&amp;gt;0&amp;amp;&amp;amp;$wait(...)这段代码，完全就让Jscex无法工作了。为此，我为Jscex开发了AOT编译器（scripts目录下的jscexc.js及JscexExtractor.js文件），即在部署前便对代码进行编译并生成目标代码（之前是在运行时生成代码，即JIT编译）。AOT编译器同样使用JavaScript编写，使用node.js运行，这样便可以直接使用Jscex的编译器实现。与编译器核心不同，AOT编译器使用了最新版的Narcissus来解析代码，这是因为Narcissus能够提供更丰富的解析结果，我可以直接获得整个目标方法的起始和结束地址（不过有bug，我使用时绕开了），自然还包括原始代码，用起来十分方便。至于之前提到的依赖于SpiderMonkey扩展，体积较大，执行速度慢等缺点，对于AOT编译器来说便完全不是问题了。&lt;/p&gt;

&lt;p&gt;Jscex的AOT编译器使用起来十分简单：&lt;/p&gt;

&lt;pre class="code"&gt;node jscexc.js --input input_file --output output_file&lt;/pre&gt;

&lt;p&gt;例如，如果一个文件包含之前的bubbleSortAsync方法，那么经过AOT编译器之后，它的代码便会被替换成为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$_builder_$ = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;x &amp;lt; array.length;
                },
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    x++;
                },
                $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0;
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;y &amp;lt; (array.length - x);
                            },
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                y++;
                            },
                            $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(compareAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;(r) {
                                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                        &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(swapAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                            });
                                        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                        }
                                    });
                                });
                            }),
                            &lt;span style="color: blue"&gt;false
                        &lt;/span&gt;);
                    });
                }),
                &lt;span style="color: blue"&gt;false
            &lt;/span&gt;);
        });
    });
})&lt;/pre&gt;

&lt;p&gt;再进行压缩，便不会产生任何问题了。从表面看起来，编译后的Jscex代码体积大了不少，但是其中大部分为重复架子代码，压缩比例一般也会比较大。使用AOT编译后的代码有以下几个好处：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;经过JavaScript压缩器处理后也能正确执行。 &lt;/li&gt;

  &lt;li&gt;运行时只需要加载一个极小的jscex.async.min.js文件（异步核心类库），gzip后大小不到1K。 &lt;/li&gt;

  &lt;li&gt;由于代码在发布前生成，节省了JIT编译的开销。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在我个人看来，目前的Jscex已经可以在一些比较正式的场合中使用了。Jscex功能强大，实现小巧，能够与其它类库同时使用（它只会在全局对象上产生一个Jscex对象），接下来我也会为jQuery或MooTools等著名JavaScript框架/类库提供Jscex的绑定。在此也希望您可以实际使用一下Jscex项目，如果遇到问题请及时与我联系，我会给予您必要的支持。&lt;/p&gt;

&lt;p&gt;&lt;span style="color: red"&gt;广告时间：&lt;/span&gt;&lt;a href="http://nbazaar.org/"&gt;第四届nBazaar技术交流会&lt;/a&gt;将于2011年4月23日举行。第四届交流会的形式将略作改变：除了三场演讲（Windows Phone 7、IDE插件开发、单点登陆解决方案设计与实现）之外，本次活动设有嘉宾互动环节，您将有机会和嘉宾就某些话题进行探讨。我们正在收集话题，也希望大家踊跃提问，具体信息详见&lt;a href="http://nbazaar.org/"&gt;http://nbazaar.org/&lt;/a&gt;。此外，nBazaar技术沙龙的邮件列表已经正式启用，所有用户也已添加完成（之前报名或参加过技术会议）。如果有任何疑问，请邮件至&lt;img class="embed" src="http://services.nexodyne.com/email/customicon/9xaGAYDnFdYrRqAdl1tTvrs%3D/Y4fIT8s%3D/000000/ffffff/000000/0/image.png" /&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html#comments</comments>
      <pubDate>Thu, 14 Apr 2011 18:04:38 GMT</pubDate>
      <lastBuildDate>Thu, 14 Apr 2011 18:09:53 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>使用Jscex实现排序算法动画</title>
      <link>http://blog.zhaojie.me/2011/03/sorting-animations-with-jscex.html</link>
      <guid>http://blog.zhaojie.me/2011/03/sorting-animations-with-jscex.html</guid>
      <description>&lt;p&gt;用动画来观察排序算法是一件很酷的事情，例如&lt;a href="http://www.sorting-algorithms.com/"&gt;有人便为各种排序算法提供了动画效果&lt;/a&gt;。只可惜这些效果都是实现准备好的gif图片，并非由代码写成。在大部分平台上编写这样的程序并没有太大困难，只要在绘制出图形之后短暂地阻塞线程就行了。可惜，在JavaScript中我们只能“一蹴而就”，要暂停的话，只能使用setTimeout进行回调了。不过，这也正是&lt;a href="http://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;的用武之地，用Jscex编写的代码需要“暂停”，只需要简单地调用sleep异步方法，一切都很直接。&lt;/p&gt;

&lt;p&gt;例如，传统的冒泡排序算法，使用JavaScript实现可能是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSort = &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(array[y] &amp;gt; array[y + 1]) {
                swap(array, y, y + 1);
            }
        }
    }
}&lt;/pre&gt;

&lt;p&gt;如果使用Jscex，则变成了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[y], array[y + 1]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                $await(swapAsync(array, y, y + 1));
            }
        }
    }
}));&lt;/pre&gt;

&lt;p&gt;算法实现上唯一的变化可能就是“比较”方法被独立成单独的compareAsync方法了。它和swapAsync方法的代码如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;compareAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(x, y) {
    $await(Jscex.Async.sleep(10));
    &lt;span style="color: blue"&gt;return &lt;/span&gt;x - y;
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;swapAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array, x, y) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t = array[x];
    array[x] = array[y];
    array[y] = t;

    repaint(array);

    $await(Jscex.Async.sleep(20));
}));&lt;/pre&gt;

&lt;p&gt;简单地说，一个排序算法的性能如何，关键看它的比较和元素交换的次数。因此，我在compareAsync和swapAsync方法中都使用sleep进行了一定时间的停顿（其中后者更长一些）。由于改变了元素位置，因此在swapAsync方法中我还重新绘制了图案。有了这两个方法，我们只要将其简单地组合进bubbleSortAsync方法中即可。&lt;/p&gt;

&lt;p&gt;我们轻松实现的冒泡排序算法，Jscex编译器则会将其转化为复杂的Monadic代码，我本想贴在这里，但发现过于复杂凌乱，也太占地方。如果您感兴趣可以打开浏览器的console窗口，查看其输出结果。基于Jscex实现的选择排序和快速排序也和传统实现可谓毫无二致：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;selectionSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;j = 0; j &amp;lt; array.length - 1; j++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;mi = j;
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = j + 1; i &amp;lt; array.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[i], array[mi]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;lt; 0) { mi = i; }
        }
        $await(swapAsync(array, mi, j));
    }
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;quickSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    $await(_quickSortAsync(array, 0, array.length - 1));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;_quickSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array, begin, end) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;index = $await(_partitionAsync(array, begin, end));

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(begin &amp;lt; index - 1) {
        $await(_quickSortAsync(array, begin, index - 1));
    }

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(index &amp;lt; end) {
        $await(_quickSortAsync(array, index, end));
    }
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;_partitionAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array, begin, end) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;i = begin;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;j = end;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;pivot = array[Math.floor((begin + end) / 2)];

    &lt;span style="color: blue"&gt;while &lt;/span&gt;(i &amp;lt;= j) {
        &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[i], pivot));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;lt; 0) { i++; } &lt;span style="color: blue"&gt;else &lt;/span&gt;{ &lt;span style="color: blue"&gt;break&lt;/span&gt;; }
        }

        &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[j], pivot));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) { j--; } &lt;span style="color: blue"&gt;else &lt;/span&gt;{ &lt;span style="color: blue"&gt;break&lt;/span&gt;; }
        }

        &lt;span style="color: blue"&gt;if &lt;/span&gt;(i &amp;lt;= j) {
            $await(swapAsync(array, i, j));
            i++;
            j--;
        }
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;i;
}));&lt;/pre&gt;

&lt;p&gt;言止于此，我们现在就来看一下（&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/sorting-animations.html?bubble" target="_blank"&gt;冒泡&lt;/a&gt;，&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/sorting-animations.html?selection" target="_blank"&gt;选择&lt;/a&gt;，&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/sorting-animations.html?quick" target="_blank"&gt;快速&lt;/a&gt;）三种排序方式的动画吧（请使用支持canvas的浏览器，例如IE 9、FireFox、Chrome、Safari）。我很难想象，使用传统方式实现一个快速排序的动画会是什么情况。&lt;/p&gt;

&lt;p&gt;我最近愈发懒惰，愈发不愿意去想这些事情了。如今Jscex已经支持JavaSript语言中绝大部分语言特性，包括条件判断，各种循环（以及循环中的break和continue），还有异常处理等等。对于Jscex带来的编程体验我有十足的信心，但现在的最大问题却是不知道该如何推广。我也希望可以有更多人可以参与并接受这个项目。之前想过用它来实现一个小游戏等等，但素材方面却总是难以落实。我十分希望能从您那里得到各方面的意见和建议。&lt;/p&gt;

&lt;p&gt;最后还是一则&lt;span style="color: red"&gt;招聘广告&lt;/span&gt;：盛大创新院某英语学习方向的项目招聘1至2名ASP.NET程序员：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;岗位职责：负责服务器及数据库的搭建、维护工作，并配合前端工程师完成网站架构。 &lt;/li&gt;

  &lt;li&gt;岗位要求：熟悉.NET 3.5开发，有关系型数据库（MySQL优先）开发管理经验。有MongoDB等NoSQL开发经验者优先。有网站数据负载均衡经验者优先。 &lt;/li&gt;

  &lt;li&gt;办事认真踏实，对行业充满热情，了解行业最新动态。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;很显然，这不是我的招聘需求，不过我也帮忙筛选面试。欢迎大家自荐或推荐，邮件请至：&lt;img class="embed" src="http://services.nexodyne.com/email/icon/T3pYH0sugOKOkgan4fyp/GoPfEek%3D/R01haWw%3D/0/image.png" /&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/03/sorting-animations-with-jscex.html#comments</comments>
      <pubDate>Thu, 10 Mar 2011 15:35:09 GMT</pubDate>
      <lastBuildDate>Fri, 11 Mar 2011 03:41:49 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>我在面试.NET/C#程序员时会提出的问题</title>
      <link>http://blog.zhaojie.me/2011/03/my-interview-questions-for-dotnet-programmers.html</link>
      <guid>http://blog.zhaojie.me/2011/03/my-interview-questions-for-dotnet-programmers.html</guid>
      <description>&lt;p&gt;说起来我也面试过相当数量的.NET（包括C#，后文不重复）程序员了，有的通过电话，有的面谈。后来发现，其实提的问题来来回回也就那么几个。这些问题有的已经有十年历史了，至少也有三年。我想对于一个“不错”的.NET程序员来说，在简单的提示下绝大部分问题应该可以“对答如流”。可能您也会觉得这些太细节，真要追究起来似乎也大都不是必须的，无视这些照样可以写程序，做网站，赚工资，但是我不会满足于成为（包括招聘）这样的程序员，暂时也懒得解释掌握这些东西的益处和重要性。 每个人都有自己的看法， 一切就看您自己的选择了。&lt;/p&gt;

&lt;p&gt;那么现在就开始吧。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;什么是.NET？什么是CLI？什么是CLR？IL是什么？JIT是什么，它是如何工作的？GC是什么，简述一下GC的工作方式？&lt;/li&gt;

  &lt;li&gt;类（class）和结构（struct）的区别是什么？它们对性能有影响吗？.NET BCL里有哪些是类（结构），为什么它们不是结构（类）？在自定义类型时，您如何选择是类还是结构？&lt;/li&gt;

  &lt;li&gt;在.NET程序运行过程中，什么是堆，什么是栈？什么情况下会在堆（栈）上分配数据？它们有性能上的区别吗？“结构”对象可能分配在堆上吗？什么情况下会发生，有什么需要注意的吗？&lt;/li&gt;

  &lt;li&gt;泛型的作用是什么？它有什么优势？它对性能有影响吗？它在执行时的行为是什么？.NET BCL中有哪些泛型类型？举例说明平时编程中您定义的泛型类型。&lt;/li&gt;

  &lt;li&gt;异常的作用是什么？.NET BCL中有哪些常见的异常？在代码中您是如何捕获/处理异常的？在“catch (ex)”中，“throw”和“throw ex”有什么区别？您会如何设计异常的结构，什么情况下您会抛出异常？&lt;/li&gt;

  &lt;li&gt;List&amp;lt;T&amp;gt;和T[]的区别是什么，平时你如何进行选择？Dictionary&amp;lt;TKey, TValue&amp;gt;是做什么的？.NET BCL中还有哪些常用的容器？它们分别是如何实现的（哪种数据结构）？分别是适用于哪些场景？&lt;/li&gt;

  &lt;li&gt;抽象类和接口有什么区别？使用时有什么需要注意的吗？如何选择是定义一个“完全抽象”的抽象类，还是接口？什么是接口的“显式实现”？为什么说它很重要？&lt;/li&gt;

  &lt;li&gt;字符串是引用类型类型还是结构类型？它和普通的引用类型相比有什么特别的地方吗？使用字符串时有什么需要注意的地方？为什么说StringBuilder比较高效？在连接多个字符串时，它无论何时都比直接相加更高效吗？&lt;/li&gt;

  &lt;li&gt;如何高效地进行数组复制？“二维数组”和“数组的数组”有什么区别？在使用双重循环遍历一个二维数组时，如何选择内外层的遍历顺序？&lt;/li&gt;

  &lt;li&gt;什么是元编程，.NET有哪些元编程的手段和场景？什么是反射？能否举一些反射的常用场景？有人说反射性能较差，您怎么看待这个问题？有什么办法可以提高反射的性能吗？&lt;/li&gt;

  &lt;li&gt;委托是什么？匿名方法是什么？在C# 3.0中，Lambda表达式是什么？扩展方法是什么？LINQ是什么？您觉得C# 3.0中还有哪些重要的特性，它们带来了什么优势？BCL中哪些类库和这些特性有关？您平时最常用哪些？&lt;/li&gt;

  &lt;li&gt;工作之外您看哪些技术相关的书、网站、社区、项目等等？您还接触哪些.NET以外的技术，能和.NET或.NET中有针对性的部分做个对比吗？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上便是暂时想到的问题，以后有需要再做补充──当然，不提供答案，一是写出来太累了，二是这些东西往往也没有完全标准的答案。您一定发现了，这些问题其实都是和.NET基础相关，与某个特定的框架或是类库并没有多大关系。在实际面试时，如果是Web开发人员，我一定还会考察对方对ASP.NET及Web基础（更主要是协议和理念相关，而不是HTML，JS，CSS编程）的了解，某些情况下还会有关于多线程、并发的知识点。其他可能还会有一些例如软件开发、设计、实现等实践方面的问题。更重要的是，我一定会需要您在白板上当场写代码来解决一个小问题。不难，也不会直接要求写某个经典算法，事实上甚至是“&lt;a href="http://blog.zhaojie.me/2009/05/1491052.html"&gt;交换元素&lt;/a&gt;”这种简单到近乎毫无意义的问题，只可惜这类题目也能筛掉八成以上的人。&lt;/p&gt;

&lt;p&gt;我并不担心大家知道这些问题，而且我可以表示以后的面试估计也逃不开这些。如果您有某些意向，做些针对性的准备可能也是不错的。当然，既然是面试，就会有“随机应变”，你我都一样。我有自信可以在不断追问下发现到底是“真材实料”还是“临时抱佛脚”。&lt;/p&gt;

&lt;p&gt;您觉得还有哪些问题值得补充呢？&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/03/my-interview-questions-for-dotnet-programmers.html#comments</comments>
      <pubDate>Thu, 03 Mar 2011 07:38:54 GMT</pubDate>
      <lastBuildDate>Fri, 04 Mar 2011 01:10:37 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <title>基于Jscex.Async的JavaScript动画/游戏</title>
      <link>http://blog.zhaojie.me/2010/12/animations-and-games-based-on-jscex-async.html</link>
      <guid>http://blog.zhaojie.me/2010/12/animations-and-games-based-on-jscex-async.html</guid>
      <description>&lt;p&gt;首先和大家宣布一个消息，Jscex的代码已经&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;提交至Github上&lt;/a&gt;了，感兴趣的朋友下载来Dog Fooding一把，并欢迎提出反馈意见。Jscex受到F#计算表达式的启发，是一个面向JavaScript语言的monadic扩展，最常见的用途便是编写异步程序，尤其是逻辑复杂的异步程序。不过除此之外，使用这套异步库来编写动画或是游戏也是十分容易的事情。例如，一个人物的走动或是爆炸效果，其实可以视为一个贴图随时间不断变化的过程。这个变化的过程是异步的，但是有了Jscex.Async，我们只需使用最直接的同步形式编写代码就行了。&lt;/p&gt;

&lt;p&gt;例如这里有个&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/bullet.html"&gt;我用一个多小时编写的示例&lt;/a&gt;（需要支持canvas的浏览器，例如IE 9，FireFox，Chrome，Safari等等），类似于“是男人就撑过20秒”，其中有这么一段代码：&lt;/p&gt;

&lt;pre class="code"&gt;BulletGame.prototype._bulletFlyAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(f) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;options = &lt;span style="color: blue"&gt;this&lt;/span&gt;._options;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;bullet = {pos: {x: 0, y: 0}};

    &lt;span style="color: blue"&gt;this&lt;/span&gt;._addBullet(bullet);
    
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; &lt;span style="color: blue"&gt;this&lt;/span&gt;._playing &amp;amp;&amp;amp; &lt;span style="color: blue"&gt;this&lt;/span&gt;._inArea(bullet.pos); t += 20) {
        $await(&lt;span style="color: blue"&gt;this&lt;/span&gt;._timer.setTimeoutAsync(20));
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._tryHit(bullet.pos); &lt;span style="color: green"&gt;// 判断是否击中&lt;/span&gt;
        bullet.pos = f(t); &lt;span style="color: green"&gt;// 改变子弹位置&lt;/span&gt;
    }

    &lt;span style="color: blue"&gt;this&lt;/span&gt;._removeBullet(bullet);
}));&lt;/pre&gt;

&lt;p&gt;这段代码是一个异步程序，表示一颗子弹的飞行过程。子弹的飞行轨迹（某一时刻的位置）由函数f来决定，因此我们只需要写一个for循环，并且在循环内部“暂停”一段时间就行了，就这么简单。在循环之前，我们将子弹添加到一个容器里，这样在一个不断循环的paint方法里就会把这颗子弹绘制在canvas上面。循环结束后，我们将子弹从容器中移出。这一切都十分顺其自然，虽然这段代码的执行过程完全是异步的。&lt;/p&gt;

&lt;p&gt;Jscex可以使用各种方式与现有代码组合至一块儿，例如这里我为BulletGame的prototype对象扩展了一个方法，为此我晚上还改进了一下Jscex的编译逻辑，以及Jscex.Async的实现，其目的就是在_bulletFlyAsync这样的函数内部保留this对象在JavaScript语义上的正确性。JavaScript语言中this引用的动态特征是种很强大的特性，但是对于“语言改造者”来说，在一个充满回调的过程中保持语义正确并不是件十分容易的事情（当然，也不算十分困难，只要“想明白”即可）。上面的代码经过编译之后，就类似于我们直接编写了这样的代码（暂时如此，未来会有修改和优化）：&lt;/p&gt;

&lt;pre class="code"&gt;BulletGame.prototype._bulletFlyAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;(f) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;options = &lt;span style="color: blue"&gt;this&lt;/span&gt;._options;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;bullet = { pos: { x: 0, y: 0} };
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._addBullet(bullet);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Combine(
            $async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0;
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Loop(
                    &lt;span style="color: blue"&gt;function &lt;/span&gt;() { &lt;span style="color: blue"&gt;return this&lt;/span&gt;._playing &amp;amp;&amp;amp; &lt;span style="color: blue"&gt;this&lt;/span&gt;._inArea(bullet.pos); },
                    &lt;span style="color: blue"&gt;function &lt;/span&gt;() { t += 20; },
                    $async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(&lt;span style="color: blue"&gt;this&lt;/span&gt;._timer.setTimeoutAsync(20), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                            &lt;span style="color: blue"&gt;this&lt;/span&gt;._tryHit(bullet.pos);
                            bullet.pos = f(t);
                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
                        });
                    })
                );
            }),
            $async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;this&lt;/span&gt;._removeBullet(bullet);
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
            })
        );
    });
};&lt;/pre&gt;

&lt;p&gt;此外，JavaScript是一个很容易编写此类动画或是游戏的平台，因为它所有的代码都在UI线程上执行，因此不会有任何多线程方面的问题。例如上面的代码，我们可以放心地向容器里添加或删除“子弹”对象，试想在一个并发环境里构造一个线程安全的双向链表是件多么麻烦的事情。&lt;/p&gt;

&lt;p&gt;最大的问题其实是浏览器环境里的性能问题，不过我对此并不怎么担心，因为V8已经给我们开了个好头，并且还在&lt;a href="http://blog.chromium.org/2010/12/new-crankshaft-for-v8.html"&gt;不断前进&lt;/a&gt;。此外对于动画和游戏来说，最大的性能问题更可能是canvas的绘图性能。现在这个简单的例子性能自然不会有太大问题，不过一旦加上贴图和素材，性能问题就会凸现出来了。对于目前的示例，我在安装Windows 7的台式机里试验了多种浏览器，帧数（fps）各有高低：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Firefox：110左右 &lt;/li&gt;

  &lt;li&gt;Chrome：210左右 &lt;/li&gt;

  &lt;li&gt;IE 9：300至800不等，比较常见的是500左右 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而在我的MBP上：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Firefox：70左右 &lt;/li&gt;

  &lt;li&gt;Safari：60左右 &lt;/li&gt;

  &lt;li&gt;Chrome：190左右 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此外还有同事测试了Safari最新的Nightly Build，帧数与Chrome不相上下。从中可以看出，IE 9的表现最为出色，可见它的GPU加速的确不是在吹牛的。值得一提的是，如果图像上没有子弹，那么帧数大约只有60出头，一旦同时出现了几十颗子弹，帧数变会飙升至500以上，峰值甚至可以到800。由此推测，IE 9也是在“按需”使用GPU，十分周全。&lt;/p&gt;

&lt;p&gt;Jscex的源码及示例都在Github上，欢迎尝试。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/12/animations-and-games-based-on-jscex-async.html#comments</comments>
      <pubDate>Wed, 15 Dec 2010 17:03:28 GMT</pubDate>
      <lastBuildDate>Thu, 16 Dec 2010 05:14:09 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>演出季上“异步编程模型的演变”幻灯片</title>
      <link>http://blog.zhaojie.me/2010/12/event-season-async-evolution-slides.html</link>
      <guid>http://blog.zhaojie.me/2010/12/event-season-async-evolution-slides.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html"&gt;演出季&lt;/a&gt;终于过去了，现在就来做一个收尾吧。这次的主题是“异步编程模型的演变”，主要回顾了微软在.NET平台上异步编程上的进化：基于回调，基于迭代生成器，基于类库，基于语言。不过这样的编程模型其实并非微软独有，而是一些运用比较广泛的异步编程方式，因此在SD 2.0大会上我其实完全用JavaScript进行演示。从结果上来看，除了最早的TUP，其他两场演讲（&lt;a href="http://www.net-china.org/"&gt;.NET技术大会&lt;/a&gt;和&lt;a href="http://sd2china.csdn.net/"&gt;SD 2.0&lt;/a&gt;）的反响都不错。&lt;/p&gt;

&lt;p&gt;严格说来，这场演讲在过去一个月中讲了四次，其中第一次是在CSDN举办的&lt;a href="http://tup.csdn.net/"&gt;TUP&lt;/a&gt;活动上。您可能注意到“异步编程”是我后半年关注的重点，例如在&lt;a href="http://nbazaar.org"&gt;创新院赞助的nBazaar交流会&lt;/a&gt;上我分享的话题都是关于异步编程的，这次要在1小时内覆盖之前两场演讲，自然信息量很大。于是在TUP活动上的演讲便不太理想，因为内容实在太多，结果绝大部分内容几乎都是匆匆扫过。不幸中的万幸，这次演讲的幻灯片倒成了一个“阅读材料”（&lt;a href="http://files.zhaojie.me/slides/async-evolution/async-evolution-full.pdf"&gt;下载&lt;/a&gt;）。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_6103768"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="The Evolution of Async-Programming on .NET Platform (TUP, Full)" href="http://www.slideshare.net/jeffz/async-evolutionfull"&gt;The Evolution of Async-Programming on .NET Platform (TUP, Full)&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6103768" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-full-101210113950-phpapp02&amp;stripped_title=async-evolutionfull&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6103768" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-full-101210113950-phpapp02&amp;stripped_title=async-evolutionfull&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;我将异步编程模型的演变过程分为四个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;基于回调：&lt;/strong&gt;这是最传统的异步编程模型，“回调”是“异步”的天然属性，例如.NET里的Begin/End异步模型或是基于事件的异步模型。在前端开发中，XMLHttpRequest或是DOM事件，事实上都是基于事件的异步编程模型。这种编程模型破坏了代码的局部性，写起来非常麻烦，更别说是异步操作的组合、异常处理、取消等操作了。 &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;基于迭代生成器：&lt;/strong&gt;我把这个单独提出来说，是因为迭代生成器几乎已经成为现代语言的标配了，&lt;a href="http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html"&gt;在JavaScript 1.7里也有相应的特性&lt;/a&gt;——自然，可怜的Java语言是不曾提供支持的。有了迭代生成器，我们就可以在发起异步操作的时候，将控制权交给外部，由外部来决定代码什么时候继续执行，这就在一定程度上保持了代码的局部性。 &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;基于类库：&lt;/strong&gt;由于异步编程十分困难，有时候语言层面的支持有限，聪明的程序员们想尽了各种办法来简化异步编程。其中主要的办法就是总结出一种异步模型，并围绕这种模型提供一种异步类库。其中的典型便是&lt;a href="http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html"&gt;“推集合模型”以及“响应式编程”&lt;/a&gt;。只可惜想要基于死板的Java语言开发出好用的异步类库也总是使不出劲，与它形成鲜明对比的便是Scala语言，基于同样的平台，生产力却天下地下。 &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;基于语言：&lt;/strong&gt;有时候，语言设计者会直接在语言层面上简化异步编程的问题，其中的典型便是Erlang语言以及&lt;a href="http://blog.zhaojie.me/2010/10/pdc2010-the-future-of-csharp-and-vb-by-anders-hejlsberg-1.html"&gt;下个版本的C#&lt;/a&gt;。不过这里我主要讨论的是&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;F#语言里的异步工作流&lt;/a&gt;。不过严格来说，F#的异步工作流是个类库，它使用了F#的“计算表达式”特性，这才是个语言特性。我参考了F#的异步工作流，开发Jscex（一个JavaScript至JavaScript编译器）以及Jscex.Async组件（基于Jscex的异步类库）。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于TUP上的失策，我为一个星期后的.NET技术大会上重新组织了内容，减少了“代码赏析”的数量，增加了演示用的示例。我的演示是在Mac OS上基于Mono 2.8和MonoMac编写的带有Mac OS原生界面的应用程序，以此体现异步编程对于各平台上UI编程的重要性。有了TUP的经验，这次的演讲反响不错，我也被评为这次会议Top 5讲师。幻灯片如下（&lt;a href="http://files.zhaojie.me/slides/async-evolution/async-evolution-netchina.pdf"&gt;下载&lt;/a&gt;）：&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_6103757"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="The Evolution of Async-Programming on .NET Platform (.Net China, C#)" href="http://www.slideshare.net/jeffz/async-evolutiondotnetconf"&gt;The Evolution of Async-Programming on .NET Platform (.Net China, C#)&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6103757" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-dotnetconf-101210113849-phpapp02&amp;stripped_title=async-evolutiondotnetconf&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6103757" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-dotnetconf-101210113849-phpapp02&amp;stripped_title=async-evolutiondotnetconf&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;我为SD 2.0大会使用JavaScript重新编写了所有的示例，并在创新院内部的分享交流会上进行了试讲。试讲的反馈是“干货太多”，这使得我重新提炼了一下示例。不过在大会的前一天，我还是补充了一个基于node.js开发的最简单的静态文件服务器，以此表示Jscex并非只能在浏览器里使用。幻灯片如下（&lt;a href="http://files.zhaojie.me/slides/async-evolution/async-evolution-sd2.pdf"&gt;下载&lt;/a&gt;）：&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_6103746"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="The Evolution of Async-Programming (SD 2.0, JavaScript)" href="http://www.slideshare.net/jeffz/async-evolutionsd2"&gt;The Evolution of Async-Programming (SD 2.0, JavaScript)&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6103746" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-sd2-101210113722-phpapp01&amp;stripped_title=async-evolutionsd2&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6103746" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-sd2-101210113722-phpapp01&amp;stripped_title=async-evolutionsd2&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;有趣的是，在我之前的一场演讲接近尾声的时候，会场里大约只坐了一半人，因此我一开始还担心没人来听。幸运的是在演讲开始时位子已经基本坐满了，后来还有一些人站在一旁。演讲之后的反馈很少，不过都是正面的。会后我还和业界的一些JavaScript大牛交流了一下Jscex，这也是我接下来一段时间的一个工作重点，希望能够做大做好。&lt;/p&gt;

&lt;p&gt;这个演出季已经过去了，接下来又到了nBazaar交流会的时间了。第三届交流会将在1月15日举行，具体消息将在不久之后公开，敬请关注。我们又准备了四场有价值的演讲，一定能让您满意。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/12/event-season-async-evolution-slides.html#comments</comments>
      <pubDate>Sat, 11 Dec 2010 14:01:05 GMT</pubDate>
      <lastBuildDate>Mon, 13 Dec 2010 07:16:55 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>适合JavaScript 1.7中迭代生成器的异步编程机制</title>
      <link>http://blog.zhaojie.me/2010/12/javascript-17-yield-async-programming.html</link>
      <guid>http://blog.zhaojie.me/2010/12/javascript-17-yield-async-programming.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html"&gt;上篇文章&lt;/a&gt;我提出了一种基于JavaScript 1.7中迭代生成器（yield）的异步编程方式，它可以让混乱的异步代码逻辑变得清晰一些。不过之前的AsyncIterator其实是对&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html"&gt;基于C# 2.0的AsyncEnumerator&lt;/a&gt;的仿制品，在公司的分享会上进行交流以后，同事&lt;a href="http://hax.javaeye.com/"&gt;hax&lt;/a&gt;提出其实可以实现地更漂亮一些。在他的提示下，我了解到JavaScript 1.7中不同于C# 2.0里的特性，因而对这种异步编程机制提出了改进。只可惜yield特性被ECMAScript 5排除了，这实在可以说是&lt;a href="http://en.wikipedia.org/wiki/Design_by_committee"&gt;委员会设计模式&lt;/a&gt;的又一次伟大胜利。&lt;/p&gt;

&lt;h1&gt;JavaScript 1.7的yield&lt;/h1&gt;

&lt;p&gt;与C#中yield功能不同的是，JavaScript 1.7的yield语句可以让生成器内部获得一个值。例如这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;numbers = &lt;span style="color: blue"&gt;function &lt;/span&gt;(min, max) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = min; i &amp;lt;= max; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;sum = &lt;span style="background-color: yellow; color: blue"&gt;yield&lt;/span&gt; i;
        document.write(sum + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;quot;&lt;/span&gt;);
    }
}

&lt;span style="color: blue"&gt;var &lt;/span&gt;iterator = numbers(1, 10);
&lt;span style="color: blue"&gt;var &lt;/span&gt;sum = iterator.next();
&lt;span style="color: blue"&gt;try &lt;/span&gt;{
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;i = iterator.&lt;font color="#ff0000"&gt;send&lt;/font&gt;(sum);
        document.write(i + &lt;span style="color: maroon"&gt;&amp;quot; - &amp;quot;&lt;/span&gt;);
        sum += i;
    }
} &lt;span style="color: blue"&gt;catch &lt;/span&gt;(err &lt;span style="color: blue"&gt;if &lt;/span&gt;err &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;StopIteration) { }&lt;/pre&gt;

&lt;p&gt;执行后的结果是：&lt;/p&gt;

&lt;pre class="code"&gt;1
2 - 3
3 - 6
4 - 10
5 - 15
6 - 21
7 - 28
8 - 36
9 - 45
10 - 55&lt;/pre&gt;

&lt;p&gt;在JavaScript 1.7中，yield除了停止流程，将控制权交给外部之外，还能够返回一个值。这个值由外部控制的代码send至迭代器内部——不过，“启动”一个迭代器则不能使用带参数的send。如上面的代码，我们在while循环外使用next启动一个迭代器。&lt;/p&gt;

&lt;h1&gt;异步编程模型&lt;/h1&gt;

&lt;p&gt;在上一篇文章中，我们把异步编程模型统一为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; beginXxx = &lt;span style="color: blue"&gt;function&lt;/span&gt; (arg0, arg1, ..., callback) { ... }&lt;/pre&gt;

&lt;p&gt;调用这个异步方法时，我们需要显示地提供一个callback回调函数。而如今我们已经有了进一步的打算，则需要对其进行修改，如下所示：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;xxxAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;(arg1, arg2, ...) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start: &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) { ... }
    }
}&lt;/pre&gt;

&lt;p&gt;简单地说，以前的beginXxx方法会直接发起一个异步请求，而如今的xxxAsync方法，则是返回一个表示“异步任务”的对象，这个对象上有一个start方法，接受一个callback函数作为参数，并启动这个异步任务。待异步任务完成时，使用callback函数发起通知并回传结果。基于这个异步模型，我们可以封装一些常用的“异步任务”，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;sleepAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;(ms) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start : &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) {
            window.setTimeout(callback, ms);
        }
    };
}&lt;/pre&gt;

&lt;p&gt;sleepAsync获得一个“休眠”的异步任务，执行时便会使用setTimeout计时回调。另一个常见的异步任务则是AJAX请求：&lt;/p&gt;

&lt;pre class="code"&gt;XMLHttpRequest.prototype.receiveAsync = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start : &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
            _this.onreadystatechange = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.readyState == 4) {
                    callback(_this.responseText);
                }
            }

            _this.send();
        }
    };
}&lt;/pre&gt;

&lt;p&gt;我为XMLHttpRequest做了扩展，它的receiveAsync方法将获得一个异步任务，这个异步任务的执行结果便是这个请求的responseText值。&lt;/p&gt;

&lt;h1&gt;辅助类：AsyncTask&lt;/h1&gt;

&lt;p&gt;之前的辅助对象是AsyncIterator，而现在我们只需根据一个迭代器新建一个异步任务即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;AsyncTask = &lt;span style="color: blue"&gt;function&lt;/span&gt;(iterator) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator = iterator;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
}
AsyncTask.prototype._loop = &lt;span style="color: blue"&gt;function&lt;/span&gt;(result) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
    &lt;span style="color: blue"&gt;try &lt;/span&gt;{
        &lt;span style="color: blue"&gt;var &lt;/span&gt;task = &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator.send(result);
        task.start(&lt;span style="color: blue"&gt;function&lt;/span&gt;(r) { _this._loop(r); });
    } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(err &lt;span style="color: blue"&gt;if &lt;/span&gt;err &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;StopIteration) {  
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;._callback) &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback();
    }
}
AsyncTask.prototype.start = &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = callback;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._loop();
}&lt;/pre&gt;

&lt;p&gt;作为符合我们异步编程模型的对象，AsyncTask也包含一个start方法，它会调用_loop，在_loop中则从迭代器里获得下一个异步任务，启动，并在它执行结束时继续调用_loop自身。在新的异步模型中，构建一个辅助类库也变得非常容易。&lt;/p&gt;

&lt;h1&gt;示例1：移动HTML元素&lt;/h1&gt;

&lt;p&gt;还是使用上次的例子。实现一个移动HTML元素的逻辑其实很简单，只要根据一个时间间隔不断地改变其top和left即可。例如这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// Pseudocode, cannot work
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;move = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
        e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
        sleep(50); &lt;span style="color: #006400"&gt;// cannot sleep
    &lt;/span&gt;}

    e.style.left = endPos.x;
    e.style.top = endPos.y;
}&lt;/pre&gt;

&lt;p&gt;只可惜上面这段代码是无法运行的，因为在浏览器里我们没有任何手段让当前的工作线程暂停，我们没有一个阻塞的同步的sleep方法。不过我们现在有了sleepAsync方法可以提供一个异步任务。因此，moveAsync方法只能这样编写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = &lt;span style="color: blue"&gt;function&lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start: &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0;
            &lt;span style="color: blue"&gt;var &lt;/span&gt;loop = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(t &amp;lt; duration) {
                    t += 50;
                    e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
                    e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
                    sleepAsync(50).start(loop);
                } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                    &lt;span style="color: blue"&gt;if &lt;/span&gt;(callback) callback();
                }
            }
        
            loop();
        }
    };
}&lt;/pre&gt;

&lt;p&gt;我们无法使用for循环，只能把循环拆成loop回调。这就是异步代码破坏了代码局部性的例证。可能有些朋友会觉得这样的代码写起来没什么困难的，那么如果再加上if…else或是try…catch呢？不管怎么样，这段代码破坏了程序员编程思路，我觉得实在太丑了。幸好，使用AsyncTask和迭代生成器之后代码完全不需要这么写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// beginMove with AsyncTask
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync2 = &lt;span style="color: blue"&gt;function&lt;/span&gt;(e, startPos, endPos, duration) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
            e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
            e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
            &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;yield&lt;/span&gt; sleepAsync(50);&lt;/span&gt;
        }

        e.style.left = endPos.x;
        e.style.top = endPos.y;
    };

    &lt;span style="color: blue"&gt;return new &lt;/span&gt;AsyncTask(generator());
}&lt;/pre&gt;

&lt;p&gt;调用beginSleep之后代码使用yield将控制权交还给了AsyncIterator，而beginSleep完成之后也会通知AsyncIterator并继续这段逻辑。有了yield，我们的代码编写起来便顺畅多了。与之前yield只是用作“交出控制权”相比，如今的yield直接返回一个异步任务，可谓干净清爽，语义优雅。&lt;/p&gt;

&lt;h1&gt;示例二：获取多个URL的内容&lt;/h1&gt;

&lt;p&gt;如果有一个URL数组，要编写一个异步方法，返回这个URL数组对应的请求内容。如今我们可以这么写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;receiveAllAsync = &lt;span style="color: blue"&gt;function&lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;result = [];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
            req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
            &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;content = &lt;span style="color: blue"&gt;yield&lt;/span&gt; req.receiveAsync();&lt;/span&gt;
            result.push(content);
        }
    };

    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start: &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
            (&lt;span style="color: blue"&gt;new &lt;/span&gt;AsyncTask()).start(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                callback(result);
            });
        }
    };
}&lt;/pre&gt;

&lt;p&gt;与上例有所不同的是，我们通过yield语句直接得到了receiveAsync异步任务的结果。比较可惜的一点是，无论是之前的AsyncIterator还是现在的AsyncTask都对需要返回值的异步方法不是十分友好，写法有些绕。这也是yield受到功能本身的限制，可见语言特性的确对编程模型的塑造有十分重要的影响：C# 2.0的异步编程模型不如JavaScript 1.7，JavaScript 1.7的异步编程模型却不如&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;总体而言，有了JavaScript 1.7中的迭代生成器，在许多时候已经可以极大地简化异步操作的编程体验了。只可惜JavaScript 1.7只是Mozilla一家的语言，虽然在ECMAScript 4中也有类似功能，但在ECMAScript 5却已经被废弃了。在可预见的将来，这个语言特性不会被各浏览器或是JavaScript引擎所接受。实在可以说是&lt;a href="http://en.wikipedia.org/wiki/Design_by_committee"&gt;委员会设计模式&lt;/a&gt;的又一次伟大胜利——他们就是不希望开发人员的生活能够好过一些。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/12/javascript-17-yield-async-programming.html#comments</comments>
      <pubDate>Fri, 03 Dec 2010 11:09:53 GMT</pubDate>
      <lastBuildDate>Fri, 03 Dec 2010 11:10:16 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>JavaScript版本的AsyncEnumerator</title>
      <link>http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html</link>
      <guid>http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html</guid>
      <description>&lt;p&gt;地球人都知道，在C# 2.0里提供了yield关键字，可以方便好用地生成一个迭代器，更可以&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html"&gt;简化异步操作&lt;/a&gt;——这是因为有了Jeffrey Richter开发的AsyncEnumerator。在接下来的某些演讲中我准备的主题是“异步编程模型”的演变，自然少不了这非常重要的一环。为了便于广大人民群众更好地接受，我决定使用JavaScript来进行说明。为此，我用JavaScript实现了一个AsyncEnumerator。&lt;/p&gt;

&lt;h1&gt;JavaScript 1.7里的Iterator生成器&lt;/h1&gt;

&lt;p&gt;AsyncEnumerator的关键在于yield，yield可以让开发者&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html"&gt;轻易实现出一个迭代器&lt;/a&gt;。只可惜在&lt;a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm"&gt;ECMA-262&lt;/a&gt;里并没有定义这个功能，还好在&lt;a href="https://developer.mozilla.org/en/New_in_javascript_1.7"&gt;FireFox 2.0之后里实现了JavaScript 1.7&lt;/a&gt;，其中便提供了&lt;a href="https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators"&gt;Iterator的生成器&lt;/a&gt;。这里我先做一个简单的介绍。&lt;/p&gt;

&lt;p&gt;例如，基于JavaScript 1.7实现无限长的菲波纳妾数列，只要：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;fibonacci() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;fn1 = 1;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;fn2 = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;current = fn2;
        fn2 = fn1;
        fn1 = fn1 + current;
        &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;yield&lt;/span&gt; current;&lt;/span&gt;
    }
}&lt;/pre&gt;

&lt;p&gt;在使用的时候，JavaScript 1.7里也提供了比较方便的语法，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i &lt;span style="color: blue"&gt;in &lt;/span&gt;fibonacci()) {
    print(i);
}&lt;/pre&gt;

&lt;p&gt;就像C#中的foreach是使用了MoveNext方法和Current属性一样，其实JavaScript 1.7中针对迭代器的for…in语法也是使用了迭代器对象上的next方法。例如上面的代码便可以修改为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;iterator = fibonacci();
&lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
    print(iterator.next());
}&lt;/pre&gt;

&lt;p&gt;您可能会想，这为什么是一个死循环？这是因为在JavaScript 1.7中，而每次调用next方法都会得到yield返回的某一项，同时通过抛出“StopIteration”来表示“迭代终止”。一般来说不需要手动维护异常，因为这些都交给for…in处理了。&lt;/p&gt;

&lt;h1&gt;异步模型&lt;/h1&gt;

&lt;p&gt;无论是哪种“异步编程模型”，首先都需要总结出一种通用的“异步任务模型”，例如.NET中的Begin/End或是基于事件的异步模型。为了简化问题，我在这里提出一种最为简单的异步编程：每个异步操作都是以下形式的：&lt;/p&gt;

&lt;pre class="code"&gt;beginXxx(arg0, arg1, ..., callback);&lt;/pre&gt;

&lt;p&gt;例如我们来扩展一下XMLHttpRequest类型，提供一个beginReceive方法，最终返回responseText：&lt;/p&gt;

&lt;pre class="code"&gt;XMLHttpRequest.prototype.beginReceive = &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;.onreadystatechange = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.readyState == 4) {
            callback(&lt;span style="color: blue"&gt;this&lt;/span&gt;.responseText);
        }
    }

    &lt;span style="color: blue"&gt;this&lt;/span&gt;.send();
}&lt;/pre&gt;

&lt;p&gt;同样，我们提供一个beginSleep方法，它仅仅是一个setTimeout函数的简单封装：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginSleep = &lt;span style="color: blue"&gt;function &lt;/span&gt;(ms, callback) {
    window.setTimeout(callback, ms);
}&lt;/pre&gt;

&lt;p&gt;调用beginXxx方法则表示发起一个异步的Xxx操作，其结果会通过callback回调函数传递过来。“回调”是“异步”的精髓，尽管它经常让我们痛苦万分。一般来说，无论是何种异步模型（甚至不关.NET还是JavaScript），最终都是基于回调函数的，最终其实也可以归纳成这里的异步调用形式。&lt;/p&gt;

&lt;h1&gt;AsyncIterator&lt;/h1&gt;

&lt;p&gt;下面我们便来仿造AsyncEnumerator编写一个AsyncIterator。AsyncEnumerator的原理很简单，我们在这里继续将其简化，由于是JavaScript，我们还不需要处理多线程之间的竞争，可谓再简单不过了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;AsyncIterator = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._result = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
}
AsyncIterator.prototype.callback = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
    &lt;span style="color: blue"&gt;return function &lt;/span&gt;(result) {
        _this._result = result;
        &lt;span style="color: blue"&gt;try &lt;/span&gt;{
            _this._iterator.next();
        } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(e) {
            _this._callback();
        }
    };
}
AsyncIterator.prototype.result = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return this&lt;/span&gt;._result;
}
AsyncIterator.prototype.beginInvoke = &lt;span style="color: blue"&gt;function &lt;/span&gt;(iterator, callback) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator = iterator;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = callback;

    iterator.next();
}&lt;/pre&gt;

&lt;p&gt;AsyncIterator的原理和使用方式可以用以下几句话描述清楚：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;异步代码将异步操作的结果提交至AsyncIterator提供的回调函数中（通过调用callback方法获得）。 &lt;/li&gt;

  &lt;li&gt;异步代码在发起一个异步操作之后，使用yield将控制权交还给外部（其实就是AsyncIterator）。 &lt;/li&gt;

  &lt;li&gt;异步操作完成后会执行AsyncIterator提供的回调函数，AsyncIterator则调用迭代器的next方法，继续执行代码。 &lt;/li&gt;

  &lt;li&gt;异步代码可以从AsyncIterator的result方法中获得上一个异步操作的结果。 &lt;/li&gt;

  &lt;li&gt;当next方法抛出异常时，表示迭代器执行完毕，AsyncIterator则通过回调函数进行通知。 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了便于使用，我为AsyncIterator提供一个“静态方法”：&lt;/p&gt;

&lt;pre class="code"&gt;AsyncIterator.beginInvoke = &lt;span style="color: blue"&gt;function &lt;/span&gt;(generator, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;ai = &lt;span style="color: blue"&gt;new &lt;/span&gt;AsyncIterator();
    &lt;span style="color: blue"&gt;var &lt;/span&gt;iterator = generator(ai);
    ai.beginInvoke(iterator, callback);
}&lt;/pre&gt;

&lt;p&gt;接下来，我们便来看两个实例。&lt;/p&gt;

&lt;h1&gt;示例1：移动HTML元素&lt;/h1&gt;

&lt;p&gt;实现一个移动HTML元素的逻辑其实很简单，只要根据一个时间间隔不断地改变其top和left即可。例如这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// Pseudocode, cannot work
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;move = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
        e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
        sleep(50); &lt;span style="color: #006400"&gt;// cannot sleep
    &lt;/span&gt;}

    e.style.left = endPos.x;
    e.style.top = endPos.y;
}&lt;/pre&gt;

&lt;p&gt;只可惜上面这段代码是无法运行的，因为在浏览器里我们没有任何手段让当前的工作线程暂停，我们没有一个阻塞的同步的sleep方法，只有一个异步的beginSleep方法（如上文）。因此，其实beginMove方法只能这样编写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginMove = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;loop = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(t &amp;lt; duration) {
            t += 50;
            e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
            e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
            beginSleep(50, loop);
        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
            callback();
        }
    }

    loop();
}&lt;/pre&gt;

&lt;p&gt;我们无法使用for循环，只能把循环拆成loop回调。这就是异步代码破坏了代码局部性的例证。可能有些朋友会觉得这样的代码写起来没什么困难的，那么如果再加上if…else或是try…catch呢？不管怎么样，这段代码破坏了程序员编程思路，我觉得实在太丑了，使用AsyncIterator便会直观许多：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginMove2 = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration, callback) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function &lt;/span&gt;(ai) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
            e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
            e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
            beginSleep(50, &lt;span style="background-color: yellow"&gt;ai.callback()&lt;/span&gt;);
            &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;yield &lt;/span&gt;0;&lt;/span&gt;
        }

        e.style.left = endPos.x;
        e.style.top = endPos.y;
    };

    AsyncIterator.beginInvoke(generator, callback);
}&lt;/pre&gt;

&lt;p&gt;调用beginSleep之后代码使用yield将控制权交还给了AsyncIterator，而beginSleep完成之后也会通知AsyncIterator并继续这段逻辑。有了yield，我们的代码编写起来便顺畅多了。&lt;/p&gt;

&lt;h1&gt;示例2：批量请求数据&lt;/h1&gt;

&lt;p&gt;如果现在有一个urls数组，其中包含了目标地址，您如何将它们的结果也通过一个数组返回过来呢？伪代码如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// Pseudocode, cannot work
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;receiveMany = &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;results = [];

    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
        req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
        &lt;span style="color: blue"&gt;var &lt;/span&gt;r = req.receive(); &lt;span style="color: #006400"&gt;// cannot recieve
        &lt;/span&gt;results.push(r);
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;results;
}&lt;/pre&gt;

&lt;p&gt;很显然实际可以运行的代码只能是异步的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginReceiveMany = &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;results = [];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;loop = &lt;span style="color: blue"&gt;function &lt;/span&gt;(i) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(i &amp;lt; urls.length) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
            req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
            req.beginReceive(&lt;span style="color: blue"&gt;function &lt;/span&gt;(r) {
                results.push(r);
                loop(i + 1);
            });
        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
            callback(results);
        }
    }

    loop(0);
}&lt;/pre&gt;

&lt;p&gt;之前我们扩展了XMLHttpRequest，提供了一个beginReceive方法。于是我们在回调中驰骋，最终实现了beginReceiveMany方法。如果用AsyncIterator则会方便一些，只可惜它对“带有返回值”的异步操作支持并不友好，因此还是有点绕：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginReceiveMany2 = &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;results = [];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function&lt;/span&gt;(ai) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
            req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
            req.beginReceive(&lt;span style="background-color: yellow"&gt;ai.callback()&lt;/span&gt;);
            yield 0;
            results.push(&lt;span style="background-color: yellow"&gt;ai.result()&lt;/span&gt;);
        }
    }
    
    AsyncIterator.beginInvoke(generator, &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        callback(results);
    });
}&lt;/pre&gt;

&lt;p&gt;幸好有JavaScript的闭包在，总体来说还算方便。在这个例子中，AsyncIterator会得到beginReceive从回调函数中提交上来的结果，然后代码便可以从result方法里获得并保存。&lt;/p&gt;

&lt;h1&gt;更多&lt;/h1&gt;

&lt;p&gt;AsyncIterator虽好，只可惜只能在FireFox里使用，这自然无法推广。幸好我们还可以用&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;，而且这一切都没有Jscex来的简单，正如之前所提到的那样，在Jscex中一段移动HTML元素的动画只需这样写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
        e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sleep(50));
    }

    e.style.left = endPos.x;
    e.style.top = endPos.y;
}));&lt;/pre&gt;

&lt;p&gt;批量请求数据也是最为直观的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;getMultiContentAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;result = [];

    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;content = &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(getContentAsync(urls[i]));
        result.push(content);
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;result;
}));&lt;/pre&gt;

&lt;p&gt;甚至&lt;a href="http://files.zhaojie.me/demos/jscex-hanoi/hanoi.html?n=5"&gt;汉诺塔&lt;/a&gt;的解法，也完全是最最直观的递归算法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;hanoiAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(n, a, b, c) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(hanoiAsync(n - 1, a, c, b));
    }

    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveDishAsync(n, a, b));

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(hanoiAsync(n - 1, c, b, a));
    }
}));&lt;/pre&gt;

&lt;p&gt;关于异步编程模型的演变，以及Jscex的更多内幕，本周三下午4点我将在创新院内部开展一次分享会，&lt;span style="color:red;"&gt;欢迎创新院外的朋友前来一起交流&lt;/span&gt;。这也是我在即将举办的&lt;a href="http://sd2china.csdn.net/"&gt;SD 2.0&lt;/a&gt;大会上的一次预演。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html#comments</comments>
      <pubDate>Mon, 29 Nov 2010 14:25:50 GMT</pubDate>
      <lastBuildDate>Mon, 29 Nov 2010 17:18:39 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/news/">新闻信息</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>关于即将到来的“演出季”以及Jscex类库</title>
      <link>http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html</link>
      <guid>http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html</guid>
      <description>&lt;p&gt;又到了一年一度的“演出季”，接下来将是各式会议扑面而来的一个月。作为“与会爱好者”我自然也进入了繁忙的准备工作。接下来我将在&lt;a href="http://tup.csdn.net/"&gt;TUP&lt;/a&gt;（11月27日）、&lt;a href="http://www.net-china.org/"&gt;2010年第二届.NET技术大会&lt;/a&gt;（12月4~5日）以及&lt;a href="http://sd2china.csdn.net/"&gt;CSDN软件开发2.0大会&lt;/a&gt;（12月9~10日）上与大家分享四场演讲。不过除了一场是关于Windows并发编程的基础以外，其余三场的话题都是围绕“微软在异步编程方面的演变”。在这场演讲中，我还会引入一个与该话题密切相关的JavaScript类库：Jscex。&lt;/p&gt;

&lt;p&gt;并发编程已经成为我们无法回避的一点，但是传统的编程语言在并发、异步等方面的支持非常有限，除了一些面向并发的编程语言（如Erlang），我们只能使用回调的方式，这样代码则被拆得支离破碎。如今的一些新语言，如F#，Scala，Go以及未来的C#都会在这方面有直接的支持。就我个人而言，用过了这些语言之后，就几乎无法回头，因为我实在无法体会到编程的快感。&lt;/p&gt;

&lt;p&gt;如今微软的.NET平台在异步编程领域已经开始发力，从最初十分原始的Begin/End的异步模型和基于事件的异步模型，到后来&lt;a href="http://blog.zhaojie.me/2010/03/async-and-parallel-design-patterns-in-fsharp-1-parallelizing-cpu-and-io-computations.html"&gt;F#中的异步工作流&lt;/a&gt;，.NET 4.0中的&lt;a href="http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html"&gt;Observable及响应式框架&lt;/a&gt;，以及&lt;a href="http://blog.zhaojie.me/2010/10/pdc2010-the-future-of-csharp-and-vb-by-anders-hejlsberg-1.html"&gt;未来C#中将要出现的异步特性&lt;/a&gt;，.NET平台上涌现了各种与并发和异步编程的机制。未来几场演讲的主要话题，便是关于这些异步编程模型的演变。&lt;/p&gt;

&lt;p&gt;只可惜，在某些和异步密切相关的平台上，例如使用JavaScript的浏览器平台，开发人员还是不得不利用最传统的开发模型。响应式框架通过引入一个“推模型”改进了这一点，不过我最欣赏的异步编程支持还是F#上的异步工作流。首先，它是一套“类库”（基于F#中的计算表达式特性）；其次，它让异步编程变得像传统的顺序式开发那样简单。有天我突然意识到，JavaScript其实提供一个很强大的机制：它可以使用toString方法得到一个函数的原始代码，于是我们可以对此解析并生成新的代码，最后使用eval得到新的函数。通过这个手法，JavaScript语言几乎得到了无限的灵活性。&lt;/p&gt;

&lt;p&gt;于是在未来的演讲中，我将会展示一个JavaScript类库的原型：&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;（目前还未提交任何代码）。Jscex是&lt;font color="#ff0000"&gt;J&lt;/font&gt;ava&lt;font color="#ff0000"&gt;S&lt;/font&gt;cript &lt;font color="#ff0000"&gt;C&lt;/font&gt;omputation &lt;font color="#ff0000"&gt;Ex&lt;/font&gt;pressions的缩写，即“JavaScript的计算表达式”。计算表达式的原理，便是将一段顺序式的代码，重新编译成另外一个表达式，它便是F#中异步工作流的基础。&lt;/p&gt;

&lt;p&gt;例如，它会将下面这段JavaScript函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(urlA, urlB) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;reqA = &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sendRequest(urlA, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthA = reqA.responseText.length;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;reqB = &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sendRequest(urlB, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthB = reqB.responseText.length;

    &lt;span style="color: blue"&gt;return &lt;/span&gt;lengthA + lengthB;
}&lt;/pre&gt;

&lt;p&gt;编译成另一段代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function&lt;/span&gt; (urlA, urlB) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.sendRequest(urlA, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;), &lt;span style="color: blue"&gt;function&lt;/span&gt;(reqA) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthA = reqA.responseText.length;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.sendRequest(urlB, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;), &lt;span style="color: blue"&gt;function&lt;/span&gt;(reqB) {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthB = reqB.responseText.length;
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return(lengthA + lengthB);
            });
        });
    });
};&lt;/pre&gt;

&lt;p&gt;对于$async这个构造器来说，$await便是一个bind函数，在此处则会形成一个回调。此外，Jscex也必须能够正确处理循环操作，例如这个&lt;a href="http://files.zhaojie.me/demos/jscex-prototype/clock.html"&gt;时钟示例&lt;/a&gt;（建议使用IE 9，Chrome或Firefox等支持canvas的浏览器观看）其实是这样实现的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;drawClockAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(interval) {
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        drawClock(&lt;span style="color: blue"&gt;new &lt;/span&gt;Date());
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sleep(interval));
    }
}));

Jscex.Async.start(drawClockAsync(1000));&lt;/pre&gt;

&lt;p&gt;对于时钟来说，我们往往会不断使用window.setTimeout来更新界面。不过有了Jscex，一切都变得“顺其自然”。我们只要写一个“死循环”，在需要“等待”的时候，直接$await一个sleep操作即可。这种看似“阻塞”的代码，其实最终会被重新编译成新的函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function&lt;/span&gt;(interval) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.While(
            &lt;span style="color: blue"&gt;function&lt;/span&gt;() { &lt;span style="color: blue"&gt;return true&lt;/span&gt;; },
            $async.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                drawClock(&lt;span style="color: blue"&gt;new &lt;/span&gt;Date);
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.sleep(interval), &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
                });
            })
        );
    });
};&lt;/pre&gt;

&lt;p&gt;实际上，它们都是异步的。&lt;/p&gt;

&lt;p&gt;其实在浏览器上有太多的异步场景。例如一个AJAX请求，或者是一段动画效果。这里我还准备了&lt;a href="http://files.zhaojie.me/demos/jscex-prototype/animation.html"&gt;第二个示例&lt;/a&gt;，其中有一段移动元素的方法（将元素e在duration时间内从startPos移动至endPos）是这样编写的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    e.style.left = startPos.x;
    e.style.top = startPos.y;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;time = 0;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(time &amp;lt; duration) {
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sleep(50));
        time = time + 50;
        e.style.left = startPos.x + (endPos.x - startPos.x) * time / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * time / duration;
    }
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;moveBox = document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;moveBox&amp;quot;&lt;/span&gt;);
Jscex.Async.start(moveAsync(moveBox, { x: 0, y: 0 }, { x: 300, y: 0 }, 1000));&lt;/pre&gt;

&lt;p&gt;没有回调，只有最普通的顺序式编程。甚至我们还可以将moveAsync方法组合到另一个方法中去：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveSquareAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 100, y: 100 }, { x: 400, y: 100 }, 1000));
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 400, y: 100 }, { x: 400, y: 400 }, 1000));
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 400, y: 400 }, { x: 100, y: 400 }, 1000));
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 100, y: 400 }, { x: 100, y: 100 }, 1000));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;moveSquareBox = document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;moveSquareBox&amp;quot;&lt;/span&gt;);
Jscex.Async.start(moveSquareAsync(moveSquareBox));&lt;/pre&gt;

&lt;p&gt;如何让一个元素沿方形移动？“连续执行”四遍moveAsync方法即可。当然，moveSquareAsync方法最终还是由“回调”组成的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
                    });
                });
            });
        });
    });
};&lt;/pre&gt;

&lt;p&gt;我以前在许多地方说过，目前浏览器上的开发效率已经受限于JavaScript的语言特性了。不过，正如我刚才所提到的那样，JavaScript语言的灵活性几乎是无穷的。我认为，Jscex会是一个突破。当然，现在您看到的演示只不过是个原型，如果要提供一个完整的解决方案还有太多的路要走。例如现在的Jscex还不支持for，if，try...catch等常见语言特性，代码也丑陋得很。与此对应的Jscex.Async也只是个初步的模型，且只有寥寥无几的辅助方法。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html#comments</comments>
      <pubDate>Mon, 22 Nov 2010 12:30:57 GMT</pubDate>
      <lastBuildDate>Mon, 29 Nov 2010 14:28:28 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>使用Narcissus解析JavaScript代码</title>
      <link>http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html</link>
      <guid>http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html</guid>
      <description>&lt;p&gt;最近在做一个有关JavaScript的实验，需要在客户端将JavaScript代码解析为一棵语法树。换句话说，就是一个用JavaScript实现的JavaScript解析器。这方面的选择有很多，常见的yacc、lex或是bison等等都有JavaScript的版本，使用ANTLR也可以将生成目标设为JavaScript。不过我不想在这方面耗费太多时间，自然想找个现成的工具，于是最终我将目标放在了Narcissus上。&lt;/p&gt;

&lt;p&gt;Narcissus是一个JavaScript引擎，完全使用JavaScript编写，不过利用了&lt;a href="http://en.wikipedia.org/wiki/SpiderMonkey_(JavaScript_engine)"&gt;SpiderMonkey&lt;/a&gt;的一些扩展，因此无法直接在仅仅实现了ECMAScript 3的引擎上执行（例如各浏览器）。从&lt;a href="http://en.wikipedia.org/wiki/Narcissus_%28JavaScript_engine%29"&gt;它的Wikipedia页面上得知&lt;/a&gt;，Narcissus由SpiderMonkey的作者&lt;a href="http://en.wikipedia.org/wiki/Brendan_Eich"&gt;Brendan Eich&lt;/a&gt;开发，名称来源于&lt;a href="http://en.wikipedia.org/wiki/Narcissus_(mythology)"&gt;希腊神话中爱上自己倒影的人物&lt;/a&gt;，和“JavaScript编写的JavaScript引擎”的概念契合（真是太有文化了）。此外，Firefox有一个Zaphod插件，可以将浏览器的JavaScript引擎替换为Narcissus。&lt;/p&gt;

&lt;p&gt;Narcissus是个十分简单的JavaScript引擎，可以用来做一些JavaScript语言新特性的探索工作。它几乎不做任何优化，因此不能与其他引擎比拼性能，但很显然它包含完整的JavaScript分析器，正好为我所用。首先，&lt;a href="https://github.com/mozilla/narcissus"&gt;从Github上下载它的源代码&lt;/a&gt;，其中包括六个文件，而我只需要其中的三个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;jsdef.js：包含了Narcissus.definitions组件，各种Token定义等等。 &lt;/li&gt;

  &lt;li&gt;jslex.js：包含了Narcissus.lexer组件，分词器。 &lt;/li&gt;

  &lt;li&gt;jsparse.js：包含了Narcissus.parser，分析器。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;之前提到过，Narcissus不能直接在浏览器上运行，因此我们还必须对它进行修改。首先，是在jsdefs.js文件中，我们需要将开头的一段利用Object.create方法的定义：&lt;/p&gt;

&lt;pre class="code"&gt;(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;builderTypes = Object.create(&lt;span style="color: blue"&gt;null&lt;/span&gt;, {
        ...
    });

    ...

    &lt;span style="color: blue"&gt;var &lt;/span&gt;narcissus = {
        ...&lt;span style="color: blue"&gt;
    &lt;/span&gt;};

    Narcissus = narcissus;
})();&lt;/pre&gt;

&lt;p&gt;替换成直接的声明：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Narcissus = { };&lt;/pre&gt;

&lt;p&gt;其次还是在jsdefs.js中，我们要改变defineProperty和defineGetter的实现：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;defineGetter(obj, prop, fn, dontDelete, dontEnum) {
    Object.defineProperty(...);
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
    Object.defineProperty(...);
}&lt;/pre&gt;

&lt;p&gt;Object的defineProperty和defineGetter方法也是SpiderMonkey的扩展，我们要把它们修改为“直接赋值”的版本：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;defineGetter(obj, prop, fn, dontDelete, dontEnum) {
    obj[prop] = fn;
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
    obj[prop] = val;
}&lt;/pre&gt;

&lt;p&gt;当然，这么做与之前的效果并不等价，不过并不影响代码的使用。您可以从jsparse.js文件中找到使用了这两个方法的地方。&lt;/p&gt;

&lt;p&gt;现在您就可以在一个页面里引入这三个JavaScript文件，并Narcissus.parser分析JavaScript代码了。Narcissus几乎没有说明文档，不过从代码中找到它的使用方法并不困难。例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;parseSelf() { 
    &lt;span style="color: blue"&gt;var &lt;/span&gt;builder = &lt;span style="color: blue"&gt;new &lt;/span&gt;Narcissus.parser.DefaultBuilder();
    &lt;span style="color: blue"&gt;return &lt;/span&gt;Narcissus.parser.parse(builder, parseSelf.toString(), &lt;span style="color: maroon"&gt;&amp;quot;temp&amp;quot;&lt;/span&gt;, 1);
}

document.write(&lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;pre&amp;gt;&amp;quot; &lt;/span&gt;+ parseSelf() + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;textarea id="parseSelf_tree" style="display:none;"&gt;&amp;lt;pre&amp;gt;{
    type: SCRIPT,
    children: {
        type: FUNCTION,
        body: {
            type: SCRIPT,
            children: {
                type: VAR,
                children: {
                    type: IDENTIFIER,
                    children: ,
                    end: 38,
                    initializer: {
                        type: NEW,
                        children: {
                            type: DOT,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 55,
                                    lineno: 2,
                                    start: 46,
                                    tokenizer: [object Object],
                                    value: Narcissus
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 62,
                                    lineno: 2,
                                    start: 56,
                                    tokenizer: [object Object],
                                    value: parser
                                },
                                end: 62,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 77,
                                lineno: 2,
                                start: 63,
                                tokenizer: [object Object],
                                value: DefaultBuilder
                            },
                            end: 77,
                            lineno: 2,
                            parenthesized: true,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },
                        end: 77,
                        lineno: 2,
                        start: 41,
                        tokenizer: [object Object],
                        value: new
                    },
                    lineno: 2,
                    name: builder,
                    readOnly: false,
                    start: 31,
                    tokenizer: [object Object],
                    value: builder
                },
                destructurings: ,
                end: 38,
                lineno: 2,
                start: 27,
                tokenizer: [object Object],
                value: var
            },{
                type: RETURN,
                children: ,
                end: 90,
                lineno: 3,
                start: 84,
                tokenizer: [object Object],
                value: {
                    type: CALL,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 100,
                                lineno: 3,
                                start: 91,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 107,
                                lineno: 3,
                                start: 101,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 107,
                            lineno: 3,
                            start: 91,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 113,
                            lineno: 3,
                            start: 108,
                            tokenizer: [object Object],
                            value: parse
                        },
                        end: 113,
                        lineno: 3,
                        start: 91,
                        tokenizer: [object Object],
                        value: .
                    },{
                        type: LIST,
                        children: {
                            type: IDENTIFIER,
                            children: ,
                            end: 121,
                            lineno: 3,
                            start: 114,
                            tokenizer: [object Object],
                            value: builder
                        },{
                            type: CALL,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 132,
                                    lineno: 3,
                                    start: 123,
                                    tokenizer: [object Object],
                                    value: parseSelf
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 141,
                                    lineno: 3,
                                    start: 133,
                                    tokenizer: [object Object],
                                    value: toString
                                },
                                end: 141,
                                lineno: 3,
                                start: 123,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: LIST,
                                children: ,
                                end: 142,
                                lineno: 3,
                                start: 141,
                                tokenizer: [object Object],
                                value: (
                            },
                            end: 142,
                            lineno: 3,
                            start: 123,
                            tokenizer: [object Object],
                            value: (
                        },{
                            type: STRING,
                            children: ,
                            end: 151,
                            lineno: 3,
                            start: 145,
                            tokenizer: [object Object],
                            value: temp
                        },{
                            type: NUMBER,
                            children: ,
                            end: 154,
                            lineno: 3,
                            start: 153,
                            tokenizer: [object Object],
                            value: 1
                        },
                        end: 154,
                        lineno: 3,
                        start: 113,
                        tokenizer: [object Object],
                        value: (
                    },
                    end: 154,
                    lineno: 3,
                    start: 91,
                    tokenizer: [object Object],
                    value: (
                }
            },
            end: 90,
            funDecls: ,
            id: 0,
            lineno: 1,
            start: 21,
            tokenizer: [object Object],
            value: {,
            varDecls: {
                type: IDENTIFIER,
                children: ,
                end: 38,
                initializer: {
                    type: NEW,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 55,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 62,
                                lineno: 2,
                                start: 56,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 62,
                            lineno: 2,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 77,
                            lineno: 2,
                            start: 63,
                            tokenizer: [object Object],
                            value: DefaultBuilder
                        },
                        end: 77,
                        lineno: 2,
                        parenthesized: true,
                        start: 46,
                        tokenizer: [object Object],
                        value: .
                    },
                    end: 77,
                    lineno: 2,
                    start: 41,
                    tokenizer: [object Object],
                    value: new
                },
                lineno: 2,
                name: builder,
                readOnly: false,
                start: 31,
                tokenizer: [object Object],
                value: builder
            }
        },
        children: ,
        end: 158,
        functionForm: 0,
        lineno: 1,
        name: parseSelf,
        params: ,
        start: 0,
        tokenizer: [object Object],
        value: function
    },
    funDecls: {
        type: FUNCTION,
        body: {
            type: SCRIPT,
            children: {
                type: VAR,
                children: {
                    type: IDENTIFIER,
                    children: ,
                    end: 38,
                    initializer: {
                        type: NEW,
                        children: {
                            type: DOT,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 55,
                                    lineno: 2,
                                    start: 46,
                                    tokenizer: [object Object],
                                    value: Narcissus
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 62,
                                    lineno: 2,
                                    start: 56,
                                    tokenizer: [object Object],
                                    value: parser
                                },
                                end: 62,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 77,
                                lineno: 2,
                                start: 63,
                                tokenizer: [object Object],
                                value: DefaultBuilder
                            },
                            end: 77,
                            lineno: 2,
                            parenthesized: true,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },
                        end: 77,
                        lineno: 2,
                        start: 41,
                        tokenizer: [object Object],
                        value: new
                    },
                    lineno: 2,
                    name: builder,
                    readOnly: false,
                    start: 31,
                    tokenizer: [object Object],
                    value: builder
                },
                destructurings: ,
                end: 38,
                lineno: 2,
                start: 27,
                tokenizer: [object Object],
                value: var
            },{
                type: RETURN,
                children: ,
                end: 90,
                lineno: 3,
                start: 84,
                tokenizer: [object Object],
                value: {
                    type: CALL,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 100,
                                lineno: 3,
                                start: 91,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 107,
                                lineno: 3,
                                start: 101,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 107,
                            lineno: 3,
                            start: 91,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 113,
                            lineno: 3,
                            start: 108,
                            tokenizer: [object Object],
                            value: parse
                        },
                        end: 113,
                        lineno: 3,
                        start: 91,
                        tokenizer: [object Object],
                        value: .
                    },{
                        type: LIST,
                        children: {
                            type: IDENTIFIER,
                            children: ,
                            end: 121,
                            lineno: 3,
                            start: 114,
                            tokenizer: [object Object],
                            value: builder
                        },{
                            type: CALL,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 132,
                                    lineno: 3,
                                    start: 123,
                                    tokenizer: [object Object],
                                    value: parseSelf
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 141,
                                    lineno: 3,
                                    start: 133,
                                    tokenizer: [object Object],
                                    value: toString
                                },
                                end: 141,
                                lineno: 3,
                                start: 123,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: LIST,
                                children: ,
                                end: 142,
                                lineno: 3,
                                start: 141,
                                tokenizer: [object Object],
                                value: (
                            },
                            end: 142,
                            lineno: 3,
                            start: 123,
                            tokenizer: [object Object],
                            value: (
                        },{
                            type: STRING,
                            children: ,
                            end: 151,
                            lineno: 3,
                            start: 145,
                            tokenizer: [object Object],
                            value: temp
                        },{
                            type: NUMBER,
                            children: ,
                            end: 154,
                            lineno: 3,
                            start: 153,
                            tokenizer: [object Object],
                            value: 1
                        },
                        end: 154,
                        lineno: 3,
                        start: 113,
                        tokenizer: [object Object],
                        value: (
                    },
                    end: 154,
                    lineno: 3,
                    start: 91,
                    tokenizer: [object Object],
                    value: (
                }
            },
            end: 90,
            funDecls: ,
            id: 0,
            lineno: 1,
            start: 21,
            tokenizer: [object Object],
            value: {,
            varDecls: {
                type: IDENTIFIER,
                children: ,
                end: 38,
                initializer: {
                    type: NEW,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 55,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 62,
                                lineno: 2,
                                start: 56,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 62,
                            lineno: 2,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 77,
                            lineno: 2,
                            start: 63,
                            tokenizer: [object Object],
                            value: DefaultBuilder
                        },
                        end: 77,
                        lineno: 2,
                        parenthesized: true,
                        start: 46,
                        tokenizer: [object Object],
                        value: .
                    },
                    end: 77,
                    lineno: 2,
                    start: 41,
                    tokenizer: [object Object],
                    value: new
                },
                lineno: 2,
                name: builder,
                readOnly: false,
                start: 31,
                tokenizer: [object Object],
                value: builder
            }
        },
        children: ,
        end: 158,
        functionForm: 0,
        lineno: 1,
        name: parseSelf,
        params: ,
        start: 0,
        tokenizer: [object Object],
        value: function
    },
    id: 0,
    lineno: 1,
    tokenizer: [object Object],
    varDecls: 
}&amp;lt;/pre&amp;gt;&lt;/textarea&gt; 

&lt;p&gt;在JavaScript中调用一个函数的toString方法会得到它的代码，于是执行上面这段代码会打印出&lt;a href="javascript:;" onclick="__showInNewWindow(document.getElementById('parseSelf_tree').value)"&gt;parseSelf方法的语法树&lt;/a&gt;。剩下的我就不多说了，爱玩的同学自然知道可以做什么。&lt;/p&gt;

&lt;p&gt;补：经过实验，Narcissus还是过于依赖SpiderMonkey引擎的特性，如果要在IE上运行还是需要修改更多内容。此外，最新的Narcissus源码还有一些bug，如果您想要使用合适的实现，不妨参考&lt;a href="http://www.neilmix.com/narrativejs/doc/"&gt;NarrativeJS&lt;/a&gt;中旧版的Narcissus代码。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html#comments</comments>
      <pubDate>Wed, 17 Nov 2010 10:08:03 GMT</pubDate>
      <lastBuildDate>Mon, 22 Nov 2010 11:24:36 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <title>异步编程与响应式框架</title>
      <link>http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html</link>
      <guid>http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html</guid>
      <description>&lt;p&gt;本文原发表于《程序员》杂志9月刊，现将全文公开至此。前几天的技术交流会上我也讲了这部分内容，配合着看会有更好效果。&lt;/p&gt;

&lt;p&gt;此外我想再多几句。《程序员》杂志在某些方面必须尽快做出调整。就拿我这篇文章来说，代码除了被三栏的版式搞得支离破碎以外，其中必要的空格也莫名奇妙地少了许多。我认为《程序员》如今在内容方面已经有了显著提高，但是在这种“表面”工夫上也得抓紧才好，这也是国内同类杂志中“最专业者”所应有的风范。&lt;/p&gt;

&lt;h1&gt;前言&lt;/h1&gt;

&lt;p&gt;异步操作是强大的，它是许多高伸缩性架构的基石。异步操作在许多情况下是必须的，例如在客户端保持用户界面的响应能力，以及在日益兴起的云计算场景中。但是，异步编程又是十分困难的，它让这让许多程序员敬而远之。因此，越来越多的编程语言都对异步编程提供了相当程度的支持，其中的典型代表便是F#中的异步工作流以及Scala的Actor模型。不过目前的一些主流编程语言，如C#或是JavaScript，它们在设计之时并没有在异步编程上考虑太多，我们便会根据它们的语言特性，提供合适的异步编程模型及其实现。而本文介绍的便是其中一例：响应式编程（Reactive Programming）模型及响应式框架（Reactive Framework，简称Rx）。&lt;/p&gt;

&lt;h1&gt;异步编程的难点&lt;/h1&gt;

&lt;p&gt;异步编程之所以困难，主要有三大难点。&lt;/p&gt;  &lt;p&gt;首先是对于状态的维护。在普通编程中，我们已经习惯了根据各种状态采取不同做法的编程方式。在异步编程中，状态对于操作的影响则往往更为复杂。例如，我们在编写一个鼠标“拖动及绘图”的行为时，一般会采用这样的逻辑：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;在MouseDown事件中将isDragging标记设为true，表示“拖动开始”，并记录当前鼠标位置prevPos。 &lt;/li&gt;    &lt;li&gt;在MouseUp事件中将isDragging标记设为false，表示“拖动结束”。 &lt;/li&gt;    &lt;li&gt;在MouseMove事件中检查isDragging标记，如果为true，根据鼠标当前位置currPos和之前记录的prevPos进行绘图，并将currPos的值写入prevPos。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;仅在这样一个最基本的场景中，我们便需要编写三个事件处理器（Event Handler），控制isDragging，prevPos等外部状态，并根据这些状态决定事件触发时的效果。这样的例子数不胜数，尤其是在各式拖放操作中，几乎都会涉及大量状态的控制（例如，判断物体是否进入某个特定区域）。&lt;/p&gt;  &lt;p&gt;异步编程的另一个难点，在于异步操作之间的组合及交互。例如在如上的简单拖放操作中，我们便涉及到了MouseDown，MouseUp及MouseMove三个事件。从某些角度来说，客户端的UI事件还是比较容易处理的，因为它们往往都是在单一线程上依次执行。但是在另外一些场景中，如云计算时，我们往往会同时发起多个异步操作，并根据这些操作的结果进行后续处理，甚至还会有一个额外的超时监控，这样便很有可能会出现并发操作的竞争（Race）情况，这将会成为程序复杂度的灾难。&lt;/p&gt;  &lt;p&gt;此外，异步操作还会破坏“代码局部性（Code Locality）”，这可能也是异步操作中最为常见的阻碍。程序员早已习惯了“线性”地表达逻辑，但即便是多个顺序执行的异步操作，也会因为大量的回调函数而将算法拆得支离破碎，更何况还会出现各种循环及条件判断。同时，在线性的代码中，我们可以使用“局部变量”保存状态，而在编写异步代码时则需要手动地在多个函数中传递状态。此外，由于逻辑被拆分至多个方法，因此我们也无法使用传统的try/catch进行统一异常处理。&lt;/p&gt;

&lt;h1&gt;推模型与拉模型&lt;/h1&gt;

&lt;p&gt;平时我们使用最多的便是“交互式（Interactive）”的编程方式，采用的是组件之间的相互调用来表现逻辑。例如，对象A向对象B请求数据并等待返回，待对象B完成并返还数据之后A才继续进行后面的操作。交互式编程的一个典型应用便是GoF23中的迭代器（Iterator）模式，它在.NET中的实现为IEnumerable及IEnumerator接口，例如：&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;void &lt;/span&gt;Traverse(&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; source)
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;etor = source.GetEnumerator();
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(etor.MoveNext())
    {
        &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(etor.Current);
    }
}&lt;/pre&gt;

&lt;p&gt;为了更好地说明问题，这里我们将标准的foreach操作展开为传统的迭代器使用形式，并省略了using语句。在使用时，我们先调用一个IEnumerable对象的GetEnumerator方法，获得一个迭代器，再根据MoveNext及Current进行遍历。在调用MoveNext时，迭代器会去“准备”下一个元素，并根据存在与否返回true或者false。试想，如果其中某个MoveNext的“准备”工作涉及到一个耗时较长的操作，则迭代器的使用者也必须眼巴巴地等待其返回。&lt;/p&gt;

&lt;p&gt;这是一种“拉（Pull）”模型，数据由消费者（Consumer）从生产者（Producer）那里主动“拉”来。这是一种同步的交互方式，数据消费者会依赖于数据生产者的表现。这就好比我们去食堂吃饭时必须主动去取餐，此时则必须从队伍的最后排起，我们什么时候能结束等待并进行下一步操作（即“吃饭”），则要看食堂的生产速度如何。很显然，有些时候这种交互方式是不可接受的，例如我们在实现一个搜索引擎的“关键字提示”功能时，不可能让用户在输入一个字符后，必须等待远程的提示请求返回才能继续输入下一个字符。&lt;/p&gt;

&lt;p&gt;而与交互式编程对应的便是“响应式（Reactive）”编程。响应式编程是一种基于“改变”的编程方式。例如在交互式编程中，A = B + C这样的表达式意味着将B与C之和赋给A，而此后B与C的改变都与A无关。而在响应式编程中，A会去“响应”B或C的变化，即一旦B或C改变之后，A的值也会随之变化。响应式编程的一个典型应用便是GoF23中的观察者（Observer）模式。与迭代器的IEnumerable/IEnumerator不同，在之前的.NET框架中并没有对这样一种编程模型指定“标准化（Formallized）”接口，不过在.NET 4.0的基础类库中增加了IObservable及IObserver接口，签名如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IObservable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;out &lt;/span&gt;T&amp;gt;
{
    &lt;span style="color: #2b91af"&gt;IDisposable &lt;/span&gt;Subscribe(&lt;span style="color: #2b91af"&gt;IObserver&lt;/span&gt;&amp;lt;T&amp;gt; observer);
}

&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IObserver&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;in &lt;/span&gt;T&amp;gt;
{
    &lt;span style="color: blue"&gt;void &lt;/span&gt;OnCompleted();
    &lt;span style="color: blue"&gt;void &lt;/span&gt;OnError(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;error);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;OnNext(T value);
}&lt;/pre&gt;

&lt;p&gt;如果我们仔细比较“迭代器”与“观察者”的标准化接口，则会发现它们是完全“对偶（dual）”的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IEnumerable.GetEnumerator方法“输出”一个IEnumerater对象；IObservable.Subscribe方法“输入”一个IObserver对象。 &lt;/li&gt;

  &lt;li&gt;在遍历元素用尽时，IEnumerator.MoveNext方法返回false；在响应内容用尽时，IObserver.OnCompleted方法被调用。 &lt;/li&gt;

  &lt;li&gt;在有新元素需要遍历时，IEnumerator.MoveNext方法返回true，并通过Current属性“输出”；在有新元素需要响应时，IObserver.OnNext方法被调用，并通过参数“输入”。 &lt;/li&gt;

  &lt;li&gt;在出现错误时，IEnumerator.MoveNext方法会“抛出”一个异常；在出现错误时，IObserver.OnError方法会被调用，并通过参数“接受”异常信息。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;至于IObservable.Subscribe方法返回的IDisposable对象，则用于“退定”操作，即让输入的IObserver对象再也不需要继续响应IObservable对象的新元素了。&lt;/p&gt;

&lt;p&gt;从比较中可以看出，如果说IEnumerator对象是由数据消费者使用的话，那么IObserver对象则是由数据的生产者，即IObservable对象使用的。换句话说，数据是由数据的生产者“推”给数据消费者的，是一种“推（Push）”模型。在这种异步的交互方式中，数据消费者不必依赖于数据生产者的表现。这就好比我们去饭店吃饭，点菜后便可坐下和同伴聊聊天或是用手机上上网，而作为菜品的生产者，饭店，则会在产出之后主动端上桌来。这么做无疑解放了数据的消费者，例如用户可以在文本框里不断地输入字符，而只需等远程服务器将提示结果“推”给客户端后再显示即可。&lt;/p&gt;

&lt;p&gt;许多模型都可以统一至标准的生产者接口IObservable上，如MouseMove事件便可以认做是“永不停止的MouseEventArgs对象的生产者”。而另一方面，单个异步操作则可以被视为“只产出单个数据便结束的生产者”。&lt;/p&gt;

&lt;h1&gt;LINQ to Observable&lt;/h1&gt;

&lt;p&gt;在许多人眼中，C# 3.0中新增的LINQ特性只是一种用于操作数据的DSL，它的主要作用也仅仅是针对IEnumerable或是IQueryable的数据操作。事实上，LINQ本身的能力远不止此。LINQ是一种非常简单的语言特性，编译器只是将LINQ查询语句转化为“字面等价”的“LINQ标准方法”调用（如Where，Select等等）和“和Lambda表达式”参数（如x =&amp;gt; x &amp;gt; 0）而已。但是这里的关键便是“字面等价”四个字，LINQ本身并不规定“LINQ标准方法”是对象的实例方法还是扩展方法，“Lambda表达式”是构造出一个匿名函数还是表达式树，这一切都是由利用LINQ的类库来决定的。因此，微软能够基于LINQ实现了PLINQ这样的并行类库。直到最近的访谈中，LINQ的设计者Erik Meijer依旧认为LINQ还是被低估了，他们自己也还在继续挖掘LINQ的更多能力。&lt;/p&gt;

&lt;p&gt;虽然LINQ本身在语法上只是一些方法调用，但是它在语义上是针对数据流的一系列操作，因此LINQ to Object，LINQ to SQL以及PLINQ可以认为是最自然，最符合LINQ语义的应用。如今，微软的“云编程能力团队（Cloud Programmability Team）”在其“响应式框架（Reactive Framework，简称Rx）”提供了LINQ to Observable，这又是另一个LINQ的经典使用案例。IObservable本身可以被认为是一股“推送数据”的数据流，因此也可以对其进行“过滤”或是“投影”等操作，这便是LINQ的应用场景，LINQ to Observable是对IObservable接口实现的一系列LINQ标准方法。因此，我们可以认为，LINQ to Observable是一套与LINQ to Object对偶的类库，事实上在响应式框架中，还有一套与LINQ to Queryable对偶的LINQ to Qbservable，请注意第一个字母是Q，我们可以把它看作是LINQ to Observable的“远程查询”版本。&lt;/p&gt;

&lt;p&gt;利用LINQ to Object可以编写出声明式（表示“做什么”）的代码，其可读性往往远高于等价的命令式（表示“怎么做”）代码。LINQ to Observable也有类似的效果。假设现在有一个需求：利用ADWS键控制小球的位置。传统的写法可能是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;void &lt;/span&gt;OnKeyPress(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af"&gt;KeyPressEventArgs &lt;/span&gt;e)
{
    // 如果游戏已经开始
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(isPlaying) &lt;span style="color: green"&gt;&lt;/span&gt;
    {
        &lt;span style="color: green"&gt;// 向左且小球没有超出边界&lt;/span&gt;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(e.KeyChar == &lt;span style="color: #a31515"&gt;'a'&lt;/span&gt; &amp;amp;&amp;amp; ball.Left &amp;gt; 0)
        {
            ball.Left -= 5;
        }
        &lt;span style="color: green"&gt;// 向上且小球没有超出边界&lt;/span&gt;
        &lt;span style="color: blue"&gt;else if &lt;/span&gt;(e.KeyChar == &lt;span style="color: #a31515"&gt;'w'&lt;/span&gt; &amp;amp;&amp;amp; ball.Top &amp;gt; 0)
        {
            ball.Top -= 5;
        }
        &lt;span style="color: blue"&gt;else &lt;/span&gt;... &lt;span style="color: green"&gt;&lt;/span&gt;
    }
    &lt;span style="color: blue"&gt;else &lt;/span&gt;...
}&lt;/pre&gt;

&lt;p&gt;由于KeyPress事件总是不断触发，因此我们只能它的事件处理器中进行判断各种状态，采取不同措施。而如果我们利用LINQ to Observable，则几乎是另外一种思维方式：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// 过滤出isPlaying时的keyPress事件
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;keyPress = GetKeyPress().Where(_ =&amp;gt; isPlaying);

&lt;span style="color: green"&gt;// 过滤出向左移动的事件
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveLeft = &lt;span style="color: blue"&gt;from &lt;/span&gt;ev &lt;span style="color: blue"&gt;in &lt;/span&gt;keyPress
               &lt;span style="color: blue"&gt;where &lt;/span&gt;ev.EventArgs.KeyChar == &lt;span style="color: #a31515"&gt;'a'
               &lt;/span&gt;&lt;span style="color: blue"&gt;where &lt;/span&gt;ball.Left &amp;gt; 0
               &lt;span style="color: blue"&gt;select &lt;/span&gt;ev;
moveLeft.Subscribe(_ =&amp;gt; ball.Left -= 5);

&lt;span style="color: green"&gt;// 过滤出向上移动的事件
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveTop = &lt;span style="color: blue"&gt;from &lt;/span&gt;ev &lt;span style="color: blue"&gt;in &lt;/span&gt;keyPress
              &lt;span style="color: blue"&gt;where &lt;/span&gt;ev.EventArgs.KeyChar == &lt;span style="color: #a31515"&gt;'w'
              &lt;/span&gt;&lt;span style="color: blue"&gt;where &lt;/span&gt;ball.Top &amp;gt; 0
              &lt;span style="color: blue"&gt;select &lt;/span&gt;ev;
moveTop.Subscribe(_ =&amp;gt; ball.Top -= 5);&lt;/pre&gt;

&lt;p&gt;我们可以将“KeyPress事件”视为“推送KeyPressEventArgs对象”这一数据流的数据源（由GetKeyPress方法返回），那么如今的代码便是使用LINQ过滤出“需要”的数据，并针对真正需要的那部分进行响应。这么做，便将“条件”与“操作”解耦，显著增强了代码的语义表达能力。事实上，只要补充一些辅助方法，可以利用LINQ表示更为完整复杂的逻辑。例如，微软咨询师Matthew Podwysocki便&lt;a href="http://codebetter.com/blogs/matthew.podwysocki/archive/2009/11/18/introduction-to-the-reactive-framework-part-v.aspx"&gt;在博客中展示过一段代码&lt;/a&gt;，基于LINQ to Observable实现了创建一个WebRequest对象，设置属性，异步发送及下载数据的一系列操作。&lt;/p&gt;

&lt;h1&gt;更多扩展&lt;/h1&gt;

&lt;p&gt;.NET基础类库针对IEnumerable定义了大量的函数式的辅助方法，开发人员可以直接将它们组合运用在项目中。除了标准的LINQ操作方法之外，响应式框架中同样定义了大量辅助方法，可以配合LINQ to Observable组合使用。例如本文开头所设想的鼠标“拖动及绘图”功能，便可以使用如下代码完成：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseMove = GetMouseMove();
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDiff = mouseMove.Zip(mouseMove.Skip(1), (prev, curr) =&amp;gt;
    &lt;span style="color: blue"&gt;new
    &lt;/span&gt;{
        PrevPos = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Point&lt;/span&gt;(prev.EventArgs.X, prev.EventArgs.Y),
        CurrPos = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Point&lt;/span&gt;(curr.EventArgs.X, curr.EventArgs.Y)
    });

&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDrag = &lt;span style="color: blue"&gt;from &lt;/span&gt;_ &lt;span style="color: blue"&gt;in &lt;/span&gt;GetMouseDown()
                &lt;span style="color: blue"&gt;from &lt;/span&gt;diff &lt;span style="color: blue"&gt;in &lt;/span&gt;mouseDiff.TakeUntil(GetMouseUp())
                &lt;span style="color: blue"&gt;select &lt;/span&gt;diff;
mouseDrag.Subscribe(diff =&amp;gt; DrawLine(diff.PrevPos, diff.CurrPos));&lt;/pre&gt;

&lt;p&gt;在这段代码中，我们首先将mouseMove事件使用Skip跳开一项，再与自身通过Zip方法组合成mouseDiff，这是一个输出相邻两次MouseMove事件坐标的数据源；接着，我们利用LINQ从触发MouseDown事件开始，向mouseDiff数据源获取每一项diff，直至（TakeUntil）触发MouseUp事件，以此生成最终的mouseDrag；最后再将绘图功能订阅至这个数据源上。您会发现此时我们已经无须手动维护操作过程中的各种状态了，从事件的“开始”到“结束”均使用响应式框架的辅助方法“声明”而来。&lt;/p&gt;

&lt;p&gt;以上便是一个利用了Skip，Zip，TakeUntil等辅助方法的例子。当然，这些辅助方法在IEnumerable上都有语义相同的对应操作，而在响应式框架中还有更多辅助方法是针对特性异步场景的。假设我们现在要编写一个即时翻译功能，同时发起三个请求，将中文分别翻译至英语、法语及西班牙语，并显示最先返回的两个结果（真是个奇怪的需求）。此外，我们不会在用户输入每个字符的时候便发起一个远程请求，而是在用户停止输入0.5秒之后才根据当前的输入框中的文字进行提示。于是我们可以编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;limit = &lt;span style="color: #2b91af"&gt;TimeSpan&lt;/span&gt;.FromSeconds(0.5);
&lt;span style="color: blue"&gt;var &lt;/span&gt;translate =
    &lt;span style="color: blue"&gt;from &lt;/span&gt;_ &lt;span style="color: blue"&gt;in &lt;/span&gt;GetKeyPress().Throttle(limit)
    &lt;span style="color: blue"&gt;let &lt;/span&gt;text = &lt;span style="color: blue"&gt;this&lt;/span&gt;.txtInput.Text
    &lt;span style="color: blue"&gt;where &lt;/span&gt;text.Length &amp;gt; 0
    &lt;span style="color: blue"&gt;let &lt;/span&gt;english = &lt;span style="color: #2b91af"&gt;Bing&lt;/span&gt;.Translate(text, &lt;span style="color: #a31515"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;let &lt;/span&gt;french = &lt;span style="color: #2b91af"&gt;Bing&lt;/span&gt;.Translate(text, &lt;span style="color: #a31515"&gt;&amp;quot;fr&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;let &lt;/span&gt;spanish = &lt;span style="color: #2b91af"&gt;Bing&lt;/span&gt;.Translate(text, &lt;span style="color: #a31515"&gt;&amp;quot;es&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;from &lt;/span&gt;result &lt;span style="color: blue"&gt;in &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Observable&lt;/span&gt;.Join(
       english.And(french).Then((en, fr) =&amp;gt;
           &lt;span style="color: blue"&gt;new &lt;/span&gt;{ English = en, French = fr, Spanish = &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt; }),
       english.And(spanish).Then((en, es) =&amp;gt;
           &lt;span style="color: blue"&gt;new &lt;/span&gt;{ English = en, French = &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt;, Spanish = es }),
       french.And(spanish).Then((fr, es) =&amp;gt;
           &lt;span style="color: blue"&gt;new &lt;/span&gt;{ English = &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt;, French = fr, Spanish = es }))
    &lt;span style="color: blue"&gt;select &lt;/span&gt;result;

translate.Subscribe(...);&lt;/pre&gt;

&lt;p&gt;这里用到了Throottle方法，它会过滤某个数据源的输出，确保在该数据源“静默”特定时间之后，才将最近的一条数据推送至外部。此外，这里还使用了Observable.Join方法控制多个数据源，根据返回结果的先后获得合适的结果。响应式框架提供了大量针对某种异步场景的辅助方法，例如用于定期推送数据的Interval方法，从一个数据源根据特定条件进行采样的Sample方法，合并多个数据源的ForkJoin方法，以及表示流程控制的For，While，If等等。这些方法内部会维护各种所需要的状态，为我们打理各种复杂的竞争情况，以此节省了开发人员的精力。&lt;/p&gt;

&lt;p&gt;如果这些还不能满足我们的要求，我们也可以根据自己的需要开发特定的辅助方法，就像我们在使用LINQ to Object时为IEnumerable所作的各种扩展那样。响应式框架也提供了一系列Subject类型，简化了IObservable自定义扩展的开发过程。由于响应式框架尚未正式发布，微软目前建立了&lt;a href="http://rxwiki.wikidot.com/"&gt;一个Wiki&lt;/a&gt;，用于展示关于各辅助方法及Subject类的使用示例及其他相关信息。&lt;/p&gt;

&lt;h1&gt;响应式框架的JavaScript版本&lt;/h1&gt;

&lt;p&gt;响应式编程的重要使用场景之一便是与用户交互的GUI界面。例如，Silverlight禁止任何阻塞的IO操作，换言之Silverlight中的所有网络操作都是异步的，微软也正是出于简化异步开发的目的才设计了响应式框架（事实上响应式框架已经集成到Silverlight Toolkit中）。不过与Silverlight相比，基于浏览器的原生JavaScript应用程序无疑使用地更为广泛。对于这样的应用程序来说，动画是异步的，AJAX请求也是异步的，我们几乎可以断言，如果有一套面向JavaScirpt应用程序的响应式框架，一定会比面向Silverlight的框架更有意义得多。&lt;/p&gt;

&lt;p&gt;微软也想到了这一点。之前我们讨论的“响应式框架”，其实只是响应式编程模型的一种实现。更确切地说，我们只是讨论了这套框架的.NET版本，微软还提供了JavaScript版本的响应式框架。JavaScript版本的API与.NET版本几乎完全一致，例如我们之前讨论的拖放操作，使用JavaScript即可写作：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;target = $(&lt;span style="color: maroon"&gt;&amp;quot;#dragTarget&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseMove = target.toObservable(&lt;span style="color: maroon"&gt;&amp;quot;mousemove&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDiff = mouseMove.Zip(mouseMove.Skip(1), 
    &lt;span style="color: blue"&gt;function&lt;/span&gt;(prev, curr) {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;{
            PrevPos: { x: prev.clientX, y: prev.clientY },
            CurrPos: { x: curr.clientX, y: curr.clientY }
        };
    });

&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDown = target.toObservable(&lt;span style="color: maroon"&gt;&amp;quot;mousedown&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseUp = target.toObservable(&lt;span style="color: maroon"&gt;&amp;quot;mouseup&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDrag = mouseDown.SelectMany(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
    mouseDiff.TakeUntil(mouseUp);
});

mouseDrag.Subscribe(...);&lt;/pre&gt;

&lt;p&gt;由于没有C#中的LINQ查询语言，我们只能直接使用展开后的方法，如SelectMany来编写逻辑。JavaScript版本的响应式框架还提供了一系列的“胶合”层，能够与jQuery，Dojo，MooTools，Prototype等流行框架同时使用。例如，上一段代码中的toObservable便是在jQuery根对象上扩展的方法。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;异步编程在用户交互式界面及一些云计算场景中尤其重要。微软的云编程能力团队针对.NET平台和JavaScirpt分别提供了一套响应式框架，希望以此简化异步程序的开发。不过，这套响应式框架所表现出的理念是通用的。而且，事实上只要是拥有匿名函数及闭包的语言，例如Scala，Python，Ruby等等，实现这样一套框架其实都不是十分困难的事情。&lt;/p&gt;
</description>
      <comments>http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html#comments</comments>
      <pubDate>Tue, 14 Sep 2010 12:14:28 GMT</pubDate>
      <lastBuildDate>Tue, 14 Sep 2010 12:14:28 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>程序员的档次？有没有搞错？！</title>
      <link>http://blog.zhaojie.me/2010/08/programmer-hierarchy-disgusting-csdn-news.html</link>
      <guid>http://blog.zhaojie.me/2010/08/programmer-hierarchy-disgusting-csdn-news.html</guid>
      <description>&lt;a href="http://img.zhaojie.me/blog/programmer-hierarchy.gif" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/programmer-hierarchy.gif" width="200" /&gt;&lt;/a&gt; 

&lt;p&gt;最近有&lt;a href="http://img.zhaojie.me/blog/programmer-hierarchy.gif" target="_blank"&gt;一幅老图&lt;/a&gt;很火，很多人说这是“从语言来区分程序员的高低”。很多人说这幅图有问题，我说是你们的理解有问题，类似这样的对话在推特上面出现过多次，一般说着说着也就过了。不过突然发现，在CSDN——这个中国最大、最权威的IT类网站上居然发布了一篇名为“&lt;a href="http://news.csdn.net/a/20100826/278719.html"&gt;程序员的档次&lt;/a&gt;”的新闻，让我有些愤慨。CSDN首页是我常去的地方，在我印象中以前最令人不满的也最多是技术方面的硬伤吧，但这次就不明白了，这是故意的还是怎么的？我已经很难相信这是“无心之失”了。如果不是故意的，那么某些编辑同志的业务水平的确需要提高了。&lt;/p&gt;

&lt;p&gt;首先来看看CSDN这则新闻是如何理解这幅图的吧，下面的文字是它的“导读”，在此一字不漏地摘录：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;程序员是否可以以它熟悉的语言判断高下？国外某程序员对此表示认可，并绘出了一张有趣的有关于程序员的图像，箭头的意思是表示某语言的程序员比另一语言的程序员更高级。最高阶自然是神级Lisp和汇编语言，最后一个嘛，自己看吧。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;很显然，它认为这幅图在表达“某语言的程序员比另一语言的程序员更高级”。那么您的理解呢？如果您的理解与它相同，那么我建议您洗把脸，透口气，再仔细看看右上角的深底色框内的文字——如果还没有意识到其中问题，我想以您的某某能力，还是放弃程序员这门很有前途的工作吧。&lt;/p&gt;

&lt;p&gt;框里的文字是“Consider themselves superior to”，翻译成中文便是“认为他们比对方高级”。例如有个箭头从Lisp指向C，这说明“Lisp程序员自认为比C程序员高级”。这里的关键在于“自认为”，这是种“自我感觉”，因此这幅图不是根据语言为程序员排名，而是一个“使用某种语言的程序员”的“自我感觉良好程度”排行榜。而这幅图右上方的注释也说明了这一点：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ruby programmers consider themselves superior to everybody, but are not aware of the existence of non-web languages so on this chart com in above Perl Programmers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;当然，我这里不想去谈“正确性”云云，因为这一定谈不出结果来。这种“自我感觉”既难论证，又明显不可对整个语言社区一概而论。其实就图片上的文字来看，我的感觉也是有大量调侃的意味在里面（如把“People who refuse to use the word Ajax”也作为单独一类程序员）。那么，别人既没想要分出高下，又是在开玩笑（连CSDN那篇文章都能意识到这点），我们又何必当真？&lt;/p&gt;

&lt;p&gt;其实我也一直在努力去“善意”地理解这则新闻，我也想，可能它的“导读”也是在开玩笑的，但是我发现即便在最后，它还是在谈论“语言”和程序员的“水平”关系，甚至还是“档次”问题（如HTML等）：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;为什么Java会地位这么低呢？除了作者是PHP出身外，也许和欧美国家Java程序员太多，平均水平更低有关？&lt;/p&gt;

  &lt;p&gt;当然，有一点所有自称程序员的同学注意了，不要再将HTML（其实还包括ASP、JSP）称为一种编程语言……&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;令人感到可笑的是，在这样一则新闻里面，还出现了冠冕堂皇的“方向性引导”：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;我们想特别指出的是，各种语言都有其自身的长处和缺点，笼统比较优劣毫无意义。而且就目前一般的编程任务而言，基本上各种语言都能胜任，开发效率也和使用者的熟练程度密切相关。语言的此消彼长，关键在于社区，如果你热爱哪一种语言，请到CSDN开设博客，多写自己的心得、经验、各种层次的教程，让更多人体验到这种语言之美，扩大自己的社区。我们欢迎有质量有内容的语言和框架比较的技术文章，坚决排斥浪费自己和他人时间和资源的口水战。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;原本这幅图并没有挑起争论的意思，而这篇新闻却一边煽风点火，一边道貌岸然地说“大家要讲道理”。就我看来，这篇新闻要不是故意在搬弄是非，便只能说是十分愚蠢了。&lt;/p&gt;

&lt;p&gt;但是，点燃我愤怒的却是新闻里的最后一句话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;最后，不要在下面的评论里花费太多口舌，因为已经有国外的同学说了：“使用最佳工具完成工作的程序员”鄙视“对哪种编程语言好争论不休的人”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;不知道是不是我敏感了，我觉得这句话的重音是在“国外的”三个字──如此普通的一句话，我相信国内也无数人说过，为什么要说是“国外的”？如果真是某位国外同学，那么为什么不说明是谁呢？这让我感觉是类似“月亮是国外的圆”、“国外程序员水平高”的意思，我很反感。丝毫不夸张地说，如果这篇新闻作者在我面前，我很可能就直接“甘礼良”、“草泥马”或是“法克鱿”地招呼上去了。&lt;/p&gt;

&lt;p&gt;这篇新闻的恶劣之处，在于进一步扭曲了“语言讨论”的意义与价值。之前有人说语言之争意义不大吧，相对这个还算号，至少说的是语言之间没有必要分出高低，只是语言，和使用这个语言的程序员没有关系——就算争论双方的潜意识里有这样的看法，但是还真没见过有人如这篇新闻一样，赤裸裸地将使用这个语言的程序员拖下水，说语言能够说明程序员的水平。如此这般，还会有人认真地讨论技术么，还不是不分高低誓不罢休了？如此这般，居然还在进行“讨论方向”的引导。&lt;/p&gt;

&lt;p&gt;引导？我靠。&lt;/p&gt;

&lt;p&gt;没错，我是在批判Java，是在批评它是种不思进取的劣等语言——但我说过Java语言的程序员半句坏话吗？我认为讨论语言是有价值的，从表面上看，这篇新闻是在鼓励大家使用正确方式讨论语言，但是其内容只能让人对“语言之争”产生厌恶之情。这让我这样想要真正好好讨论语言的人情何以堪？&lt;/p&gt;

&lt;p&gt;真是“&lt;a href="http://blog.zhaojie.me/2010/06/no-room-for-discussion.html"&gt;猪一般的队友&lt;/a&gt;”。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/08/programmer-hierarchy-disgusting-csdn-news.html#comments</comments>
      <pubDate>Fri, 27 Aug 2010 18:43:07 GMT</pubDate>
      <lastBuildDate>Fri, 27 Aug 2010 18:43:07 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/reading/">阅读相关</category>
      <title>读完《Scala程序设计》，随便谈一些想法吧</title>
      <link>http://blog.zhaojie.me/2010/08/programming-scala-and-more.html</link>
      <guid>http://blog.zhaojie.me/2010/08/programming-scala-and-more.html</guid>
      <description>&lt;a href="http://img.zhaojie.me/blog/books/programming-scala-cn.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/books/programming-scala-cn.jpg" width="200" /&gt;&lt;/a&gt; 

&lt;p&gt;虽然我对Scala还算熟悉，但还是在china-pub上订阅了一本图灵引进的《&lt;a href="http://www.china-pub.com/196931"&gt;Scala程序设计&lt;/a&gt;》。上上周五在公司收到了这本书，顺手带回家，在路上简单翻了翻。周六用来欢乐，周日懒觉睡觉中午，直到下午才又拿起这本书，不过加上周一上班途中的工夫也算将这本不到200页的小册子浏览完了。这本书的内容本身并没有给我留下太深刻的印象，但我也想谈一些有关Scala语言和其他一些方面的想法。&lt;/p&gt;

&lt;p&gt;“编程语言”这东西自然是和“编程”相关，刚出版的8月份《程序员》杂志的专题是“语言”，其中田春（binghe）写了一篇关于Common LISP的文章，里面有这么一句话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;作为一门以编程为职业的程序员，时间是如此宝贵，以至于绝对不能把宝贵的时间浪费在不断跟进各种草率设计的新语言上，而忽略了对编程语言本质和一般编程方法（算法、数据结构上）的理解上。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这句话从道理上来看似乎是没错的，不过给我的感觉多少有些原教旨主义了。或者应该这么说：要理解这句话，还得谈一下“什么是编程语言本质”及“哪些是草率设计的新语言”等话题。我不懂“本质”，各种东西的“本质”我都不懂，就像我理解不了《建筑的永恒之道》对于学习GoF设计模式有哪些帮助一样。但如果你真要我谈一下心目中编程语言的“本质”，我可能会说是“更好地表达人类思考”，“辅助人类解决问题”一样。举个例子，C语言体现了编程语言的本质吗？在我看来，没有，因为它体现的是冯诺依曼机的本质，是操作机器的本质，和“表达人类思考”，“辅助人类解决问题”无关。因为，要不是有冯诺依曼机就不会有C语言；同时，在我看来人类也不会在“思考”时关心指针、地址、堆/栈、偏移量、字节等事物。&lt;/p&gt;

&lt;p&gt;当然，如果您是一位C语言粉丝，看到这里也不要急着反驳。首先，我只是说“C语言不能代表编程的本质”，并没有说“不本质就不好了”，也没有对C语言做出“不重要”或是“不值得了解”之类的评价。其次，我不都已经说过我不懂所谓“本质”了嘛。&lt;/p&gt;

&lt;p&gt;回到这个话题来说，什么样的语言算是“本质”了呢？按照我的标准，就是能够“更好地辅助人类解决问题”的语言，再通俗地讲，就是生产力更高的语言。比如人们发现C/C++中的内存管理造成了很大的麻烦，于是出现了垃圾收集机制；人们发现C++过于复杂，而Java语言便可以视为是它的一种简化。虽然我一直说Java是种劣质语言，但也必须承认它的历史地位，它在十几年前绝对是一种突破，只不过多年来的不思进取、固步自封让它成为了“现今标准”下的劣质语言——这很正常，因为即便是C# 2.0，在现在看来也是一种劣质语言。语言在发展，它的评价标准也在不断提高。&lt;/p&gt;

&lt;p&gt;在&lt;a href="http://news.csdn.net/a/20100813/278161.html"&gt;刚获得Jolt图书大奖&lt;/a&gt;的《&lt;a href="http://www.amazon.com/Masterminds-Programming-Conversations-Creators-Languages/dp/0596515170"&gt;Masterminds of Programming&lt;/a&gt;》一书中，作者向&lt;a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg"&gt;Anders Hejlsberg&lt;/a&gt;问起过有关C#和Java在语言演化方面的问题：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When I compare C# to Java, C# seems to have a stronger push toward evolution. The Java people seem to want a baseline where everyone’s code looks more or less the same. Whether you’ve programmed Java for a decade, never programmed before, or just graduated from a six-month course on Java, all of your code will look the same. C# seems to pull in new ideas from Haskell or from F#. Is there a desire to add new features that people who’ve just finished the six-month C# course haven’t seen and won’t immediately understand?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anders回答道：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is it that powers the Internet revolution and the electronic revolution that we’ve seen? It’s the fact that we’re constantly evolving. I bring it back to that. The minute you stop evolving, I don’t know that you’re adding any value. This is, again, taking it to the extreme. Of course, there is value in stability of the platform, but I think you provide that value by ensuring backward compatibility. You are free to get off the bus at C# 1.0 and just not move any further. For those people that really want to be more productive and want to build newer kinds of apps like SOA or whatever and get into more dynamic styles of programming—adaptable programs and more declarative styles of programming like we’re doing with LINQ—then you’ve got to evolve or get out of the way, or something else will replace you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;人们对生产力的探索和追求是无止境的，“固步自封”是一种违背潮流的行为。&lt;/p&gt;

&lt;p&gt;之所以设计出新的语言，主要原因大都是设计者对现有语言的不满——当然也不排除一些商业上的因素，例如当年Sun迫使微软不得触碰Java语言，于是微软设计了.NET和C#语言与之竞争（无独有偶，Oracle最近又和Google展开了有关Java的专利诉讼）。例如，&lt;a href="http://blog.zhaojie.me/2010/04/something-about-groovy-and-fsharp-from-qcon.html"&gt;Groovy在考虑到Java语言的平滑升级的基础上&lt;/a&gt;融入了有更好生产力的元素，而F#和Scala都在并发编程和DSL方面给予了加强。那么，它们都是草率设计的语言吗？可能有人说，是的，因为这些语言里的语法糖特别多。那么，语法糖便意味着草率吗？&lt;/p&gt;

&lt;p&gt;我的看法是否定的，一个特性是否草率或是是否重要，关键不在于它是不是个所谓的“语法糖”，而是要看它有没有带来编程思维上的改变，或者说，它有没有遵循语言设计者的文化。我们可以从各种语言找到许多此类“有文化”的“语法糖”：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;C#的Lambda表达式是匿名委托的语法糖，但是它带来/鼓励了函数式编程及无副作用编程的思维。 &lt;/li&gt;

  &lt;li&gt;Haskell中的“$”运算符，和F#中的“&amp;lt;|”运算符的作用是简单地“改变优先级”，让程序员可以省略括号，写出流畅的代码。 &lt;/li&gt;

  &lt;li&gt;Scala中的方法调用a.method(b)可以省略点和括号，写为a method b，这样开发人员便很容易写出one day ago这样的DSL。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scala还有许多用于“省略”或是“替代”的规则，目的就是让程序员可以写出更为漂亮而紧凑的代码，这便是Scala的文化（之一）。Scala是我认为最有机会，也是最适合代替Java的语言。因为从JVM上的语言来说，Jython和JRuby都是“另一种语言”，程序员甚至需要在“Ruby的Date对象”和“Java的Date对象”间进行切换；Clojure是一种类LISP的语言，它的编程模型和思想与Java相距太远；Groovy是Scala的有力竞争对手，因为它考虑到了Java程序员的平滑过渡，只不过它是一门动态类型语言，这点和Java程序员的习惯有所不同；而Scala是一门静态编译型语言，性能很高，能够和Java一样进行面向对象和命令式编程，也可以表现出和Java一样的对象模型——自然还有额外的提高生产力的语言特性。&lt;/p&gt;

&lt;p&gt;有些朋友认为Scala过于复杂，难以掌握。关于这点我想说：有利自然有弊，Scala既然可以带来更好的生产力，自然需要您进行额外的学习，从Java语言切换到Scala并非一蹴而就的，但是这些付出在我十分值得，因为Scala是一门挺有想法的语言。再者，Scala真难学吗？或者说，它比Java语言难学吗？在我看来真不见得。这里有一个事实是：&lt;a href="http://java.sun.com/docs/books/jls/download/langspec-3.0.pdf"&gt;Java的语言规范&lt;/a&gt;比&lt;a href="http://www.scala-lang.org/docu/files/ScalaReference.pdf"&gt;Scala的语言规范&lt;/a&gt;要多得多，因此其实从某些角度来说，Scala要比Java要简单的多。出现这个情况的原因也很容易理解：因为Java是一门十分死板的语言，它需要用大量篇幅来表示各种限制和规定，而Scala只是提供一些最基本的语法描述和变化规则，剩下的都是这些规则的自由组合了——此正所谓&lt;font color="#ff0000"&gt;Scala&lt;/font&gt;ble。&lt;/p&gt;

&lt;p&gt;例如，在Scala中，“1 + 2”这个基本表达式，从概念上说也是一个Int对象上的方法调用，其完整形式为“1.+(2)”。那么，试想在语言规范中，还需要定义次方运算或是求模运算吗？这些篇幅都可以省下了，因为这些“运算符”都已经变成了“类库”，要知道整数之间有哪些运算符，只要去浏览文档就行了。至于“+”方法最终表现为一个加法指令而不是方法调用，这便是编译器在优化时所做的事情了。“语言”有自己的文化，“编译器”也有自己的优化策略。&lt;/p&gt;

&lt;p&gt;当然，语言规约的规模并不能直接用于论证语言的复杂度，“变化”有时也会带来复杂度。其实我只是想说，Scala语言并不可怕，作为一个程序员，比这门语言难以掌握的东西有太多了。在C#这边也有人经常抱怨说“C# 3.0太复杂，不能保证团队里人人都会”，对此我的说法是，当年我在创业时，招了几个工资在3000左右的程序员，他们都能在一个星期里学会C# 3.0的新特性（原本就会C# 2.0）——假如说一份能力一份待遇的话，那么您的期望薪资是多少呢？&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/books/programming-in-scala.jpg" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/books/programming-in-scala-s.gif" class="floatRight" /&gt;&lt;/a&gt; 

&lt;p&gt;最后再回到《Scala程序设计》这本书吧，这是本连附录也只有180页的小册子，很明显只能是Scala语言的概述和入门，并没有企图让您精通这门语言。这本书的副标题是“Java虚拟机多核编程实战”，这我认为只是个“噱头”而已，因为这本书对于Scala引以为豪的Actor库也只用了20页不到的篇幅。不过话说回来，即便是《&lt;a href="http://www.amazon.com/gp/product/0981531601/"&gt;Programming in Scala&lt;/a&gt;》这本近800页的，覆盖了Scala语言方方面面的“巨著”，其中关于Actor的那章也只有30多页——因为Scala的Actor模型本身也只是以标准库的形式提供的，且十分简单，并不是Scala的重点。如果您对这方面感兴趣，我觉得更应该关注的是&lt;a href="http://akkasource.org/"&gt;akka&lt;/a&gt;这个类库而不是Scala的标准库。而如果要将Actor模型和并发编程的方方面面都讲清楚的话，估计又需要整本书的篇幅了。&lt;/p&gt;

&lt;p&gt;总体来说，《Scala程序设计》可以算作是一本“称职”的书，即使您无法在工作中使用Scala语言，甚至您不是个Java程序员，也不妨可以从这本小册子里简单地体会一下这门语言。许多开发人员常说“静态语言”的开发效率如何如何低，其实也只是被Java这门劣等语言给误导了，事实上Haskell、C#，F#和Scala都是静态语言的典范，了解Scala也有助于开阔眼界。《&lt;a href="http://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X"&gt;Programmatic Programmer&lt;/a&gt;》里建议开发人员每年学一门新的语言，Scala也是个不错的选择。与此相比，如果让一个C#程序员去学习Java，那投资回报率则实在是太低了。&lt;/p&gt;

&lt;p&gt;最后我也在想，可能这种小册子形式的书也是有不少价值的，例如阅读的代价较低，也比较容易快速地翻译引进，而像《Expert F# 2.0》这样的由语言设计者（还是个科学家）亲自编写的砖头，受众面自然会狭窄许多。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/08/programming-scala-and-more.html#comments</comments>
      <pubDate>Sun, 15 Aug 2010 15:13:11 GMT</pubDate>
      <lastBuildDate>Sun, 15 Aug 2010 15:13:11 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>关于DLR及.NET版本的问题</title>
      <link>http://blog.zhaojie.me/2010/07/dlr-in-different-version-of-dotnet.html</link>
      <guid>http://blog.zhaojie.me/2010/07/dlr-in-different-version-of-dotnet.html</guid>
      <description>&lt;p&gt;&lt;a href="http://dlr.codeplex.com/"&gt;动态语言运行时（Dynamic Language Runtime，DLR）&lt;/a&gt;是一套基于.NET的类库，它的作用是简化在CLR上开发动态语言的工作，例如DLR中提供了表达式树的创建，代码生成、优化及调试等实现动态语言的常见功能，而语言的编写者则着重关注解析器等方面的工作。不过最近接触了观察了DLR代码之后，却发现它和各版本.NET中BCL的协作还真是件不太容易理清的事情。&lt;/p&gt;

&lt;p&gt;这还要从.NET 2.0说起，因为DLR的支持范围是.NET 2.0 SP1及.NET 4.0。.NET 2.0的类库较少，因此DLR要做的事情最多，例如它定义了一套完整的表达式树。不过到了.NET 3.5时，在新增的System.Core.dll中却也同样出现了一套表达式树的类库（例如BinaryExpression等等），而这套类库也完整包含在DLR中，它们的区别在于一个在System.Linq.Expressions命名空间下，而另一个在Microsoft.Scripting.Ast命名空间下。那么，如果您基于.NET 3.5编写代码的时候，您是利用哪一套代码？&lt;/p&gt;

&lt;p&gt;事实上，您只能使用定义在DLR中的那套类库，因为一旦使用了.NET 3.5 BCL中的类库之后，它就无法与DLR中的其他组件进行交互了——除非您修改DLR的代码，让它与BCL中包含的表达式树进行合作。然而这么做还不一定可行，因为我们无法保证这些类库交互中是否会牵涉到内部状态。因此，在基于.NET 3.5开发DLR应用程序的时候，我们可以完全忽视BCL中表达式树的存在。&lt;/p&gt;

&lt;p&gt;下图是DLR源代码中Codeplex-DLR.sln文件打开后的结果，其中Microsoft.Scripting.Core项目中的Ast文件夹便包含了一套“貌似”完整表达式树（抽象语法树）：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/dotnet-bcl-dlr/codeplex-dlr-core.png" /&gt; 

&lt;p&gt;不过到了.NET 4.0之后，状况又有所改变。您打开DLR源码中的Codeplex-DLR-Dev10.sln文件，便会发现Microsoft.Scripting.Core项目消失了，取而代之的是System.Core项目：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/dotnet-bcl-dlr/codeplex-dlr-dev10-core.png" /&gt; 

&lt;p&gt;System.Core不是BCL中的类库吗？怎么会出现在DLR中？没错，这里的System.Core项目纯粹是来打酱油的，没有其他任何项目对它产生依赖。事实上，这个System.Core项目，也就是之前的Microsoft.Scripting.Core项目里的内容，已经完全融入在.NET 4的BCL里面了，它们分布在System.Core.dll的以下几个命名空间中：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ast目录：System.Linq.Expressions命名空间 &lt;/li&gt;

  &lt;li&gt;Actions目录：System.Dynamic命名空间 &lt;/li&gt;

  &lt;li&gt;Compiler目录：System.Linq.Expressions.Compiler命名空间 &lt;/li&gt;

  &lt;li&gt;Utils目录：System.Dynamic.Utils、System.Runtime.CompilerServices甚至System命名空间 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果您进一步阅读其中的内容，会发现代码在需要的情况下则使用了“&lt;a href="http://msdn.microsoft.com/en-us/library/aa691095(VS.71).aspx"&gt;条件编译符号&lt;/a&gt;”来切换其所在的命名空间，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;#if &lt;/span&gt;CLR2
&lt;span style="color: blue"&gt;namespace&lt;/span&gt; Microsoft.Scripting.Ast {
&lt;span style="color: blue"&gt;#else&lt;/span&gt;
&lt;span style="color: gray"&gt;namespace System.Linq.Expressions {&lt;/span&gt;
&lt;span style="color: blue"&gt;#endif&lt;/span&gt;

    &lt;span style="color: gray"&gt;/// &amp;lt;summary&amp;gt;
    /// &lt;/span&gt;&lt;span style="color: green"&gt;Represents an expression that has a binary operator.
    &lt;/span&gt;&lt;span style="color: gray"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;
    public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;BinaryExpression &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;Expression &lt;/span&gt;{
        ...&lt;/pre&gt;

&lt;p&gt;这样，在面向CLR 2（即.NET 2和.NET 3.5）和CLR 4的不同编译条件下，整套表达式树类库所在的命名空间是不同的。此外，如一些System.Dynamic命名空间下的类库，它们会使用条件编译符号依赖不同的表达式树类库，这也是显而易见的，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;#if &lt;/span&gt;CLR2
&lt;span style="color: blue"&gt;using&lt;/span&gt; Microsoft.Scripting.Ast;
&lt;span style="color: blue"&gt;using&lt;/span&gt; Microsoft.Scripting.Ast.Compiler;
&lt;span style="color: blue"&gt;using&lt;/span&gt; Microsoft.Scripting.Utils;
&lt;span style="color: blue"&gt;#else&lt;/span&gt;
&lt;span style="color: gray"&gt;using System.Linq.Expressions;
using System.Linq.Expressions.Compiler;&lt;/span&gt;
&lt;span style="color: blue"&gt;#endif&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;由此可见，.NET 4之前“声称”自己包含了DLR，事实上它只是包含了DLR的子集，也就是原本在Microsoft.Scripting.Core项目中的内容。那么假设我们要基于.NET 4和DLR开发一门新的语言，又该依赖哪些DLR组件呢？&lt;a href="http://ironjs.com/"&gt;IronJS&lt;/a&gt;给了我们一个参考，您可以下载&lt;a href="http://github.com/fholm/IronJS/tree/0.1"&gt;它的tag为0.1代码&lt;/a&gt;（目前的主代码无法编译通过），打开后便会发现它只是引用了Microsoft.Dynamic.dll和Microsoft.Scripting.dll两个程序集，其他的部分则已经包含在System.Core.dll中了：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/dotnet-bcl-dlr/ironjs-ref.png" /&gt; 

&lt;p&gt;如果我们要将其移植到.NET 3.5上，则需要引用一个额外的Microsoft.Scripting.Core.dll。我将IronJS移植到了.NET 3.5上并执行成功，项目引用情况如下：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/dotnet-bcl-dlr/ironjs-clr2-ref.png" /&gt; 

&lt;p&gt;基于我们之前的分析，其实您会发现，理想情况下将一个项目从.NET 4.0移植到.NET 3.5上并不需要修改太多代码，只要将所有对System.Linq.Expressions下类型的依赖修改至Microsoft.Scripting.Ast即可。当然在移植IronJS的过程中我还遇到了一些其他类库和语言方面的问题，例如一些使用了协变的代码，还有Enum类型中新增的HasFlag方法——幸好它没有使用C# 4.0中的dynamic，这些简单的问题只要兵来将挡，水来土掩就行了。&lt;/p&gt;

&lt;p&gt;那么，Microsoft.Dynamic项目中究竟包含了什么呢？还是看看它的项目树吧：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/dotnet-bcl-dlr/codeplex-dlr-dev10-dynamic.png" /&gt; 

&lt;p&gt;您可能已经意识到了，这才是“真正”完整的DLR，包含了“真正”完整的表达式树类库及其他支持。我们有理由相信，所谓的Microsoft.Scripting.Core项目，其实只是为了.NET 4而特地分离出去的。从这点上看，DLR为了和.NET进行配合，真可谓是“用心良苦”。不过，我认为它们的做法及思路十分清晰，许多事情该怎么做，现在看来应该都是一目了然的。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/07/dlr-in-different-version-of-dotnet.html#comments</comments>
      <pubDate>Mon, 26 Jul 2010 13:12:14 GMT</pubDate>
      <lastBuildDate>Mon, 26 Jul 2010 13:12:14 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>Why Java Sucks and C# Rocks（6）：yield及其作用</title>
      <link>http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html</link>
      <guid>http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html</guid>
      <description>&lt;p&gt;C# 2.0新增了yield关键字，其初衷是简化迭代器的生成，这可以说是现代语言的标配。只可惜Java历经数次升级，从数量上来说也算增加了不少语言特性了，却还是将这个功能拒之门外，让人费解。除了用于生成迭代器之外，yield还可用于其它一些场景，颇为奇妙。这些场景都是在生产过程中常用的开发模式，只可惜对于使用Java语言的程序员来说都只能望而兴叹了。&lt;/p&gt;

&lt;h1&gt;迭代生成器&lt;/h1&gt;

&lt;p&gt;说起迭代器（Iterator）大家一定都不陌生，无论是是Java，C#或是Python等语言都有内置标准的迭代器结构，它们也都提供了内置的for或foreach关键字简化迭代器的“使用”。不过对于迭代器的“生成”，不同语言之间的就会有很大差距。例如，在C#和Python中都提供了yield来简化迭代器的“创建”，此时生成一个迭代器便再简单不过了。但对于Java程序员来说，即使到了Java 7还必须为在迭代器内部手动维护状态，非常痛苦。而更重要的一点是，利用yield我们可以轻松地创建一个“延迟”的，“无限”的序列。&lt;/p&gt;

&lt;p&gt;例如，如果我们使用Java写一个无限的斐波那契数列，一般则需要这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Fibonacci &lt;/span&gt;&lt;span style="color: blue"&gt;implements&lt;/span&gt; &lt;span style="color: #2b91af"&gt;Iterable&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt; {

    @Override
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterator&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt; iterator() {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterator&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt;() {

            &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_state = 0;
            &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_current;
            &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_last0;
            &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_last1;
            
            &lt;span style="color: blue"&gt;public boolean&lt;/span&gt; hasNext() {
                &lt;span style="color: blue"&gt;return true&lt;/span&gt;;
            }
            
            &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt; next() {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(m_state == 0) { &lt;span style="color: green"&gt;// first&lt;/span&gt;
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = 0;
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state = 1;
                }
                &lt;span style="color: blue"&gt;else if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state == 1) {
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = 1;
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1 = 0;
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state = 2;
                }
                &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last0 = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1;
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1 = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current;
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last0 + &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1;
                }
                
                &lt;span style="color: blue"&gt;return this&lt;/span&gt;.m_current;
            }

            &lt;span style="color: blue"&gt;public void &lt;/span&gt;remove() {
                &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;UnsupportedOperationException&lt;/span&gt;();
            }
        };
    }
}&lt;/pre&gt;

&lt;p&gt;在C# 1.0实现相同的功能（即IEnumerable&amp;lt;int&amp;gt;迭代器）也需要使用类似的做法，甚至比Java更麻烦一些，因为在C#中没有Java语言中的“匿名类型”特性。如下：&lt;/p&gt;

&lt;pre id="this-is-code-A-hide" class="code"&gt;&lt;a href="javascript:__showAndHide('this-is-code-A-show', 'this-is-code-A-hide')"&gt;展开代码&lt;/a&gt;&lt;/pre&gt;

&lt;pre id="this-is-code-A-show" class="code jeffz_seo"&gt;&lt;a href="javascript:__showAndHide('this-is-code-A-hide', 'this-is-code-A-show')"&gt;隐藏代码&lt;/a&gt; 

&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Fibonacci &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;
{
    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Enumerator &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;
    {
        &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_state = 0;
        &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_current;
        &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_last0;
        &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_last1;

        &lt;span style="color: blue"&gt;public bool &lt;/span&gt;MoveNext()
        {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state == 0) &lt;span style="color: green"&gt;// first
            &lt;/span&gt;{
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = 0;
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state = 1;
            }
            &lt;span style="color: blue"&gt;else if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state == 1)
            {
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = 1;
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1 = 0;
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_state = 2;
            }
            &lt;span style="color: blue"&gt;else
            &lt;/span&gt;{
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last0 = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1;
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1 = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current;
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last0 + &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_last1;
            }

            &lt;span style="color: blue"&gt;return true&lt;/span&gt;;
        }

        &lt;span style="color: blue"&gt;public int &lt;/span&gt;Current { &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return this&lt;/span&gt;.m_current; } }
        &lt;span style="color: blue"&gt;object &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;.Current { &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return this&lt;/span&gt;.Current; } }

        &lt;span style="color: blue"&gt;public void &lt;/span&gt;Reset() { }
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;Dispose() { }
    }

    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; GetEnumerator()
    {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Enumerator&lt;/span&gt;();
    }

    &lt;span style="color: #2b91af"&gt;IEnumerator IEnumerable&lt;/span&gt;.GetEnumerator()
    {
        &lt;span style="color: blue"&gt;return this&lt;/span&gt;.GetEnumerator();
    }
}&lt;/pre&gt;

&lt;p&gt;一个枚举器其实就是个状态机，在普通状态下我们往往需要手动维护其中的格式状态，编写起来可谓既费神又不直观。幸好C# 2.0提供了yield语法支持，一切就变得简单了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; GenerateFibonacci()
{
    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;0;
    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;1;

    &lt;span style="color: blue"&gt;int &lt;/span&gt;last0 = 0, last1 = 1, current;

    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;)
    {
        current = last0 + last1;
        &lt;span style="color: blue"&gt;yield return &lt;/span&gt;current;

        last0 = last1;
        last1 = current;
    }
}&lt;/pre&gt;

&lt;p&gt;yield return的作用是在执行到这行代码之后，将控制权立即交还给外部，此时外部代码可以通过Current对象访问到返回出去的值。而yield return之后的代码会在外部代码再次调用MoveNext时才会执行，直到下一个yield return——或是迭代结束。虽然上面的代码看似有个死循环，但事实上在循环内部我们始终会把控制权交还给外部，这就由外部来决定何时中止这次迭代。有了yield之后，我们便可以利用“死循环”，我们可以写出含义明确的“无限的”斐波那契数列。&lt;/p&gt;

&lt;p&gt;就最终执行的代码来说，C# 2.0和Java或C# 1.0是差不多的，只不过C#的编译器帮助开发人员节省了许多工作。事实上，我们根据C#编译器最终的生成结果，&lt;a href="http://blog.zhaojie.me/2010/01/decompile-methods-with-yield-manually.html"&gt;可以根据一定规律反推出原始代码&lt;/a&gt;，只是在某些情况下会显得比较困难罢了。&lt;/p&gt;

&lt;p&gt;如“无限斐波那契数列”那样，利用yield我们可以用最直观的方式实现一个迭代器，例如连接多个迭代器：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;T&amp;gt; Concat&amp;lt;T&amp;gt;(&lt;span style="color: blue"&gt;params &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;T&amp;gt;[] iterators)
{
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;iter &lt;span style="color: blue"&gt;in &lt;/span&gt;iterators)
    {
        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;item &lt;span style="color: blue"&gt;in &lt;/span&gt;iter)
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;item;
    }
}&lt;/pre&gt;

&lt;p&gt;或是一个二叉树的中序遍历：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;T&amp;gt; Traverse&amp;lt;T&amp;gt;(&lt;span style="color: #2b91af"&gt;TreeNode&lt;/span&gt;&amp;lt;T&amp;gt; node)
{
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(node == &lt;span style="color: blue"&gt;null&lt;/span&gt;) &lt;span style="color: blue"&gt;yield break&lt;/span&gt;;

    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;child &lt;span style="color: blue"&gt;in &lt;/span&gt;Traverse(node.Left))
        &lt;span style="color: blue"&gt;yield return &lt;/span&gt;child;

    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;node.Value;
    
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;child &lt;span style="color: blue"&gt;in &lt;/span&gt;Traverse(node.Right))
        &lt;span style="color: blue"&gt;yield return &lt;/span&gt;child;
}&lt;/pre&gt;

&lt;p&gt;如果没有yield，那么这两段代码会是什么样子呢？如果您感兴趣的话，也不妨使用Java语言来实现一下，有比较便能看出差距。&lt;/p&gt;

&lt;h1&gt;简化异步操作&lt;/h1&gt;

&lt;p&gt;异步操作是强大的，它是许多高伸缩性架构的基石。但是，异步编程又是十分困难的，它让这让许多程序员敬而远之。因此，越来越多的编程语言都对异步编程提供了相当程度的支持，其中的典型代表便是F#中的异步工作流。不过，其实在C# 2.0出现了yield之后，许多情况下的异步编程已经变得十分简单了。那么，我们还是先来看一下异步编程困难的原因吧。&lt;/p&gt;

&lt;p&gt;这里我准备了一个接口：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CompletedEventArgs &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;EventArgs&lt;/span&gt;
{
    &lt;span style="color: blue"&gt;public &lt;/span&gt;CompletedEventArgs(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
    {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.Error = ex;
    }

    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;Error { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;private set&lt;/span&gt;; }
}

&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebAsyncTransfer&lt;/span&gt;
{
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;StartAsync(&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context, &lt;span style="color: blue"&gt;string &lt;/span&gt;url)
    {
        ...
    }

    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;CompletedEventArgs&lt;/span&gt;&amp;gt; Completed;
}&lt;/pre&gt;

&lt;p&gt;在这里WebAsyncTransfer是一个“异步下载类”，它的StartAsync方法会发起一个针对远程url的请求，并将内容下载至context中（并设置ContentType等参数），下载完成后则通过Completed事件进行通知。写好了吗？那么也来看看我给的参考答案吧：&lt;/p&gt;

&lt;pre id="this-is-code-B-hide" class="code"&gt;&lt;a href="javascript:__showAndHide('this-is-code-B-show', 'this-is-code-B-hide')"&gt;展开代码&lt;/a&gt;&lt;/pre&gt;

&lt;pre id="this-is-code-B-show" class="code jeffz_seo"&gt;&lt;a href="javascript:__showAndHide('this-is-code-B-hide', 'this-is-code-B-show')"&gt;隐藏代码&lt;/a&gt; 

&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebAsyncTransfer&lt;/span&gt;
{
    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;m_context;
    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebRequest &lt;/span&gt;m_request;
    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebResponse &lt;/span&gt;m_response;
    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Stream &lt;/span&gt;m_streamIn;
    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Stream &lt;/span&gt;m_streamOut;

    &lt;span style="color: blue"&gt;public void &lt;/span&gt;StartAsync(&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context, &lt;span style="color: blue"&gt;string &lt;/span&gt;url)
    {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_context = context;

        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_request = &lt;span style="color: #2b91af"&gt;HttpWebRequest&lt;/span&gt;.Create(url);
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_request.BeginGetResponse(&lt;span style="color: blue"&gt;this&lt;/span&gt;.EndGetResponse, &lt;span style="color: blue"&gt;null&lt;/span&gt;);
    }

    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;CompletedEventArgs&lt;/span&gt;&amp;gt; Completed;

    &lt;span style="color: blue"&gt;private void &lt;/span&gt;EndGetResponse(&lt;span style="color: #2b91af"&gt;IAsyncResult &lt;/span&gt;ar)
    {
        &lt;span style="color: blue"&gt;try
        &lt;/span&gt;{
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_response = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_request.EndGetResponse(ar);
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_context.Response.ContentType = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_response.ContentType;

            &lt;span style="color: blue"&gt;var &lt;/span&gt;buffer = &lt;span style="color: blue"&gt;new byte&lt;/span&gt;[1024];
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamIn = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_response.GetResponseStream();
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamOut = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_context.Response.OutputStream;

            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamIn.BeginRead(
                buffer, 0, buffer.Length,
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.EndReadInputStream, buffer);
        }
        &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
        {
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.OnCompleted(ex);
        }
        &lt;span style="color: blue"&gt;finally
        &lt;/span&gt;{
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_request = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
        }
    }

    &lt;span style="color: blue"&gt;private void &lt;/span&gt;EndReadInputStream(&lt;span style="color: #2b91af"&gt;IAsyncResult &lt;/span&gt;ar)
    {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;buffer = (&lt;span style="color: blue"&gt;byte&lt;/span&gt;[])ar.AsyncState;
        &lt;span style="color: blue"&gt;int &lt;/span&gt;lengthRead;

        &lt;span style="color: blue"&gt;try
        &lt;/span&gt;{
            lengthRead = &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamIn.EndRead(ar);
        }
        &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
        {
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.OnCompleted(ex);
            &lt;span style="color: blue"&gt;return&lt;/span&gt;;
        }

        &lt;span style="color: blue"&gt;if &lt;/span&gt;(lengthRead &amp;lt;= 0)
        {
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.OnCompleted(&lt;span style="color: blue"&gt;null&lt;/span&gt;);
        }
        &lt;span style="color: blue"&gt;else
        &lt;/span&gt;{
            &lt;span style="color: blue"&gt;try
            &lt;/span&gt;{
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamOut.BeginWrite(
                    buffer, 0, lengthRead,
                    &lt;span style="color: blue"&gt;this&lt;/span&gt;.EndWriteOutputStream, buffer);
            }
            &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
            {
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.OnCompleted(ex);
            }
        }
    }

    &lt;span style="color: blue"&gt;private void &lt;/span&gt;EndWriteOutputStream(&lt;span style="color: #2b91af"&gt;IAsyncResult &lt;/span&gt;ar)
    {
        &lt;span style="color: blue"&gt;try
        &lt;/span&gt;{
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamOut.EndWrite(ar);

            &lt;span style="color: blue"&gt;var &lt;/span&gt;buffer = (&lt;span style="color: blue"&gt;byte&lt;/span&gt;[])ar.AsyncState;
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_streamIn.BeginRead(
                buffer, 0, buffer.Length,
                &lt;span style="color: blue"&gt;this&lt;/span&gt;.EndReadInputStream, buffer);
        }
        &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
        {
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.OnCompleted(ex);
        }
    }

    &lt;span style="color: blue"&gt;private void &lt;/span&gt;OnCompleted(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
    {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.m_response != &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        {
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_response.Close();
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_response = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
        }

        &lt;span style="color: blue"&gt;var &lt;/span&gt;handler = &lt;span style="color: blue"&gt;this&lt;/span&gt;.Completed;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(handler != &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        {
            handler(&lt;span style="color: blue"&gt;this&lt;/span&gt;, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CompletedEventArgs&lt;/span&gt;(ex));
        }
    }
}&lt;/pre&gt;

&lt;p&gt;是不是很复杂？&lt;/p&gt;

&lt;p&gt;异步操作的难点之一，便是破坏了“代码局部性（Code Locality）”，这可能也是异步操作中最为常见的阻碍。程序员早已习惯了“线性”地表达逻辑，但即便是多个顺序执行的异步操作，也会因为大量的回调函数而将算法拆得支离破碎，更何况还会出现各种循环及条件判断。同时，在线性的代码中，我们可以使用“局部变量”保存状态，而在编写异步代码时则需要手动地在多个函数中传递状态。此外，由于逻辑被拆分至多个方法，因此我们也无法使用传统的try/catch进行统一异常处理。&lt;/p&gt;

&lt;p&gt;反映在上面这段实现中，就在于我们无法使用普通循环来实现异步读取写入，也必须在每个异步操作时使用try…catch来捕获可能会抛出的异常。此外，我们还必须手动地保持状态，更重要的是手动地清理一些资源。例如在EndGetResponse方法中，我们需要手动地将m_request设为null，这样使得该对象可以早于WebAsyncTransfer得到回收。总之，编写异步代码就是这么麻烦。&lt;/p&gt;

&lt;p&gt;那么yield又是怎么样帮到我们的呢？且看如下代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;private static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; GenerateTransferTask(
    &lt;span style="color: #2b91af"&gt;AsyncEnumerator &lt;/span&gt;ae, &lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context, &lt;span style="color: blue"&gt;string &lt;/span&gt;url)
{
    &lt;span style="color: #2b91af"&gt;WebRequest &lt;/span&gt;request = &lt;span style="color: #2b91af"&gt;WebRequest&lt;/span&gt;.Create(url);
    request.BeginGetResponse(ae.End(), &lt;span style="color: blue"&gt;null&lt;/span&gt;);
    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;1;

    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;WebResponse &lt;/span&gt;response = request.EndGetResponse(ae.DequeueAsyncResult()))
    {
        &lt;span style="color: #2b91af"&gt;Stream &lt;/span&gt;streamIn = response.GetResponseStream();
        &lt;span style="color: #2b91af"&gt;Stream &lt;/span&gt;streamOut = context.Response.OutputStream;
        &lt;span style="color: blue"&gt;byte&lt;/span&gt;[] buffer = &lt;span style="color: blue"&gt;new byte&lt;/span&gt;[1024];

        &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;)
        {
            streamIn.BeginRead(buffer, 0, buffer.Length, ae.End(), &lt;span style="color: blue"&gt;null&lt;/span&gt;);
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;1;
            &lt;span style="color: blue"&gt;int &lt;/span&gt;lengthRead = streamIn.EndRead(ae.DequeueAsyncResult());

            &lt;span style="color: blue"&gt;if &lt;/span&gt;(lengthRead &amp;lt;= 0) &lt;span style="color: blue"&gt;break&lt;/span&gt;;

            streamOut.BeginWrite(buffer, 0, lengthRead, ae.End(), &lt;span style="color: blue"&gt;null&lt;/span&gt;);
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;1;
            streamOut.EndWrite(ae.DequeueAsyncResult());
        }
    }
}&lt;/pre&gt;

&lt;p&gt;这段代码利用了&lt;a href="http://msdn.microsoft.com/en-us/magazine/cc546608.aspx"&gt;Jeffrey Ricther提供的AsyncEnumerator组件&lt;/a&gt;。在每次发起一个异步操作之后，我们使用yield将操作控制权交给外部——实际上就是AsyncEnumerator组件，然后在异步操作结束之后，AsyncEnumerator又会调用迭代器的MoveNext方法，这样便可以于yield之后的代码继续执行了。在这里我们可以继续使用while，if，break等常见的控制语句来表述“线性”的逻辑，而编译器会为我们生成那些“支离破碎”的代码。至于异常控制，我们只需要在一处进行即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;YieldWebAsyncTransfer
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;private static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; GenerateTransferTask(
        &lt;span style="color: #2b91af"&gt;AsyncEnumerator &lt;/span&gt;ae, &lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context, &lt;span style="color: blue"&gt;string &lt;/span&gt;url)
    {
        ...
    }

    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AsyncEnumerator &lt;/span&gt;m_asyncEnumerator;

    &lt;span style="color: blue"&gt;public void &lt;/span&gt;StartAsync(&lt;span style="color: #2b91af"&gt;HttpContext &lt;/span&gt;context, &lt;span style="color: blue"&gt;string &lt;/span&gt;url)
    {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_asyncEnumerator = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;AsyncEnumerator&lt;/span&gt;();
        &lt;span style="color: blue"&gt;var &lt;/span&gt;asyncTask = GenerateTransferTask(&lt;span style="color: blue"&gt;this&lt;/span&gt;.m_asyncEnumerator, context, url);
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_asyncEnumerator.BeginExecute(asyncTask, &lt;span style="color: blue"&gt;this&lt;/span&gt;.EndExecuteCallback);
    }

    &lt;span style="color: blue"&gt;private void &lt;/span&gt;EndExecuteCallback(&lt;span style="color: #2b91af"&gt;IAsyncResult &lt;/span&gt;ar)
    {
        &lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;error = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
        &lt;span style="color: blue"&gt;try
        &lt;/span&gt;{
            &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_asyncEnumerator.EndExecute(ar);
        }
        &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)
        {
            error = ex;
        }

        &lt;span style="color: blue"&gt;var &lt;/span&gt;handler = &lt;span style="color: blue"&gt;this&lt;/span&gt;.Completed;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(handler != &lt;span style="color: blue"&gt;null&lt;/span&gt;)
        {
            handler(&lt;span style="color: blue"&gt;this&lt;/span&gt;, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CompletedEventArgs&lt;/span&gt;(error));
        }
    }

    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;CompletedEventArgs&lt;/span&gt;&amp;gt; Completed;
}&lt;/pre&gt;

&lt;p&gt;这就是yield的威力。yield本身只是个基础语言特性，但是有了这个特性，开发人员就能写出如AsyncEnumerator这样简化异步编程的类库，甚至&lt;a href="http://tomasp.net/blog/csharp-async.aspx"&gt;在一定程度上模拟F#中异步工作流的功能&lt;/a&gt;。同样的功能，有的语言只能写出编写困难理解不易的代码，而有的语言却让开发人员轻松地完成工作，而最终的成果也十分利于后期的维护。这个情况下，您还会说语言是不重要的吗？&lt;/p&gt;

&lt;h1&gt;轻量级任务&lt;/h1&gt;

&lt;p&gt;如果您有过VB（不是VB.NET）编程的经验，可能还记得当时是如何在进行长时间计算的情况下保持界面响应能力的。没错，就是使用DoEvents语句。DoEvents的作用是暂时将计算挂起，把控制权交还给UI，看看有没有什么事件需要响应，然后再继续DoEvents后的计算。其实yield从某些角度上看也有这样的效果，例如&lt;a href="http://msdn.microsoft.com/zh-cn/library/dscyy5s0(VS.80).aspx"&gt;MSDN上写道&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;yield关键字用于指定返回的值。到达yield return语句时，会保存当前位置。下次调用迭代器时将从此位置重新开始执行。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里的关键就在于“保存当前位置”并交出控制权，这时候我们便有办法根据需要进行下一步的处理。例如我们知道，操作系统进行任务调度的最小单元是“线程（Thread）”，此外Windows里有“纤程（Fiber）”，可用于在线程的基础上手动实现更小粒度的任务调度，还有一些如“协程（coroutine）”之类的概念也有相似之处。利用yield我们也可以在C#中实现更小粒度的任务概念，这只需要任务本身在合适的时候使用yield将控制权交还给外部即可。外部的任务调度逻辑可以在得到控制权的时候，判断是否继续当前任务还是切换到下一个任务。如此，我们便可以自己定义调度实现了。&lt;/p&gt;

&lt;p&gt;事实上，之前的异步编程在一定程度上也是基于这里的“轻量级任务”，只不过这个应用过于典型，因此单独拿出来强调一下。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;有人说，yield不该加入到语言之中，它破坏了语言的紧凑性。但我认为，yield本身是个再简单不过的语言特性，你几乎不会察觉到它的存在。更何况，yield本身的确大大降低了创建迭代器的难度，而迭代器本身可以说是系统中最常见的功能之一，因此我认为在语言中为其加入foreach和yield关键字的支持丝毫不为过。更何况我们也看到，yield本身也有超脱于迭代器之外作用，它们都源于我日常工作中的使用模式。因此在我看来，yield是一个不可或缺的语言功能，优雅，简单。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/speech-why-java-sucks-and-csharp-rocks.html"&gt;演讲预告：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-slides-final-version.html"&gt;幻灯片：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html"&gt;Why Java Sucks and C# Rocks（1）：比较的意义与目的&lt;/a&gt;&lt;span style="color: red"&gt;（重要）&lt;/span&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-2-primitive-types-and-object-orientation.html"&gt;Why Java Sucks and C# Rocks（2）：基础类型与对象&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-3-attribute-and-annotation.html"&gt;Why Java Sucks and C# Rocks（3）：Attribute与Annotation&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/why-java-sucks-and-csharp-rocks-4-generics.html"&gt;Why Java Sucks and C# Rocks（4）：泛型&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html"&gt;Why Java Sucks and C# Rocks（补1）：Reddit，兼谈C#属性&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-5-anonymous-method.html"&gt;Why Java Sucks and C# Rocks（5）：匿名方法&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/07/more-why-java-sucks-and-csharp-rocks-2-standard-event-model.html"&gt;Why Java Sucks and C# Rocks（补2）：标准事件模型&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;Why Java Sucks and C# Rocks（6）：yield及其作用 &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html#comments</comments>
      <pubDate>Sun, 18 Jul 2010 13:26:07 GMT</pubDate>
      <lastBuildDate>Sun, 18 Jul 2010 13:26:07 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>NDC 2010视频下载：看看其他微软平台程序员们都在做什么</title>
      <link>http://blog.zhaojie.me/2010/07/ndc-2010-videos.html</link>
      <guid>http://blog.zhaojie.me/2010/07/ndc-2010-videos.html</guid>
      <description>&lt;p&gt;&lt;a href="http://www.ndc2010.no/"&gt;NDC&lt;/a&gt;（Norwegian Developers Conference，挪威开发者大会）是一年一度的挪威最大的微软平台开发者大会，&lt;a href="http://www.ndc2010.no/agenda.aspx?cat=1071"&gt;内容丰富&lt;/a&gt;，&lt;a href="http://www.ndc2010.no/index.aspx?cat=1070"&gt;讲师阵容强大&lt;/a&gt;。NDC与&lt;a href="http://blog.zhaojie.me/2009/11/videos-of-pdc09-algorithms-data-structure-visual-studio-documentary.html"&gt;PDC&lt;/a&gt;同为高端技术会议，但NDC与PDC的不同之处在于，PDC是微软官方会议，主要是面向微软资深产品的深入探讨。而NDC涉及的内容则广泛的多，包括了我所感兴趣的Java、Mono、IronRuby/Ruby on Rails、NoSQL方面的内容。这也就像我一直强调的那样，微软技术社区非常开放，微软平台上的太多程序员都能够非常热情地拥抱其他平台的技术。那些认为微软技术社区是井底之蛙的兄弟，殊不知&lt;a href="http://blog.zhaojie.me/2010/03/microsoft-technology-and-the-attitude.html"&gt;你们的嘲笑只能体现出自身的狭隘&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;总而言之，NDC是我理想中的微软平台技术大会。&lt;/p&gt;

&lt;p&gt;如今&lt;a href="http://streaming.ndc2010.no/tcs/"&gt;NDC 2010的视频已经全部公开&lt;/a&gt;，可以在线观看（使用Silverlight），也可以下载。为了便于大家浏览和下载，我写了一段小程序整理出了所有的视频简介以及下载链接，共计123条，&lt;a onclick="$(&amp;#39;#ndc-video-list&amp;#39;).show(); return false;" href="#"&gt;请点击这里&lt;/a&gt;。&lt;/p&gt;

&lt;table style="display:none;" id="ndc-video-list" border="1" cellspacing="0" cellpadding="5"&gt;&lt;tbody&gt;
    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/919676F9-9BEB-4370-B9CA-F7EADB515143/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=919676F9-9BEB-4370-B9CA-F7EADB515143" target="_blank"&gt;Fubu MVC 18 Jun 10 4:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;FubuMVC is an open source framework for web development using the ModelView Controller (MVC) pattern. FubuMVC is built in C# and depends on theSystem.Web.Routing subsystem of the base CLR, but has no dependency on the ASP.Net MVC framework. In this talk I will highlight the architectural design of FubuMVC, how it works and more importantly the reasoning behind it. I will not make this into a comparison match with ASP.NET MVC, but there are so many interesting patterns being used in the FubuMVC code base that in its self should make this a very interesting talk. The FubuMVC code base has been an example for many others even in different areas outside of web development.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0F6607A2-4202-451C-95C6-51A3BC10D907"&gt;iPod Video 320x240 (71 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/32DA3486-A0A7-4561-9CC7-2D624E325E51"&gt;MPEG-4 for QuickTime Medium 1280x480 (330.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/A065D732-92B5-4965-A8E8-B91491B0D796/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=A065D732-92B5-4965-A8E8-B91491B0D796" target="_blank"&gt;Leading a Self-Organizing Team 18 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/D348B96D-330E-4453-B6B2-81EA2873C22E"&gt;iPod Video 320x240 (147.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/88A55AE0-6276-4980-8780-E2B38B78C5D6"&gt;MPEG-4 for QuickTime Medium 1280x480 (395.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/42B4E3B9-C0D3-4C34-AA29-115C5D322CF9/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=42B4E3B9-C0D3-4C34-AA29-115C5D322CF9" target="_blank"&gt;State vs. Interaction Testing 18 Jun 10 3:19 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Mocking frameworks allow you to stub out behaviour in order to perform tests of individual peices of functionality in isolation. However, there are times when performing certain actions and assert a result is not sufficient. In this session we will drill deep into unit testing and explain the differences between state and interaction-based testing. We will examine the role of stubs versus mocks and how to correctly write unit tests that are not fragile or counter-productive.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/09EA0A40-8964-4677-83F2-F315E7DF73FC"&gt;iPod Video 320x240 (94.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7579BDFE-7278-44D3-A4DB-598770BDCCDB"&gt;MPEG-4 for QuickTime Medium 1280x480 (349.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/575B06E8-755D-41B6-BC8A-22908491B66D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=575B06E8-755D-41B6-BC8A-22908491B66D" target="_blank"&gt;Bridging code between Java &amp;amp; .NET 18 Jun 10 3:18 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Frustrated .NET developer forced to work with old, boring java? Or are you a java developer having to work with the incompatible and evil .NET technology? With IVKM.NET you can compile your java byte code to the .NET IL and run your favorite java code on .NET and maybe even your Java applications will run faster under IVKM.NET than under SUNs JVM? We take a look at IKVM.NET and how it can be used to bridge your java libraries to your .NET application and the other way around, reusing .NET APIs in your java code. We also show how to use IVKM.NET for creating java-stubs from already existing .NET libraries in order to access your .NET code in java.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/ECD741DD-BD17-4960-A3BA-957E788AA8A8"&gt;iPod Video 320x240 (120.1 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EC41B80B-D810-40E8-8B18-03EEA09A369B"&gt;MPEG-4 for QuickTime Medium 1280x480 (410.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/A4176987-8DF0-491F-B25E-951925F755C3/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=A4176987-8DF0-491F-B25E-951925F755C3" target="_blank"&gt;Rough Cuts in Legacy Code 18 Jun 10 3:17 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Getting legacy code under test is hard, particularly when it is deeply intertwined. In some of the worst code bases, there are no real components, the code is just one large soup.In this session, Michael Feathers will describe a series of strategies and techniques that you can use to separate clusters of classes from your application, characterize them and write tests for them to enable deterministic change.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/732E92BE-DE16-4516-8EF9-2F2FE0852B58"&gt;iPod Video 320x240 (57 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/1D23F326-1241-4350-B873-74F799AA53F3"&gt;MPEG-4 for QuickTime Medium 1280x480 (223.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/6118D38B-9A20-4601-A470-1752FFE1BF30/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=6118D38B-9A20-4601-A470-1752FFE1BF30" target="_blank"&gt;The SharePoint 2010 Business Intelligence Soup 18 Jun 10 4:17 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;SharePoint 2010 has rich BI capabilities, much more enhanced than what SharePoint 2007 provided. Did you try using SharePoint 2007 as a BI delivery mechanism? How was the experience? Was it too difficult to setup? Did you feel you were limited on the lines of performance, capabilities, diagnosis ability? Did you think the featureset was great, and made for good demos ?C but delivering real solutions was a whole another story? Well is SharePoint 2010 any better? Come over and let??s talk about it. In this session, Sahil will talk about the various BI specific improvements in SharePoint 2010, with specific emphasis on what is new compared to SharePoint 2007.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/1CBBCF81-6F06-410A-AA7A-53C8609D1A01"&gt;iPod Video 320x240 (131.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C5CBFADF-B8BA-4616-881C-141F39440053"&gt;MPEG-4 for QuickTime Medium 1280x480 (606.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/DDE1EF15-1290-4AD8-B8F5-E7B3EBF6B61B/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=DDE1EF15-1290-4AD8-B8F5-E7B3EBF6B61B" target="_blank"&gt;LINQ - Beyond Queries18 Jun 10 4:14 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FB02936E-A742-42FB-9510-4BBA680C3B5D"&gt;iPod Video 320x240 (115.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B6530547-1CFB-4D80-AF39-F01FFCB5A0F8"&gt;MPEG-4 for QuickTime Medium 1280x480 (523.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/66BF46DF-0BB6-4A52-831C-28165F048D6E/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=66BF46DF-0BB6-4A52-831C-28165F048D6E" target="_blank"&gt;Scaling Agile to Work with a Distributed Team 18 Jun 10 2:00 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/F8E1F30D-6377-447A-BB02-6E1CE27D377C"&gt;iPod Video 320x240 (145.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0299E29A-0D32-42D1-B5BE-491F0C358D2F"&gt;MPEG-4 for QuickTime Medium 1280x480 (398.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/C95AB956-D65D-4F80-A75D-84E48767D061/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=C95AB956-D65D-4F80-A75D-84E48767D061" target="_blank"&gt;Get Your Business Out of My Face: Legacy Refactorings for Testable User Interface Code 18 Jun 10 1:58 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Traditional acceptance testing works at the front-end of a fully-integrated system. There is a need for this kind of testing, however the need accounts for a comparatively small amount of the overall checking you should do on your system. Unfortunately, effectively checking your system requires some basic design principles, which are typically not applied.This session will give you an idea of the kinds of design changes you?ll need to make to improve the testability of your system through a demonstration of legacy refactoring techniques targeted at getting business logic out of UI code.This session will be code-driven. I?ll begin with some example code, displayed for the audience. We will begin by writing automated tests to verify some of the business logic leaving the code as is.After writing a few tests, we will review the pros and cons of the testing approach.I will then provide a few basic design principles that apply to the particular situation: dependency inversion principle, single responsibility principle, not mixing enabling code with business logic.We will then make some ?obvious? legacy refactorings, done badly. Review the results. This first round will be an application of the Single Responsibility Principle. Then we will discuss the Dependency Inversion Principle and do the refactoring more effectively.We will continue with a comparison of the first tests with their final form and the first version of the business logic with the refactored version. We will review&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E3458620-1B2B-422A-87D6-93D58736580D"&gt;iPod Video 320x240 (92.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7D609906-A58D-4543-BFFF-11C0223E1723"&gt;MPEG-4 for QuickTime Medium 1280x480 (396.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/94B33824-779E-44D9-803B-BE462815A5D5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=94B33824-779E-44D9-803B-BE462815A5D5" target="_blank"&gt;No Source Code, No Problem - Reverse Code Engineering in Product Development 18 Jun 10 2:58 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Reverse Code Engineering (RCE) is commonly applied to solve security-related challenges. It is all about understanding the behavior of software without access to its source code. This often means studying low-level machine code generated from a higher level language like C or C++. Such translation is lossy and highly architecture and compiler dependent, which makes going in the reverse direction quite challenging.In this talk I'm going to discuss RCE from a product development perspective, where interoperability with platform-specific undocumented APIs could be a necessity. First some background including practical examples of key challenges solved by RCE from my own experience, followed by a live demonstration of some tools and techniques.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8E8FAC98-DC07-4CF3-955C-8003D05E35EE"&gt;iPod Video 320x240 (182.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DD5BD460-8404-4535-B640-28797F6257C9"&gt;MPEG-4 for QuickTime Medium 1280x480 (735.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/C44B65E2-980B-4406-B2AB-3E079E5CA804/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=C44B65E2-980B-4406-B2AB-3E079E5CA804" target="_blank"&gt;Collaboration in ABB - from drawing board to real-life solution using SharePoint 2010 18 Jun 10 2:57 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;ABB is a global leader in power and automation technologies, operates in around 100 countries and employs about 117,000 people. ABB's global intranet is a complex and highly tailored solution containing several hundred thousand pages in more than fifty country sites. The intranet was named one of "The Ten Best Intranets of the Year" by Nielsen Norman Group in 2002, but the general concept of the intranet has not evolved substantially since then. For several years ABB worked on new concepts for making the intranet more valuable to users, by increasing findability, quality of content and opportunities for collaboration. Late last year, SharePoint 2010 was selected as the technical platform to achieve these goals. This talk will outline the collaboration concept that ABB envisioned, and what happened when the concept from the drawing board met the real-life platform SharePoint 2010. The talk will focus on the "social" aspects of the solution such as profiles, networking with colleagues, sharing of information through status updates, activity feeds, social tagging and commenting. It will describe, feature by feature, which parts of SharePoint were used as they are, and which were tailored to achieve a more interactive and collaborative intranet for ABB. The talk will focus both on functionality and on hands-on technical implementation.The speakers will share their experiences of what worked well and what not in terms of conceptual design, development of improved or new social fea&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6AC313B1-389D-4BB2-80E1-7D682CF30D1B"&gt;iPod Video 320x240 (131.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/480C2CC2-DDBF-4198-8924-37573ED287AA"&gt;MPEG-4 for QuickTime Medium 1280x480 (614.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/6F6790C2-BB0F-46C8-8373-E00341777A59/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=6F6790C2-BB0F-46C8-8373-E00341777A59" target="_blank"&gt;Creating Web Sites with Open Rasta 18 Jun 10 2:57 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;OpenRasta is a framework built from the ground?Cup to unleash the power of HTTP. Be it web sites, forms or web services, OpenRasta lets you build your application quickly and efficiently, without the hassle of complicated APIs and god objects.
          &lt;br&gt;Come and discover why OpenRasta is the most talked about ReST framework on .net, and how it can help you deliver better, more http?Cfriendly systems.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/BE49FF2E-99C2-4BFC-9EDF-60AF8991AC65"&gt;iPod Video 320x240 (137 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2B25C121-C445-432A-9742-CE0AA63CE664"&gt;MPEG-4 for QuickTime Medium 1280x480 (679.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/002F9CBC-CBED-4D71-83A5-08C0EBED02BC/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=002F9CBC-CBED-4D71-83A5-08C0EBED02BC" target="_blank"&gt;Product Development in TANDBERG 18 Jun 10 1:57 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;TANDBERG has never cared much about documentation, procedures, methodologies and risk reduction. However, we do care very much about our culture and our principles. This has enabled us to outperform all competition in the video conferencing and telepresence market during the last decade.In retrospect, we realize that TANDBERG has for 10-15 years built a culture that is quite compatible with Agile and Lean ideas.This talk will give a glimpse into how we do product development in TANDBERG R&amp;amp;D at Lysaker. I will show an example of how we developed a particular product with emphasis on software development, before I dive into the principles that we follow.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E1EDF859-7EFB-43CC-829A-C75532A0AD85"&gt;iPod Video 320x240 (112.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8C9D31C1-411E-4E03-8F27-470D592ABDCB"&gt;MPEG-4 for QuickTime Medium 1280x480 (320.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/4AC3CE10-48E7-476B-9238-D7FC946DB8E3/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=4AC3CE10-48E7-476B-9238-D7FC946DB8E3" target="_blank"&gt;Clean Code III: Functions 18 Jun 10 1:51 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Get ready for a challenge as Robert Martin dives deep into the topic of clean Java code by examining what makes a good function. In this talk you will look at a lot of code; some good and some bad. You will experience how such code is analyzed, critiqued, and eventually refactored. You will understand the decisions made by an expert in the field as bad code is gradually transformed into good code. How big should a function be? How should it be named? How should it be documented. How many indent levels should it have? How should it deal with exceptions, arguments, and return values. This talk is all about code at the lowest level. And yet the principles and techniques presented have far reaching implications.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E2EEF5A5-8421-4818-9CA1-7A3F15484378"&gt;iPod Video 320x240 (149 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AF569962-C936-4542-9461-B51C4F6CAF86"&gt;MPEG-4 for QuickTime Medium 1280x480 (449.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/6EB9A2DD-E1F2-4E90-9DAE-E4BDA2FF7A8D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=6EB9A2DD-E1F2-4E90-9DAE-E4BDA2FF7A8D" target="_blank"&gt;Advanced Topics in Agile Planning 18 Jun 10 12:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Velocity is perhaps the most useful metric available to agile teams. In this session we will look at advanced uses of velocity for planning under special but common circumstances. We will see how to forecast velocity in the complete absence of any historical data. We will look at how a new team can forecast velocity by looking at other teams. We will see how to predict the velocity of a team that will grow or shrink in size. Most importantly we will look at the use of confidence intervals to create plans we can be 90% confident in, even on fixed-price or fixed-date contracts.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E449FDDF-8954-4445-8539-9882B3D6276B"&gt;iPod Video 320x240 (143.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/721E1266-5F00-489A-B262-1BFEA4605EC5"&gt;MPEG-4 for QuickTime Medium 1280x480 (396.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/9147ECD9-4F4B-48B2-95E6-B2602BDA37B0/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=9147ECD9-4F4B-48B2-95E6-B2602BDA37B0" target="_blank"&gt;SharePoint 2010 - Is it Scalable or not? 18 Jun 10 12:38 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;If there is any topic that has generated more debate, it is the scalability of SharePoint lists and document libraries. In this session, Sahil will talk about the new and improved details around the scalability, and performance aspects of SharePoint 2010. Topics covered will include List management infrastructure, content database schema improvements, and RBS capabilities, with practical gotchas and things to watch out for.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FA1C656E-F92E-48D3-8CF9-52B8A970CDFC"&gt;iPod Video 320x240 (98 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E09DE432-8F2D-447D-BE34-046D99A5C4DE"&gt;MPEG-4 for QuickTime Medium 640x480 (186 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/2E806A0F-059E-4D8C-B47C-F2D30B1FAFB4/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=2E806A0F-059E-4D8C-B47C-F2D30B1FAFB4" target="_blank"&gt;Aspect Oriented Programming: Learning by Re-Invention 18 Jun 10 1:38 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;This talk aims to provide an in-depth understanding of the concept of Aspect Oriented Programming and how an AOP framework does what it does, by taking the audience through the implementation of (a simple) one from scratch.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B38D034D-864A-4D51-8021-9DA3875EEC83"&gt;iPod Video 320x240 (154.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/BDA7204A-2953-42D7-A096-785D4A693CC4"&gt;MPEG-4 for QuickTime Medium 1280x480 (616.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/9FC4E2E6-9A9F-49C0-803B-A91E6AF41312/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=9FC4E2E6-9A9F-49C0-803B-A91E6AF41312" target="_blank"&gt;Agile Release Strategy 18 Jun 10 12:37 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Releasing often is probably the most important practice in agile development. In many projects it can also be the hardest practice. This talk will give you hands on advice on how to reliably reduce your release cycle.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/3992E103-507B-4D1D-A391-9689F886B062"&gt;iPod Video 320x240 (62.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9B396E05-C208-4E56-9351-95480160831F"&gt;MPEG-4 for QuickTime Medium 1280x480 (244.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/C6611670-4B70-42B3-9C2B-86D576338350/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=C6611670-4B70-42B3-9C2B-86D576338350" target="_blank"&gt;Testing C# and ASP.NET Applications with Ruby 18 Jun 10 1:37 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;With the reach and diversity of different programming languages andparadigms at the moment, it's possible to use the appropriate languagefor the appropriate task. In my case, I develop applications using C# but test the code using Ruby. The Ruby community has always understood the importance of testing. They strive to make applications more testable while improving the approaches and tools they use. As aresult, they have created some amazing frameworks and a series of best practices to support testing. While this is great for Ruby developers, C# and ASP.net developers can take full advantage for their own applications. This session provides an insight into the Ruby world and how you can take advantage to create readable, maintainable and valuable tests for ASP.net based web applications, from the business logic up to the user interface. Taken from my own experiences of using this approach, the session will demonstrate how to integrate Ruby frameworks such as RSpec and Cucumber into your application development cycle, and how different frameworks combined with Ruby can solve a number of problems traditionally faced when using C#. There will also be discussionaround IronRuby and JRuby and their implications for the future andthe future of Testing ASP.net applications in general.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E6ED8E7D-AC7B-41EE-A43E-AC43DA25AAE7"&gt;iPod Video 320x240 (98.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C547E00D-FB1A-4E07-AD76-AF4EEE00FEE3"&gt;MPEG-4 for QuickTime Medium 1280x480 (523.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/8FFAC6DC-C74E-4001-9908-9B3188A4569D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=8FFAC6DC-C74E-4001-9908-9B3188A4569D" target="_blank"&gt;The Art of the Method 18 Jun 10 12:35 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Have you ever looked at a method or function in someone else's code and felt that you've seen it before? Methods don't come in infinite varieties. There are some very common structural and semantic patterns which recur in code. Some of them occur because teams have adopted good coding and design conventions. others occur because methods tend to fall apart and degrade in similar ways.In this session, Michael Feathers will name and describe various types of methods seen in the field and talk about how a nomenclature for these methods can simplify and focus design.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4C88314B-CE08-4159-81D5-B67E15A7482A"&gt;iPod Video 320x240 (75.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/90C5D5F4-746A-4ADF-AD7B-A68739368E7D"&gt;MPEG-4 for QuickTime Medium 1280x480 (285.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/44950364-77F8-49AA-91D7-13441F398DD0/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=44950364-77F8-49AA-91D7-13441F398DD0" target="_blank"&gt;ASP.NET MVC vs Ruby on Rails - The .NET Rocks Smackdown 18 Jun 10 1:18 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Shay Friedman's comparison of ASP.NET MVC and Ruby on Rails gets redesigned for an NDC 2010 exclusive.
          &lt;br&gt;Carl Franklin and Richard Campbell joins in and moderates a smackdown between the two leading web frameworks featuring Rob Conery, Chris Hardy, Hadi Hariri, Scott Bellware and of course Shay Friedman.

          &lt;br&gt;This is you're chance to be part of a lively discussion which will be broadcasted on the leading .NET talkshow .NET Rocks this summer.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/66253B2B-7E0B-4986-8751-6EAA21110681"&gt;iPod Video 320x240 (286.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/D470DFFF-AB8F-4409-BE2A-389107460641"&gt;MPEG-4 for QuickTime Medium 320x240 (209 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/A8883298-AAF9-4E5A-91B1-2F7A31241F34/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=A8883298-AAF9-4E5A-91B1-2F7A31241F34" target="_blank"&gt;Spark Deep Dive 18 Jun 10 11:40 AM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7CFA11A7-6E2B-40C7-BC36-5E9F46D4EE04"&gt;iPod Video 320x240 (173.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A899D0DA-E3C0-4885-9312-1D71C8AD8E08"&gt;MPEG-4 for QuickTime Medium 1280x480 (619.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/4DE02ACE-8B75-4807-B135-5B0E260FEEDC/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=4DE02ACE-8B75-4807-B135-5B0E260FEEDC" target="_blank"&gt;SharePoint 2010 - Business Connectivity Services 18 Jun 10 10:39 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Business connectivity services, now enables you to bring external data into SharePoint and office with full CRUD (Create/Retrieve/Update/Delete) capabilities. This talk explains BCS in SharePoint 2010 in an end-to-end fashion, covering from the browser, through SPD, to the Visual Studio 2010 story&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A3C836AF-7328-41BB-80AE-420F7D70858D"&gt;iPod Video 320x240 (95.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/065C76BD-1683-49B1-93A3-5E28FB50C9CE"&gt;MPEG-4 for QuickTime Medium 1280x480 (442.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/222F58E0-D553-456A-B76F-BA566D351145/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=222F58E0-D553-456A-B76F-BA566D351145" target="_blank"&gt;Agile Estimating 18 Jun 10 10:38 AM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/71E23FCC-FDDC-4ED6-8546-FC2E85273F16"&gt;iPod Video 320x240 (126.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/32B1EDB6-92AF-432C-9EA1-E92CF0B9F518"&gt;MPEG-4 for QuickTime Medium 1280x480 (247.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/A8B70AC9-947E-49B1-AC5C-53F63CFD89BB/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=A8B70AC9-947E-49B1-AC5C-53F63CFD89BB" target="_blank"&gt;Using ReST to design a better OData 18 Jun 10 11:40 AM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E68630A5-D7F6-4FF9-8DEC-1D260FBBB520"&gt;iPod Video 320x240 (162.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/57133307-C4DA-4CF0-A548-28ED270F03FE"&gt;MPEG-4 for QuickTime Medium 1280x480 (777.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/DCFE1E9A-6DFA-4AFD-BA77-42C534E3894E/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=DCFE1E9A-6DFA-4AFD-BA77-42C534E3894E" target="_blank"&gt;Code Contracts 18 Jun 10 11:36 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Code Contracts are a new feature of .NET 4 and present themselves as a way of greatly simplifying our code by using the concepts of Design by contract. In this talk we will cover the basics of Code Contracts and see how they can be used in real-world applications, how they can be tested, and see if they are all they are cut out to be.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DCA3C0F8-139F-4E98-AFBD-AB98491FAFB8"&gt;iPod Video 320x240 (139.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EBD4AE5C-F5F7-4281-B8CE-5D34098C9E69"&gt;MPEG-4 for QuickTime Medium 1280x480 (633 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/AD824D8A-F579-4CF1-9A7D-B167CC11076A/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=AD824D8A-F579-4CF1-9A7D-B167CC11076A" target="_blank"&gt;Clean Code I: Arguments 18 Jun 10 10:35 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Keeping code clean is a simple matter of professional ethics. In this talk Robert Martin shows how a Java module can start clean, grow to become messy, and then be refactored back to cleanliness. Be forewarned: his tutorial is about CODE. We will put code on the screen and we will read and critique it. And then, one tiny step at a time, we will clean it. In this tutorial you will participate in the step by step improvement of a module. You will see the techniques of the Prime Directive (Never Be Blocked), and Agile Design Principles brought into play. You will witness the decision making process that Agile Developers employ to write code that is expressive, flexible, and clean. Finally, you learn an attitude of professional ethics that defines the software developer??s craft.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/3D8BC681-77E2-4581-B7C5-CFE60A0FE4B9"&gt;iPod Video 320x240 (111.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/89664554-99D7-44E5-A2B6-CDA9396C9411"&gt;MPEG-4 for QuickTime Medium 1280x480 (309.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/E524650B-F5BD-4450-8B39-502AAD7FC053/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=E524650B-F5BD-4450-8B39-502AAD7FC053" target="_blank"&gt;The Deep Synergy Between Testability and Good Design 18 Jun 10 10:34 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Many people moan about it the fact that their code is hard to test. They hack their code to make it testable and then they moan some more about how unit testing is an irritant, it makes code ugly. The fact of the matter is, it isn't true. There's a deep synergy between testability and good design. All of the pain that we feel when writing unit tests points at underlying design problems. In this session, Michael Feathers will, through a series of examples, show how you can use testability challenges to reconsider and improve your design.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FAB20016-6162-4712-9868-41656B8976B2"&gt;iPod Video 320x240 (79.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C1FD8E8B-5010-4F4E-A8B9-556BF8293189"&gt;MPEG-4 for QuickTime Medium 1280x480 (283.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/9DE90613-B2A2-4C0C-ADCC-DFC72E45D087/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=9DE90613-B2A2-4C0C-ADCC-DFC72E45D087" target="_blank"&gt;User Stories for Agile Requirements 18 Jun 10 9:20 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The technique of expressing requirements as user stories is one of the most broadly applicable techniques introduced by the agile processes. User stories are an effective approach on all time constrained projects and are a great way to begin introducing a bit of agility to your projects. In this session, we will look at how to identify and write good user stories. The class will describe the six attributes that good stories should exhibit and present thirteen guidelines for writing better stories. We will explore how user role modeling can help when gathering a project??s initial stories. Because requirements touch all job functions on a development project, this tutorial will be equally suited for analysts, customers, testers, programmers, managers, or anyone involved in a software development project. By the end of this tutorial, you will leave knowing the six attributes of a good story, learn a good format for writing most user stories, learn practical techniques for gathering user stories, know how much work to do up-front and how much to do just-in-time.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B70ECBFF-9581-4A1E-B56B-F8BB02428B8F"&gt;iPod Video 320x240 (132.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DC45E89C-7FF6-4BE2-B7D3-20D1B0322C83"&gt;MPEG-4 for QuickTime Medium 1280x480 (340.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/E241ACE5-D26B-41FA-8FA1-FBB3AD8DF5B3/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=E241ACE5-D26B-41FA-8FA1-FBB3AD8DF5B3" target="_blank"&gt;The Solid Principles of OO &amp;amp; Agile Design 18 Jun 10 9:19 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;What happens to software? Why does is rot over time? How does an Agile development team prevent this rot, and prevent good designs from becoming legacy code? How can we be sure our designs are good in the first place? This class presents the agile S.O.L.I.D. principles for designing object oriented class structures. These principles govern the structure and interdependencies between classes in large object oriented systems. The principles include: The Open Closed Principle, The Liskov Substitution Principle, and the Dependency Inversion Principle, among others.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/07841105-200B-4F85-BCAD-FFFF1F82D407"&gt;iPod Video 320x240 (140 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/3F8944E1-F57B-46AA-83CD-37E5297245A8"&gt;MPEG-4 for QuickTime Medium 1280x480 (579.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/FEAA8270-AA9A-49CC-8CEA-ECB47B238F13/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=FEAA8270-AA9A-49CC-8CEA-ECB47B238F13" target="_blank"&gt;Being an effective team leader 18 Jun 10 9:18 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In this talk we'll review practices and principles that make good team leaders into great ones. from basic communication and influencing skills to essential day to day practices and things to look out for - this is a session every team lead should be interested in.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5919CA3D-88CB-47FD-A289-3BAF710976C7"&gt;iPod Video 320x240 (108.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/896ED493-B83B-452E-8FCA-1FF2A066CB64"&gt;MPEG-4 for QuickTime Medium 1280x480 (509.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/4087616A-3B88-4DA5-9B9E-CE2FE2887F09/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=4087616A-3B88-4DA5-9B9E-CE2FE2887F09" target="_blank"&gt;The Dependency Inversion Principle Applied 18 Jun 10 10:17 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;This is a talk which aims to explain the Dependency Inversion Principle in practice. It is not a one hour theoretical explanation of what the principle states, but rather a real life demonstration of how it becomes a natural pattern to apply in the pursuit of a clean, maintainable design within the boundaries of a statically typed language. This is a ??code and commentary?? talk, with virtually no slides. Though the code demonstrated is C#, this is not a technology-specific talk.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A6038941-AC88-4CA4-B61A-2101789D0805"&gt;iPod Video 320x240 (154.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C1070B19-4AFE-433E-A326-54ADEDE09AE4"&gt;MPEG-4 for QuickTime Medium 1280x480 (637 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D8EC7885-B8D2-447B-AE10-DF5FAE0F626D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D8EC7885-B8D2-447B-AE10-DF5FAE0F626D" target="_blank"&gt;Testable C# 18 Jun 10 9:16 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Unit testing is valuable, but it's hard to go very far with it without realizing that sometimes languages make it easy and sometimes they make it hard. In this code rich presentation, Michael Feathers will present a series of testability traps in the C# language: features and ways of using them which make unit testing impossible without specialized tooling. He will also present a simple rule along with supporting concepts that you can use to sidestep all of them and produce C# code which is always easily testable.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/56B0175F-14F2-43D6-93C5-3703F4641F8C"&gt;iPod Video 320x240 (22.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/59C524C6-4073-482D-8196-C980B1DD2574"&gt;MPEG-4 for QuickTime Medium 1280x480 (234.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/74F9C23C-BCF0-4863-9968-BB53C3C677D5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=74F9C23C-BCF0-4863-9968-BB53C3C677D5" target="_blank"&gt;Developing Testable Web Parts for SharePoint 18 Jun 10 10:15 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The development of components for use in SharePoint is a complex process, and often seems to fly in the face of what is considered good development practice in a Test Driven Development world.In this session I will show how using some good design practices and tools such as Typemock Isolator you can develop testable components for SharePoint (2007 &amp;amp; 2010); often without even having to have SharePoint on your development PC.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5EA24C36-8D98-4AEA-9278-6954AD28850A"&gt;iPod Video 320x240 (145.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/72B7DC54-080D-4A61-85F4-48BE0C7ABD9A"&gt;MPEG-4 for QuickTime Medium 1280x480 (702.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/22ECAB7E-431D-4CAF-813D-DC3B36BFA601/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=22ECAB7E-431D-4CAF-813D-DC3B36BFA601" target="_blank"&gt;Introducing Spark View Engine 18 Jun 10 10:08 AM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6AB73F67-5C72-402F-BBC5-BEDA93CB6DB2"&gt;iPod Video 320x240 (134.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8B98994B-2226-4774-86DA-3585F6C72EC3"&gt;MPEG-4 for QuickTime Medium 1280x480 (481.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/62309161-262E-44BA-BDA0-B5653698089B/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=62309161-262E-44BA-BDA0-B5653698089B" target="_blank"&gt;Getting Agile with Scrum 18 Jun 10 8:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Scrum is one of the leading agile software development processes. Over 12,000 project managers have become certified to run Scrum projects . Since its origin on Japanese new product development projects in the 1980s, Scrum has become recognized as one of the best project management frameworks for handling rapidly changing or evolving projects. Especially useful on projects with lots of technology or requirements uncertainty, Scrum is a proven, scalable agile process for managing software projects.Through lecture, discussion and exercises, this fast-paced tutorial covers the basics of what you need to know to get started with Scrum. You will learn about all key aspects of Scrum including product and sprint backlog, the sprint planning meeting, the sprint review, conducting a sprint retrospective, activities that occur during sprints, measuring and monitoring progress, and scaling Scrum to work with large and distributed teams. Also covered are the roles and responsibilities of the ScrumMaster, the product owner, and the Scrum team.This session will be equally suited for managers, programmers, testers, product managers and anyone else interested in improving product delivery.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/73B922DE-AF72-4B27-8CFF-B49766D74A08"&gt;iPod Video 320x240 (108.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A476A315-A6AB-40D1-9CBA-1DA278999726"&gt;MPEG-4 for QuickTime Medium 1280x480 (330.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/373CB716-5F6B-4233-A5C0-7E91CB5FB89F/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=373CB716-5F6B-4233-A5C0-7E91CB5FB89F" target="_blank"&gt;Introduction to GIT 18 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;An introduction to distributed version control using Git, and how your VCS should work with you and not against you. How DVCS can completely alter your development process, streamline it, and help you produce better software, faster. Covering how local repositories speed up your development, multiple authoritative sources, distributed teams, multiple workflows, and some of the more distinct features of Git. With experiences from an OSS team on how the migration from SVN to Git has helped the project and changed how the team works (Fluent NHibernate).&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AAF63449-316A-45CB-A97A-7C6D8877B655"&gt;iPod Video 320x240 (152.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C6F26DA8-6A6A-44AF-A59F-FFD2197BD557"&gt;MPEG-4 for QuickTime Medium 1280x480 (717.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/B6E99D3E-FA79-4915-B5E2-CBF1A4E0D5C2/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=B6E99D3E-FA79-4915-B5E2-CBF1A4E0D5C2" target="_blank"&gt;Riding IronRuby on Rails 18 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The most famous Ruby?Cdriven framework is, by far, Ruby on Rails. With IronRuby, .NET developers can now take advantage of this incredible web framework without leaving their comfort zone. In this session, Shay Friedman will build an entire Web 2.0 site from scratch while using and explaining the key features of Ruby on Rails.
          &lt;br&gt;Come and see what Ruby on Rails is all about and what's made it the success it is today.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/92483947-503D-4840-8634-7DA959B54272"&gt;iPod Video 320x240 (108.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/00079578-7C66-4D7D-9C78-9547E4571317"&gt;MPEG-4 for QuickTime Medium 1280x480 (468.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/3DEF9777-6E67-4DF5-A0B6-BD274A17B610/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=3DEF9777-6E67-4DF5-A0B6-BD274A17B610" target="_blank"&gt;Developing for SharePoint 2010 18 Jun 10 8:57 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Sure you're a .NET developer. Maybe you are even a SharePoint developer. But have you seen development for SharePoint 2010? It comes with full tooling support in VS2010, and massive improvements in the platform itself. Developing for SharePoint is finally manageable, but is it easy to be a SharePoint developer? Come and find out!&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/030F0119-18CA-40DD-8BBC-DBA2D0C229A3"&gt;iPod Video 320x240 (154.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/3657FB2E-DE46-4EE6-A261-1B4EDB3985E1"&gt;MPEG-4 for QuickTime Medium 1280x480 (794.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/67F7C9FF-0F0D-4F2E-91C0-49C84FF19109/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=67F7C9FF-0F0D-4F2E-91C0-49C84FF19109" target="_blank"&gt;The Three Laws of TDD 18 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The jury is in, the case is closed. TDD works, and works well. In this talk Uncle Bob makes the point that TDD is not a testing technique at all. Rather, TDD is a way to ensure good architecture, good design, good documentation, and that the software works as the programmer intended. TDD is a necessary discipline for those developers seeking to become professionals. This talk is half lecture and half demonstration. Examples are in Java and Junit.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6B224D9C-5AC2-47A5-A6FF-04FC6F17E235"&gt;iPod Video 320x240 (186.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DEE43269-466E-495F-90BF-C018F4A9435E"&gt;MPEG-4 for QuickTime Medium 1280x480 (622.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D75877EB-3F20-4A9A-8B39-B016EE0419D5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D75877EB-3F20-4A9A-8B39-B016EE0419D5" target="_blank"&gt;The Next Big Thing Or Cool-Kid Koolaid? Slicing Through The Rhetoric of MVC vs. WebForms 18 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In this presentation Rob Conery dissects the arguments that are polluting the blogosphere, surrounding the discussion of whether ASP.NET MVC is "right" for you and your team. Passion, misinformation, assumptions and fear have pushed people into entrenched positions, leaving little room for intelligent thought. The goal of this presentation is to "shake loose" the rhetoric and give you some concrete ideas to think about if you're considering a move to ASP.NET MVC. In addition, Rob will try to offer an answer to the question: "Is WebForms Dead?"&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A9076CC9-36AB-4268-95CA-61093899D2FF"&gt;iPod Video 320x240 (104.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4B633AE1-54CF-4EDF-A790-F3B9B7EBFEFB"&gt;MPEG-4 for QuickTime Medium 1280x480 (592.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/29649A14-2F1B-428E-BB12-9A43A1676221/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=29649A14-2F1B-428E-BB12-9A43A1676221" target="_blank"&gt;C# Quo Vadis? 17 Jun 10 4:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;What??s next for C#? This session will not answer that question! Mads will open with about 10 minutes about the challenges and constraints of language design, followed by an open discussion on where to take C# involving fellow C# designers Eric Lippert and Neal Gafter, C# author and luminary Jon Skeet as well as you in a lively free-form discussion that can take us anywhere and will be absolutely once-in-a-lifetime.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8519E157-0163-4764-B005-19E00AEBD895"&gt;iPod Video 320x240 (75.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7CCBD189-9996-46CA-AFAA-045482B11CC7"&gt;MPEG-4 for QuickTime Medium 1280x480 (256.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D57D0BF0-E555-44CA-B9B5-DA42B211836D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D57D0BF0-E555-44CA-B9B5-DA42B211836D" target="_blank"&gt;ASP.NET performance for free using caching 17 Jun 10 5:39 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Is your ASP.NET application not performing like you wished it would? Performance not what it has to be? Have you considered caching? While many developers know the basics of caching in ASP.NET, there's actually a lot more possible than initially thought. Also, not every technique is good to solve every problem. In this session, we'll do an overview of all the options ASP.NET has to offer for caching and state management, helping you to get a better performing application.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5508E549-95EE-4279-8DEB-FB75E41A2BCC"&gt;iPod Video 320x240 (156.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/58481C42-44C0-4A13-9FA8-156AE1EB123A"&gt;MPEG-4 for QuickTime Medium 1280x480 (637.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/921B0A59-558B-411E-B77B-E384F4162661/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=921B0A59-558B-411E-B77B-E384F4162661" target="_blank"&gt;jQuery: Write less, do more 17 Jun 10 4:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;jQuery is a JavaScript library which allows you to develop solutions with less code, in less time. You can build interactive prototypes for your prospective clients, or take an existing solution and add new dynamic behaviour with little effort.We will see how jQuery can be used to quickly and concisely apply JavaScript behaviour to your web app. It will cover selectors, Ajax, DOM manipulation and more. The aim: to produce lean unobtrusive JavaScript with jQuery&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6DF9A5DF-0AC2-4894-875E-89DF042143E4"&gt;iPod Video 320x240 (73.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/41FBB71C-159A-4F82-9F4D-F545354E805A"&gt;MPEG-4 for QuickTime Medium 1280x480 (273.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/43BE894B-DB27-4B0C-897F-D75ED6FDCFFA/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=43BE894B-DB27-4B0C-897F-D75ED6FDCFFA" target="_blank"&gt;Beautiful Teams &amp;amp; Leaders 17 Jun 10 4:38 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;A look at what makes teams productive, and what makes team leaders effective. from team practices such as automation and communication, to team leads that grow and coach their people, confront problems and finds ways to make people better. this is an overview session. specific issues are elaborated in other sessions.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2F1C583E-947D-40D6-BCE7-672CAA5FB42D"&gt;iPod Video 320x240 (107 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9D2AF5FD-6B26-415F-9979-9153A71FFB78"&gt;MPEG-4 for QuickTime Medium 1280x480 (516.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/F552E0B6-C036-4A8D-BEEA-64E4B1B005CE/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=F552E0B6-C036-4A8D-BEEA-64E4B1B005CE" target="_blank"&gt;5 reasons why projects using DDD fail 17 Jun 10 4:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Many people try applying Domain Driven Design and #fail miserably.This presentation looks at five top reasons for failure and discusses how to avoid them.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2A7C85FA-CEC9-4253-AD88-EFD57FE69A6A"&gt;iPod Video 320x240 (140.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4F985E73-D2EC-49ED-B2CE-47ED4C17DDE2"&gt;MPEG-4 for QuickTime Medium 1280x480 (459.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/FF182A3A-3793-488C-BCEC-5F644640DF19/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=FF182A3A-3793-488C-BCEC-5F644640DF19" target="_blank"&gt;Introducing the .NET Service Bus 17 Jun 10 5:37 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The .NET services bus is part of the new Microsoft Cloud Computing Windows Azure initiative, and arguably, it is the most accessible, ready to use, powerful, and needed piece. The service bus allows clients to connects to services across any machine, network, firewall, NAT, routers, load balancers, virtualization, IP and DNS as if they were part of the same local network, and doing all that without compromising on the programming model or security. The service bus also supports callbacks, event publishing, authentication and authorization and doing all that in a WCF-friendly manner. This session will present the service bus programming model, how to configure and administer service bus solutions, working with the dedicated relay bindings including the available communication modes, relying on authentication in the cloud for local services and the various authentication options, and how to provide for end-to-end security through the relay service. You will also see some advanced WCF programming techniques, original helper classes, productivity-enhancing utilities and tools, as well as discussion of design best practices and pitfalls.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EEEC5FCF-389A-4C1D-A003-3D9D69A03CE0"&gt;iPod Video 320x240 (146.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C53E66C4-A805-40DB-9002-7CF7F0037E4A"&gt;MPEG-4 for QuickTime Medium 1280x480 (674.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/7CE9268F-045F-4AC1-AA46-6362276862BE/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=7CE9268F-045F-4AC1-AA46-6362276862BE" target="_blank"&gt;Architecting for the .NET Event Model 17 Jun 10 5:25 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;You may have a basic understanding of .NET's event model, but how can you best architect your applications to take advantage of .NET events? This session spends a few minutes on the basics, then provides real-world examples showing how you can design your applications to take advantage of the .NET event model for things such as:* Custom data binding in Windows Forms and Web Forms* Establishing relationships between business components* Creating world-class, end-user-configurable security* Localizing the user interface dynamically at run time&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/306C679F-CE26-4B71-8CE1-E3254AA2EF04"&gt;iPod Video 320x240 (123.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4BC61FCB-3C07-4199-B87B-B4EFAB733A70"&gt;MPEG-4 for QuickTime Medium 1280x480 (660.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/DF0361BF-1AE6-42CE-AAB3-963E424A1BBD/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=DF0361BF-1AE6-42CE-AAB3-963E424A1BBD" target="_blank"&gt;The Purpose of Leadership and Governance 17 Jun 10 4:21 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;There are three types of organizations: ordered, chaotic, and complex organizations. The best management approach depends on the type of organization, and the amount of rule?Cmaking a development manager?team leader should concern himself with. However, this distinction is a false (but useful) metaphor. In reality, development teams are complex _adaptive_ systems, meaning that they should be doing their own rule?Cmaking, as self?Corganizing systems.
          &lt;br&gt;However, self?Corganization alone is not enough. Management is needed for imposing boundaries and constraints. There's a great checklist available to managers and team leaders for defining boundaries to authority in self?Corganizing teams.

          &lt;br&gt;There are two kinds of purpose: archeo?Cpurpose and neo?Cpurpose. Both the team itself and the manager can assign a neo?Cpurpose to a team. This neo?Cpurpose is not the same as the goal of the project they are working on, or the goals of any of the stakeholders. One thing a manager?leader can do is to assign purpose to a self?Corganizing team. We will discuss how to do that.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/949994EB-AD53-4896-8F32-14E0CEE6C120"&gt;iPod Video 320x240 (129.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0F9AFB61-056F-4DE1-B24A-AD3AFAA831FC"&gt;MPEG-4 for QuickTime Medium 1280x480 (733.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/8114E030-D145-4C7E-B34C-B92312BFC87B/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=8114E030-D145-4C7E-B34C-B92312BFC87B" target="_blank"&gt;Building Applications with Silverlight 4 17 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Now that Silverlight 4 is released, find out what is new?changed and how to write great data-driven applications with Silverlight 4. Learn about how you can leverage Silverlight 4 for rich desktop applications using the new ??trusted application?? model and how to best take advantage of these features. Accessing data using RIA Services makes data-driven applications easier and can support a ViewModel development pattern approach. This session will be faced pace in overview, but deep in code. Few slides, mostly code.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AAC3D1FC-8980-4790-9245-80D3D5A9A923"&gt;iPod Video 320x240 (131.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EFC36FEC-ADD3-42FD-A098-CCF2FF86E279"&gt;MPEG-4 for QuickTime Medium 1280x480 (323.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D4CE2890-99BC-49C5-A668-485D952A894D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D4CE2890-99BC-49C5-A668-485D952A894D" target="_blank"&gt;From one web server to two: Making the leap to web farms 17 Jun 10 4:20 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/887B2602-DD14-412A-86E6-4DC9E7D63786"&gt;iPod Video 320x240 (143.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C3FF5782-F356-4D9B-BE63-B3A4AC8EFF37"&gt;MPEG-4 for QuickTime Medium 1280x480 (709.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/F87D064C-09C8-4380-B1FE-17E526199C0C/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=F87D064C-09C8-4380-B1FE-17E526199C0C" target="_blank"&gt;Code Excavations, Wishful Invocations, and Domain Specific Unit Test Frameworks 17 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In this talk I'll describe a technique for unit-testing code embedded in an impenetrable framework (such as Sharepoint, Silverlight, and BizTalk) that make such code inaccessible. We know we should write code that is easy to test because it has clean boundaries, but sometimes the vendors we work with make that kind of modularity just too hard. I will talk about how we can test such plug-in code by faking its environment in memory, simulating the underlying engines. As an example, I'll be showing SilverUnit, a framework to test code written for the Silverlight framework without driving through the browser. I'll talk about how the need for this sort of testing arose during silverlight development and how it allowed me to make sure my silverlight code worked as expected. even the UI logic.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/90A3BEBE-7A7B-4939-8285-4BF359DD4CA5"&gt;iPod Video 320x240 (102.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4223DA88-7FB5-40C4-996E-2D143531A276"&gt;MPEG-4 for QuickTime Medium 1280x480 (475.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/AFDB4098-C5C8-49DF-8C2A-7E317F63E72D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=AFDB4098-C5C8-49DF-8C2A-7E317F63E72D" target="_blank"&gt;If I Ruled the World - C# 5.0 According to Jon 17 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Now that C# 4 is out, thoughts are naturally turning towards what C# 5 might hold. Speaking from a position of breathtaking ignorance of what the team is actually planning, and without the safety net of a working implementation, I will outline a few ideas about what could be in C# 5. Some will be wacky, some mundane and perhaps even obvious. One thing's almost certain: this won't be the feature set of the real C# 5. Even so, it will provide some food for thought.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7B88225D-8837-4650-8451-2488B664F828"&gt;iPod Video 320x240 (126.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/55B05224-8CE6-479C-8AC3-89675931E559"&gt;MPEG-4 for QuickTime Medium 1280x480 (529.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/0C981B83-1787-4D62-B395-4016989F52C3/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=0C981B83-1787-4D62-B395-4016989F52C3" target="_blank"&gt;Domain Driven Entity Framework 17 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;If you think of your database as an "implementation detail", it is likely that you are not interested in building your all important domain classes based on a database or being bound by the limitations of a modeling tool. In the U.S., Telemark skiers cry "Free the Heel". In this session we'll make that a call to "Free the Domain Classes". We'll take a look at the different mechanisms that do not involve reverse engineering a database. In VS2010, EF's Model-First support and in the Entity Framework Feature CTP, the completely model-less Code Only support. We'll finish with a quick look at SQL Server Modeling's M language, which provides yet another option for defining entity classes without the EDM designer .&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/772818E8-B827-403F-ACF5-4121883C7241"&gt;iPod Video 320x240 (121.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6FC4CB4C-8D8B-4921-9854-077420D0311A"&gt;MPEG-4 for QuickTime Medium 1280x480 (422 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/434A8AAD-2209-48A9-A2A5-9E135FBD9648/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=434A8AAD-2209-48A9-A2A5-9E135FBD9648" target="_blank"&gt;Top Security Scenarios for WCF Services: On Premise &amp;amp; In The Cloud 17 Jun 10 4:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Today you will be hard-pressed to find an enterprise application that does not rely on distributed messaging and service-orientation. Client applications such as rich clients, Rich Internet Applications (RIAs) built with Silverlight or some flavor or AJAX, and those targeting mobile devices all access resources via services exposed to the intranet or Internet. Oftentimes the middle tier also includes layers of services living in the DMZ or behind it. There are many possible security models available for scenarios involving the various client technologies and service tiers - and Windows Communication Foundation (WCF) supplies the tools necessary to implement each and every possibility. In this session, you will learn the most common and practical security scenarios that involve WCF services within the intranet or exposed to the Intranet including classic Windows security, username and password, certificates, federated identity, REST-based and securing calls between tiers. The session will also discuss scenarios that can benefit from aspects of Windows Azure platform including AppFabric Service Bus and Access Control - such as for securing services behind the DMZ and enabling federation for REST-based services. All examples will cover requirements for the client and service, give you a formula to achieve each scenario, and show you custom components that simplify implementation. You??ll leave this session with a recipe for the most common security scenarios including sample code&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/007682F1-899E-4F92-A036-1E0DFB338C24"&gt;iPod Video 320x240 (152.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FABD9586-E4EC-4890-A59A-A2C24089F4C0"&gt;MPEG-4 for QuickTime Medium 1280x480 (727.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/815EADB7-066D-4516-A70F-31EEFDFB1DE2/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=815EADB7-066D-4516-A70F-31EEFDFB1DE2" target="_blank"&gt;MonoTouch Deep Dive 17 Jun 10 2:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;A deep dive into developing applications with MonoTouch. Learn about the different UI components available on the iPhone and iPad and how to use these with MonoTouch as well as how you can create interoperability with your .Net libraries within MonoTouch.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/025D02B1-ECF6-45EE-A5DC-13C25A210FD4"&gt;iPod Video 320x240 (108.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2DB58A2E-126D-468B-A53B-8DB6E03194AB"&gt;MPEG-4 for QuickTime Medium 1280x480 (294.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/29F8ADBD-2479-4DC1-A2D2-B4D6BFF13C31/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=29F8ADBD-2479-4DC1-A2D2-B4D6BFF13C31" target="_blank"&gt;What I've learned About DDD Since the Book 17 Jun 10 2:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In the 5 years since the book was published, I've practiced DDD on various client projects, and I've continued to learn about what works, what doesn't work, and how to conceptualize and describe it all. Also, I've gained perspective and learned a great deal from the increasing number of expert practitioners of DDD who have emerged.The fundamentals have held up well, as well as most patterns, but there are differences in how I do things and look at things now. I will try to describe them, very informally, in this talk.Over this time, I have folded in a couple of additional patterns, and essentially come to ignore a few, but the biggest change has been a subtle shift of emphasis. Ubiquitous Language and Context Mapping and Core Domain are at the center, with aggregates in close orbit. Why, I ask myself, did I put context mapping in Chapter 14? Core domain in Chapter 15?! Before the book, it seemed self-evident to me that SOA fit well with DDD, but five years of questions on that topic have made it clear that my early explanations were inadequate and helped me clarify how it fits. Increased emphasis on events and distributed processing have crystallized the significance of aggregates and refined the building blocks.The talk cannot go into depth on all these topics, but the goal will be to give a quick look at where my view of DDD has been heading.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/1D0BC65F-5272-4EC2-889F-84AB41CAB3A6"&gt;iPod Video 320x240 (117.1 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2318F2BA-25D5-4739-A29A-FED9715814C7"&gt;MPEG-4 for QuickTime Medium 1280x480 (448.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/6750B4DA-632C-4888-B9BD-2F1D44D4C930/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=6750B4DA-632C-4888-B9BD-2F1D44D4C930" target="_blank"&gt;Advanced Tips &amp;amp; Tricks for ASP.NET MVC 2 17 Jun 10 1:57 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In this session we??ll not only look at improving the maintainability and performance of an MVC application, but also how to increase your productivity. Topics include model binding, meta-data providers, and T4 Templates.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/1FF232FC-BA01-45D8-9398-59587B1AEF84"&gt;iPod Video 320x240 (111.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B9D2CB7C-FF4D-4C5C-9C16-011EA9AE3DBE"&gt;MPEG-4 for QuickTime Medium 1280x480 (416 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/1D104BB5-4312-4ABE-A6B1-23AF9FB02D39/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=1D104BB5-4312-4ABE-A6B1-23AF9FB02D39" target="_blank"&gt;C# in the Big World 17 Jun 10 1:57 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;C# 4.0 focuses on being a good citizen in a big world. In this talk we look at named and optional arguments, as well as the much improved COM interaction. We pay special attention to the new dynamic feature and the Dynamic Language Runtime (DLR) that it builds on: How do they work, how can you use them and why did we design them this way. We??ll also interoperate with COM and the HTML DOM, and build our own dynamic objects.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E2BAD064-94AB-4EE0-B3FC-5A8D5C9E7817"&gt;iPod Video 320x240 (110.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A5BD7F69-EFC7-4D81-BA75-CE26620E2961"&gt;MPEG-4 for QuickTime Medium 1280x480 (365.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/0054B7C0-A854-485D-8BE2-0E2DC7D3D738/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=0054B7C0-A854-485D-8BE2-0E2DC7D3D738" target="_blank"&gt;Tasks &amp;amp; Threading in .NET 4.0 17 Jun 10 3:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Writing multi-threaded applications is hard - making them work is even harder. Scaling applications to the current and future multiple-core machines can really be a daunting task --- but it doesn't have to be! In this session, Ingo Rammer shows you the new task-based API and how it simplifies the creation of multi-core supporting applications. You will learn how you can take advantage of the fine-grained parallelism and control which is offered by this new .NET feature. Ingo will also show you how to extend your in-memory LINQ query to run in parallel, and how the new Visual Studio 10 debugging tools will make troubleshooting this kind of applications a lot easier.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2BD48D9B-02EF-477C-8CF5-251777A751FB"&gt;iPod Video 320x240 (158.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2BFDE6B9-30AA-437F-BE2B-FE99B0D14C7A"&gt;MPEG-4 for QuickTime Medium 1280x480 (579.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/54B70126-AF2C-4627-B47B-CDBE2BDEC8B1/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=54B70126-AF2C-4627-B47B-CDBE2BDEC8B1" target="_blank"&gt;Making Manual Testing a Part of Your Development Process 17 Jun 10 3:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Software has always needed to be tested manually, automation can help, but it is never going to replace the need for manual testing totally. How a team manages this requirement for manual testing can be key to a projects success or failure.In the 2010 release of Visual Studio, Microsoft have provided a whole new set of tools to aid in this process - Microsoft Test Manager. In this session I will show how MTM can be used to assist a tester in creating detailed, accurate and repeatable testing that are a joy to use (well might be stretching a point there!). Also I will show how the tooling can allow these manual tests can become the basis for automated tests and Coded UI tests, and how the advanced logging features of the tools allow bugs to be accurately passed back to developers for speed the production of fixes.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E162C105-0D34-4DB1-BCD9-47F94256CB3B"&gt;iPod Video 320x240 (137.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0B0342C0-CA91-4332-A319-C5D7C0FD871B"&gt;MPEG-4 for QuickTime Medium 1280x480 (644.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/2E7AD1AC-9998-47B7-95FB-8DE7B6E1041B/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=2E7AD1AC-9998-47B7-95FB-8DE7B6E1041B" target="_blank"&gt;Windows Identity Foundation and Windows Azure 17 Jun 10 3:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Claims-based identity provides an open and interoperable approach to identity and access control that can be consistently applied both on-premises and in the cloud. Come to this session to learn about how Windows Identity Foundation can be used to secure your Web Roles hosted in Windows Azure, how you can take advantage of existing on-premises identities and how to make the best of features in our cloud offering, such as certificate management and staged environments.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2CF8F8D0-4F6B-4E19-8C05-57D11CAD970C"&gt;iPod Video 320x240 (110.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/3D2CE3C3-DC4C-40B6-8A49-7FB0A670EEB1"&gt;MPEG-4 for QuickTime Medium 1280x480 (572.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/CB98EEE9-56A1-44BB-834B-6F8806A79B55/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=CB98EEE9-56A1-44BB-834B-6F8806A79B55" target="_blank"&gt;Software Is Not Manufacturing 17 Jun 10 12:45 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;While we assert that software development is not manufacturing, we often slip into manufacturing metaphors and analogies and then fail to extricate our explorations of how software development unfolds from manufacturing. Some of these analogies are so deeply-rooted into our customs that we readily contradict our own assertions. This presentation looks at just how handicapped your software development becomes at the hand of these engrained manufacturing perspectives. It looks at product development theory as a better analogy to software development and a more practicable body of knowledge for software development. And it looks at how even product development theory fails to illuminate software development when we backslide into manufacturing-specific product development. Lean and Agile methods are framed in terms of product development and software development productivity problems are laid open under the surgical precision of product development analogies to building software machines.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4CFECB5D-59FA-42F0-9FB6-D8966060FEA3"&gt;iPod Video 320x240 (62.1 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6D1E1D79-E553-467C-8E56-3D069878C8E9"&gt;MPEG-4 for QuickTime Medium 1280x480 (255.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/65BE4F1E-4CA8-44E0-8285-973DDE22AC94/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=65BE4F1E-4CA8-44E0-8285-973DDE22AC94" target="_blank"&gt;Introduction to MonoTouch 17 Jun 10 12:39 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;An overview of what's possible using Monotouch, Novell's tool to enable C# and .Net based applications for the iPhone, iPod touch and iPad. Find out what you need to start using Monotouch and how to create a sample application. If you have any questions on why you'd use this, what are the benefits and downsides of using Monotouch then this is your place!&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/56695511-6E17-4709-AD44-AEDF8FAF7093"&gt;iPod Video 320x240 (90.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/ECAC6BFF-B513-48B7-900B-B90E5502A650"&gt;MPEG-4 for QuickTime Medium 1280x480 (251.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/327039DB-BFD5-4465-BE31-A13BE5EAAF47/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=327039DB-BFD5-4465-BE31-A13BE5EAAF47" target="_blank"&gt;Technology Supported Requirement Handling and Estimation 17 Jun 10 1:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Lindorff Group is a leading outsourced receivables management company in Europe, and one of the leading on a global basis. For the past three-four years, they have increasingly adopted agile practices, especially in a large scale project converting and improving an old system written in Powerhouse to new .Net-technology.The results from adopting agile practices have been mostly positive, however, there were challenging issues related to requirement handling and estimation that needed attention. In 2008, Lindorff joined a project with Symphonical funded by Innovation Norway. The purpose of the project was to develop software for requirements handling, estimation and knowledge management for the Symphonical platform. Symphonical is a flexible web-based collaboration platform, where users can brainstorm, plan, organize and coordinate any process.This talk presents results from a case study detailing the challenges faced by Lindorff as they simultaneously adopted agile practices and introduced a new system for requirement handling and estimation. According to the respondents of the case study, Lindorff appears to have improved on two of the main points of concern presented in the 2007 study: requirement handling and estimation. Many users report that the introduction of agile methods and the platform Symphonical has improved the quality of important work processes. Furthermore, they report that the introduction of Symphonical has provided a framework for structured discussions re&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/03FEC611-7B4D-411F-A308-5DC675381D84"&gt;iPod Video 320x240 (124.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/190BED7E-FF81-4AEB-8C50-30B9BF466EA4"&gt;MPEG-4 for QuickTime Medium 1280x480 (594.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/316D856E-9734-473E-BD75-D6A675BDAEB7/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=316D856E-9734-473E-BD75-D6A675BDAEB7" target="_blank"&gt;Rocking AppFabric Access Control: Practical Scenarios, Killer Code and Wicked Tools 17 Jun 10 12:38 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;AppFabric Access Control is a feature of the Windows Azure platform that makes it easy to secure web resources such as REST-based services using a simple set of standard protocols. In fact, AppFabric Access Control uniquely facilitates several scenarios not previously possible including a standards-based mechanism for securing web resources, identity federation for REST, and secure calls from Silverlight and AJAX clients to web resources including REST-based WCF services or REST-based MVC implementations. In this session you will get a tour of the AppFabric Access Control feature set and learn how to implement these key security scenarios with the help of some custom tools that encapsulate common functionality exposing a simple object model for working with the protocols underlying Access Control. In addition, you will learn how to integrate typical Windows Identity Foundation (WIF) authorization techniques such as ClaimsPrincipal to decouple the authentication and authorization mechanism from the business logic.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/D10B3B51-9157-4FD3-822B-449AA11EBEA7"&gt;iPod Video 320x240 (138.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/48237FF8-8CCF-43D4-B3C5-6B6335AFE57E"&gt;MPEG-4 for QuickTime Medium 1280x480 (570.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/06AF739D-271D-4D24-9058-1935D9E304F9/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=06AF739D-271D-4D24-9058-1935D9E304F9" target="_blank"&gt;Unleash Your Domain 17 Jun 10 12:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Our application runs over 10,000 sustained transactions per second with a rich model. The key? Modeling state transitions explicitly.In today's world many systems have non-functional requirements that prevent them from being single database centric. This presentation looks at how Domain Driven Design can fit into such environments including extremely large scale web sites, batch processing, and even using highly scalable backing stores such as CouchDb or HyperTable.Event streams, a different way of storing the current state of an object, open many doors in this session not only in how we scale and store our domain but also in how we rationalize about it.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2DDA5370-3246-4442-802D-D131512D696A"&gt;iPod Video 320x240 (112.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/72B5743C-570D-4A93-B315-A78A859F67CC"&gt;MPEG-4 for QuickTime Medium 1280x480 (410 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/BB983DB3-FBB5-4898-848E-40CBB8794169/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=BB983DB3-FBB5-4898-848E-40CBB8794169" target="_blank"&gt;Testers Are Not Your Enemy 17 Jun 10 1:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Manual testing has been marginalized in agile development. Usdevelopers seems to think that manual testing is outdated and shouldnow be replaced by automatic test scripts. I think that manual testingshould still be a part of the development process, and in this talk,you will learn why that is and how we can integrate manual testers onagile teams.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DAE1B9CB-6ABC-4DF7-849E-2DBD1FE2669A"&gt;iPod Video 320x240 (66.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0F857841-5D33-49FA-9A35-E2FCB80607F8"&gt;MPEG-4 for QuickTime Medium 1280x480 (330.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/6660D5A9-C231-4EE3-8F9A-D359F15CCABB/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=6660D5A9-C231-4EE3-8F9A-D359F15CCABB" target="_blank"&gt;Improving Your ASP.NET Application Performance with Asynchcronous Pages and Parallel Extensions 17 Jun 10 1:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Asynchronous pages and handlers can be used in ASP.NET to improve the performance of the application, especially the throughput, but wrongly used can lead to unexpected behavior, including a degraded performance. One of the key technologies that are part of the Visual Studio 2010 is Parallel Extensions. So come to this interactive session to see how you can benefit from those new technologies and how they can help you to mitigate some of the problems.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AC43AF8E-6F1F-4E12-AE44-574740C6601E"&gt;iPod Video 320x240 (141.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/03F4B3EA-4E22-45E7-989D-8FF1A67BF475"&gt;MPEG-4 for QuickTime Medium 1280x480 (674.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/B663FE0E-7F19-482E-95BF-D7AB9A4C9CBE/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=B663FE0E-7F19-482E-95BF-D7AB9A4C9CBE" target="_blank"&gt;Fluent Nhibernate 17 Jun 10 11:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;(misses 4 minutes in the start)An introduction and overview to object?relational mapping using Fluent NHibernate. See how Fluent NHibernate can help you map your domain with the least amount of effort, how you can remain flexible with your database, and how to drive your design through convention?Cover?Cconfiguration; all without writing a single line of XML.
          &lt;br&gt;The talk is an introduction to Fluent NHibernate for those that aren't familiar with it, and assumes some NHibernate experience and is for .Net developers primarily. The goal is to show people how low?Cimpact NHibernate can be with Fluent NHibernate, and how it can actually speed up development in rapid?Cchange environments.

          &lt;br&gt;I'll cover an overview of what Fluent NHibernate is, the various parts of it (the fluent interface, the conventions, and the auto?Cmappings, and the configuration aspect), then expand on how these features can be utilised to improve your NHibernate experience and simplify your development process.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/715B25D0-4BA3-496D-996B-2328D03F336F"&gt;iPod Video 320x240 (94.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A0DE0481-5A63-4046-9442-35F20D826175"&gt;MPEG-4 for QuickTime Medium 1280x480 (512.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/C567290C-750D-4597-96F2-44699BD20F35/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=C567290C-750D-4597-96F2-44699BD20F35" target="_blank"&gt;Folding Design to Agile 17 Jun 10 10:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;After a decade of heavy process, the Agile revolution of the late '90s threw off the dead hand of big upfront design. The bloody purge that followed was needed!There were unintended consequences. Too many teams interpret "Agile" as a permit to not think about design. But if they have ambitious goals, Agile teams need more than standup meetings and iterations. Many teams get off to a quick start, building lots of features in early iterations, but end up with a "Big Ball of Mud". Without clear and well-structured code, they cannot sustain their pace and also put themselves at risk of, one day, encountering a critical feature they simply cannot deliver. Without the common understanding between developers and stakeholders that is forged in domain analysis, one of the greatest benefits of iteration, the deepening communication about what the software should do and how it should do it, is never realized.We must not return to the "Analysis Paralysis" that we used to endure (and that many teams still do), but interpreting "Do the Simplest Thing" as "Do the Easiest Thing" doesn't work either.This talk will consider ways of incorporating modeling and design into the iterative process in a lightweight way that increases communication with stakeholders and decreases the likelihood of painting ourselves into corners, without returning to the dead-hand of the analysis phase. As a concrete example of how such techniques can be incorporated into the Agile framework, we'll have an overview of&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E45EE484-17D1-457C-8E6E-9FE0A4009D56"&gt;iPod Video 320x240 (88.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7157FD1C-F6FA-49D5-A89A-D51EFF373CC6"&gt;MPEG-4 for QuickTime Medium 1280x480 (234.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/9569A807-9FF8-45F7-B076-566EB839EDE5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=9569A807-9FF8-45F7-B076-566EB839EDE5" target="_blank"&gt;Silverlight data access and services not for the faint of heart 17 Jun 10 10:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;For data needs, Silverlight can talk to services like WCF or REST enabled services. These service types are sufficient for most scenarios. But what if it isn't? Most examples that can be found out there cover the basics, but in the real world, that's sometimes not enough. In this session, we'll explore the dark corners of Silverlight??s service access. Among others, we??ll cover duplex communication, debugging services, the HttpWebRequest, TCP communication and securing service communication from Silverlight.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E128F1C5-4334-4A85-972E-054F356D8944"&gt;iPod Video 320x240 (126.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8E5C9B51-C6FA-48F9-B71D-0924A7150390"&gt;MPEG-4 for QuickTime Medium 1280x480 (414.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/3F9663A1-95AD-49D4-BF60-B248EB7B7824/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=3F9663A1-95AD-49D4-BF60-B248EB7B7824" target="_blank"&gt;Zen of Architecture 17 Jun 10 10:37 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Wonder about architecture best practices, guidelines and pitfalls? Wonder how to design world?Cclass systems? You understand the concepts but not how to apply them? In the first half of this high pace session, Juval will explain his original approach to large system analysis design. Then, he will discuss logical tiers, security, interoperability, scalability, transactions, and other aspects of a modern application. You will see how to approach rarely discussed topics such as allocation of services to assemblies, allocation of services to processes, transaction boundaries, identity management, authorization and authentication boundaries and more.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/23E152C9-A082-47A7-9752-CF4E6E763CB4"&gt;iPod Video 320x240 (101.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6DE2F70E-E9EA-467F-987B-CA60D0832EC6"&gt;MPEG-4 for QuickTime Medium 1280x480 (393.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D55B1D6D-1B66-4AE3-90C0-4A299CFDD207/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D55B1D6D-1B66-4AE3-90C0-4A299CFDD207" target="_blank"&gt;Modularization, Testing &amp;amp; Technical Debt in a Large Agile Project 17 Jun 10 10:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;This experience reports focuses on the major scrum?Crelated technical challenges that arose during a 120 000 hour scrum controlled project. For each of them, we try to identify the cause and the consequence, and then follow up with any solutions we tried. Finally we sum up and assess whether the problem was successfully solved or not.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4ECFFFF5-F771-4AFF-BF19-E9EC4CD36913"&gt;iPod Video 320x240 (66 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4B65502D-0303-4A4C-9649-76FAC5D4BAC6"&gt;MPEG-4 for QuickTime Medium 1280x480 (294.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/7CA2DE20-2C1E-4E15-B962-5A61426B2218/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=7CA2DE20-2C1E-4E15-B962-5A61426B2218" target="_blank"&gt;Hardcore .NET Production Debugging 17 Jun 10 11:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;But ... it used to work yesterday! In this newest version of his classic session, Ingo Rammer will introduce the hardcore and low-level tools used for production debugging of .NET applications. You'll learn how to attack the nastiest bugs in your applications, how to look at what's causing that grinding halt of your ASP.NET application and how to find the cause of that horrible memory leak in your Windows Forms application. Knowledge of these production debugging tools like WinDbg and SOS is not only important for cases when you really don't have access to Visual Studio and your source code, but these tools also reveal a lot more information than just the regular managed code debuggers.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/02A5543D-E2A8-49F9-813B-86629B1505A7"&gt;iPod Video 320x240 (165.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7D7AC028-F469-441A-B9F6-3694E2259AAF"&gt;MPEG-4 for QuickTime Medium 1280x480 (697 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/B28EBB6E-460F-4951-90A8-C4F10E3E368A/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=B28EBB6E-460F-4951-90A8-C4F10E3E368A" target="_blank"&gt;Solid C++ code by example 17 Jun 10 11:36 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Sometimes you see code that is perfectly OK according to the definition of the language, but which is flawed because it breaks too many established idioms and conventions. On the other hand, a solid piece of code is something that looks like it is written by an experienced person who cares about professionalism in programming.
          &lt;br&gt;This will be an interactive discussion about good vs bad C++ code. We will discuss simple C++ idioms and coding conventions, but we will also touch upon best practices when working with C++ in large codebases with lots of developers with mixed skills.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5B9E29D6-C172-49A7-B59C-8E5C0678DA3D"&gt;iPod Video 320x240 (98.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AD7A5C8E-F267-466B-AAC4-DE2A9C925EFB"&gt;MPEG-4 for QuickTime Medium 1280x480 (337.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/33E5A226-B72F-425A-AFE1-A0F10324EF5E/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=33E5A226-B72F-425A-AFE1-A0F10324EF5E" target="_blank"&gt;Silverlight Applications for Windows Phone 7 17 Jun 10 9:20 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Windows Phone 7 is brand new, totally fresh operating system that will appear in phones before Christmas. The new platform is a complete rewrite and offers lots of interesting opportunities to third party developers. The development platform for Windows Phone 7 is all based around managed code and the tools and frameworks you already know and love. This presentation you will give you an overview of the Windows Phone 7 development platform, and how you can leverage your existing Silverlight skills to build great applications for the Windows Phone 7 marketplace. The session assumes some prior knowledge of Silverlight, as the focus of this presentation will be features that are specific to the phone. It will not only cover the basics of the Windows Phone 7 platform, but also how you can re-use many of the same patterns, frameworks, techniques and practices that you use when building regular Silverlight applications.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DB82EF1C-7FE5-4149-AF3D-8A9C5E769E9B"&gt;iPod Video 320x240 (115.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E358E185-84EA-486D-9797-34EA9DC733F5"&gt;MPEG-4 for QuickTime Medium 1280x480 (366.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/BCEF7A82-56FF-45FE-9580-2DED9406FB9A/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=BCEF7A82-56FF-45FE-9580-2DED9406FB9A" target="_blank"&gt;Entity Framework Persistance Ignorance 17 Jun 10 9:20 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Entity Framework in .NET 4 has finally embraced agile development. Thanks to it's new POCO support, you can now build completely persistent ignorant entity classes. In this session, we'll look at building an intelligent repository from entity classes and mocking up some extra classes in order to build unit tests against methods that have some dependency on the Entity Framework without touching the EF APIs. A prior understanding of the PI and Unit Testing should keep your head from spinning too much.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/340C88A5-8F8C-460A-ABD2-A1A92315FDBC"&gt;iPod Video 320x240 (115.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/47A57CCD-C1B6-4230-B677-A2995D00773F"&gt;MPEG-4 for QuickTime Medium 1280x480 (425.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/EE42F3A4-4B04-43A1-962E-22870D7C1877/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=EE42F3A4-4B04-43A1-962E-22870D7C1877" target="_blank"&gt;Service Oriented Development Process 17 Jun 10 9:20 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;When you develop a service-oriented application, it would be naive of you to expect that the only things you will do differently will be limited to design and technology. The development process itself needs to be service-oriented. You cannot "stare into the fire" of WCF without a mature service-oriented development process supporting your effort. This talk presents you with a service-oriented development process that you can apply to your WCF-based products to achieve robust applications, manage requirements and ensure faster time to market.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5AC07B0F-EFE3-4871-ABCF-B36B87842B06"&gt;iPod Video 320x240 (90.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/97F3A10F-0EE7-49BF-B738-031C8D4F51EA"&gt;MPEG-4 for QuickTime Medium 1280x480 (324.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/7A0F3A2B-AD46-47E8-8748-5BAF9611B4E3/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=7A0F3A2B-AD46-47E8-8748-5BAF9611B4E3" target="_blank"&gt;IronRuby - A Brave New World for .NET 17 Jun 10 10:18 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;.NET developers are about to enter a brave new world. With Microsoft'sDynamic Language Runtime (DLR) developers can start taking advantageof dynamic languages, such as IronRuby and IronPython, on top of the.Net platform and integrating the language into their existing .netbased applications. However, why is this important?In this session, Ben will provide an insight into the deep darksecrets of why he loves the Ruby language and how it can result in amore effective solution when compared to C#. After demonstrating thepowerful capabilities, Ben will explain how the DLR with IronRubyallows you to take full advantage of Ruby from C# applications,resulting in you choosing the appropriate language (C# or Ruby) forthe appropriate feature. The combination results in a powerfultool-set and opens some amazing possibilities for both C# and Rubydevelopers. With C# 4.0, these capabilities are taken a step further.But the advantage is not just one way, there are also advantages forRuby developers by allowing them to develop against the .Netframework.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/D2A3C378-E023-4234-916D-B0EADEC66892"&gt;iPod Video 320x240 (119.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/56485999-04EC-43C1-95AC-BA04E3F56C25"&gt;MPEG-4 for QuickTime Medium 1280x480 (570.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/7CB648EE-545B-4184-9DFF-2FA37DBD7943/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=7CB648EE-545B-4184-9DFF-2FA37DBD7943" target="_blank"&gt;jQuery for ASP.NET developers 17 Jun 10 9:20 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;This session is a practical tour of the ??write less, do more?? JavaScript library - jQuery. In this session we will build an application using ASP.NET and jQuery while learning about CSS selectors, DOM manipulation, and asynchronous communications using the jQuery library. We??ll also look at the jQuery plug-in model, examine common jQuery programming paradigms, and see how to invoke WCF web services using jQuery.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/552627F4-8EC0-4CD9-B9CC-794EEB8879EA"&gt;iPod Video 320x240 (97.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5F2A05DA-A1D1-49E3-A40B-B2117B726B00"&gt;MPEG-4 for QuickTime Medium 640x480 (190.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/B25D60E8-48C1-4514-9276-608859B4CC72/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=B25D60E8-48C1-4514-9276-608859B4CC72" target="_blank"&gt;Advanced Debugging with Visual Studio 17 Jun 10 10:17 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Basically every .NET developer knows the Visual Studio debugger, but only few know its little secrets. In this session, Ingo shows you what you can achieve with this tool beyond the setting of simple breakpoints. You will learn how advanced breakpoints, debugger macros and visualizers, interactive breakpoints, tracepoints and interactive object instantiation at development time can support your hunt for bugs in your applications. Ingo will also show you the new crash-dump debugging features of Visual Studio 2010 which allow you to get closer to your problem even if you don't have access to Visual Studio on the machine which exhibits the unexpected behavior.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/507CBC74-B2A6-46DD-852D-69F83FFE9C1A"&gt;iPod Video 320x240 (177.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0F217368-0162-4866-B63A-E9AB0D1C43FE"&gt;MPEG-4 for QuickTime Medium 640x480 (176.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/4DEB5112-B18A-44A8-AA01-56EDD95EB0EF/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=4DEB5112-B18A-44A8-AA01-56EDD95EB0EF" target="_blank"&gt;Unit Testing Best Practices &amp;amp; Test Reviews 17 Jun 10 10:20 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In this session we look at existing .NET code form open source projects and discuss anti patterns worst practices when doing unit testing in the wild. From abuse of mock objects, unmaintainable tests and unreadable tests to checking interactions vs. state - this talk will take you deep into the real ugly world of unit testing.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E2232282-BD09-4B7B-9ADF-81206FEDF545"&gt;iPod Video 320x240 (134.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B78EE753-34E3-476D-A9D1-97A00381D43E"&gt;MPEG-4 for QuickTime Medium 1280x480 (664.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/874B6020-4A57-472A-807C-1B6E66FB308D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=874B6020-4A57-472A-807C-1B6E66FB308D" target="_blank"&gt;Ruby for .NET developers 17 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;After having spent many years coding in C#, and after having spent equally as much time in the C# language culture, Ruby seemed like a lot of bad ideas and heresy. In fact, much of Ruby is heretical to a C# or VB.NET mono-culture, but the productivity gains demonstrated by Ruby on Rails teams remains an unavoidable elephant in the room. This presentation looks at C# code examples side by side with some equivalent Ruby code and shines a little light on what it means to have either "ceremony" and "essence". It challenges the claims of static typing's effect on tooling to deliver "developer productivity". And finally, some examples of Ruby meta programming are given to demonstrate direct solutions to programming problems that would require much ado with restrictions in C# that don't end up doing much more than reducing the efficiency of software development efforts.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/646691C3-E4FB-4F22-BBC0-2A0977273BF9"&gt;iPod Video 320x240 (2.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/CFEFCB53-4D3D-4F80-B650-A13426355759"&gt;iPod Video 320x240 (22.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/D9ADEB9E-8C47-49E1-A138-CE4E227A2E12"&gt;iPod Video 320x240 (98 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/94C4A832-6CCC-4494-9F6F-BE516C1DD556"&gt;MPEG-4 for QuickTime Medium 1280x480 (384.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/DEC10BA7-5065-4A8E-8508-B2778EDE04E5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=DEC10BA7-5065-4A8E-8508-B2778EDE04E5" target="_blank"&gt;Silverlight for Windows Phone 17 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Windows Phone 7 Series is coming??and if you are a Silverlight developer there is good news: Silverlight *is* the development platform for Windows Phone applications. This session will provide an overview of the tools, the emulator, and the core APIs for Silverlight for Windows Phone development. Find out what is (and is not) possible in creating and porting your Silverlight applications ready for Windows Phone 7.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/44C70EDA-7DDE-481D-9AAD-11BD2AEEF246"&gt;iPod Video 320x240 (130.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/86E4E98C-245F-447C-A136-AD595F0A16A3"&gt;MPEG-4 for QuickTime Medium 1280x480 (316 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/FE7C8B56-42E7-4F99-9C1C-F2BB0980F520/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=FE7C8B56-42E7-4F99-9C1C-F2BB0980F520" target="_blank"&gt;Putting Some Testing Into Your TFS Build 17 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Continuous Integration and scheduled builds are an important part of any development process. To get the best out of these tools, as much testing as possible should be wired into the post build process. With the 2010 release of Visual Studio we get the Lab Manager product that allows us to deploy our automated build to a virtualised test environment for either manual and?or automated testing. In this session I will show by doing an end to end demo, showing how an application can be build, deployed and tested with the Lab Manager environment.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/551EBB80-E427-4ED1-9E55-77180F1C8985"&gt;iPod Video 320x240 (132.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/11636CFA-4AEF-4F0E-9715-05DD4052A7E1"&gt;MPEG-4 for QuickTime Medium 1280x480 (578.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/522865A3-FB1B-44F2-8528-214D8D73B333/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=522865A3-FB1B-44F2-8528-214D8D73B333" target="_blank"&gt;Big-Ass View on Competency 17 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Agile team members create their own rules, based on constraints imposed by the environment. But something else is needed for good results: some call it discipline, craftsmanship, or competence. Traffic management teaches us that there are 7 approaches to achieving competence in a self-organizing system. We are going to look at all of them.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/932D6A04-9721-43B3-A2C5-DD07C0C4DBF3"&gt;iPod Video 320x240 (129.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C0CB578F-0F6D-47E3-BE6B-FE566624D872"&gt;MPEG-4 for QuickTime Medium 1280x480 (699.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/F931D805-3B03-432A-A0FE-26F7E9CAF721/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=F931D805-3B03-432A-A0FE-26F7E9CAF721" target="_blank"&gt;Strategic Design 17 Jun 10 9:00 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;As software development leaders, we need to think more strategically. Some design decisions affect the trajectory of the whole project or even the organization. These decisions arise in early chartering and throughout development, and they are about much more than architecture. This talk will examine these issues through the lens of the Strategic Design principles of domain-driven design, which systematize a few critical practices some successful teams do intuitively.It is common for skilled teams to deliver software they are not proud of, due to compromises with legacy designs. Others toil for years, producing a platform that is never used to good advantage. These are strategic failures. On the other hand, there are projects with a direct explanation of how the software contributes to business goals. There are projects where designers work with a realistic view of the context of their development within the larger system, allowing them to maintain design clarity and integrity. These are strategic successes. Winning strategy starts with the domain.Two domain-driven design principles, "Context Mapping" and "Distilling the Core Domain", help you see your strategic situation more clearly and approach strategic design decisions more systematically. These techniques require extensive interaction with domain experts as well as the leaders of the organization, in discussions broader than functional requirements. They sometimes lead to priorities quite different from our most comfort&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EEC4043E-D680-4686-A431-B0FCA258A408"&gt;iPod Video 320x240 (130.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/A3A05BA3-0B0C-4ED9-80D1-D9D705C0F1CA"&gt;MPEG-4 for QuickTime Medium 1280x480 (603.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/F36BD4C5-4FB7-4C1B-A0E1-990DA59308F5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=F36BD4C5-4FB7-4C1B-A0E1-990DA59308F5" target="_blank"&gt;Co- and contra-variance in C# 17 Jun 10 7:57 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;What's the difference between a bunch of bananas and a fruit bowl? Why can't I order a collection of circles by area? Why is the Hokey Cokey invariant? These questions (and some rather more sensible ones) will be answered in this session on variance. C# has supported variance to different degrees over different versions; C# 4 introduces covariance and contravariance to generic delegates and interfaces. Many developers may well use these features without even being aware of them - but as ever, it's useful to know what's going on for the times where things go wrong. Pre-requisites: a strong cup of coffee for when we discuss higher order functions, and a reasonable grasp of generics. Understanding Func, Action, IEnumerable and IComparer would be a good start.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6E75B535-23C7-46FC-A163-B14216AAAEF7"&gt;iPod Video 320x240 (132.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9B1AEFFD-C380-43D3-B458-4022FDEB7D42"&gt;MPEG-4 for QuickTime Medium 640x480 (139.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/72AF92FC-C72C-4CFF-A9F3-2F51DDC5B08C/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=72AF92FC-C72C-4CFF-A9F3-2F51DDC5B08C" target="_blank"&gt;Windows Identity Foundation Overview 17 Jun 10 8:51 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Hear how Windows Identity Foundation makes advanced identity capabilities and open standards first class citizens in the Microsoft .NET Framework. Learn how the Claims Based access model integrates seamlessly with the traditional .NET identity object model while also giving developers complete control over every aspect of authentication, authorization, and identity-driven application behavior. See examples of the point and click tooling with tight Microsoft Visual Studio integration, advanced STS capabilities, and much more that Windows Identity Foundation consistently provides across on-premise, service-based, ASP.NET and Windows Communication Foundation (WCF) applications.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/23C83C49-DD49-41D5-9CB9-4B91DBC82906"&gt;iPod Video 320x240 (119.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C191BAF1-5EFD-4306-AB9B-0ECD8E14E513"&gt;MPEG-4 for QuickTime Medium 1280x480 (608.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/163E9CCC-7317-41A5-B772-81595467BBAF/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=163E9CCC-7317-41A5-B772-81595467BBAF" target="_blank"&gt;Browsers with Wings: HTML5 APIs for webapp developers 16 Jun 10 4:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;HTML5 is all the rage with the cool kids, and although there's a lot of focus on the new language, there's plenty for web app developers with new JavaScript APIs both in the HTML5 spec and separated out as their own W3C specifications. This session will take you through demos and code and show off some of the outright crazy bleeding edge demos that are being produced today using the new JavaScript APIs. But it's not all pie in the sky - plenty is useful today, some even in Internet Explorer!Specifically we'll be looking at scripting the video media element, 2D canvas and some of the mashups we can achieve. How to take our web apps completely offline, going beyond the cookie and HTML5's answer to threading: web workers.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6C10CEF6-5D4D-474A-B3D1-7E8F3B70D81F"&gt;iPod Video 320x240 (95.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0A7C3DCF-8883-4C17-98C0-24E9DC6F512D"&gt;MPEG-4 for QuickTime Medium 1280x480 (370.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/30DC4F1D-3867-41FD-944E-167872AE4EF2/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=30DC4F1D-3867-41FD-944E-167872AE4EF2" target="_blank"&gt;Test-Driven JavaScript 16 Jun 10 4:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Javascript becomes much more important to interactive website development then before (ok it has been for a while already) but the notion of testing that logic seems even further fetched then testing the code written in C#, Java. And this is something that is wrong as well. How do you test drive your javascript development, what do you need to think about to make it testable? How can you deal with timers, async calls and the dom. Demonstrate all these things including how easy it is to make your own fakes for testing. Demonstrate the refactoring and changing behaviour becomes so much easier.And not to forget that the design of the code is much better as well. Basically that you gain all the benefits that TDD gives you in other languages also when doing TDD for javascript development.Building a feature without ever loading up a web-page to physically look at it and when you finally do it does exactly what you intended it to do, that is magic :-)&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/5749CE3E-EBE0-4A72-9FBE-BB56FDFCDD9B"&gt;iPod Video 320x240 (123.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/844C4775-CC44-418A-81F7-FC3FFF16CB45"&gt;MPEG-4 for QuickTime Medium 1280x480 (326.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/0CB02DD9-8B8E-43CF-BA75-5C3FEA3BDCEF/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=0CB02DD9-8B8E-43CF-BA75-5C3FEA3BDCEF" target="_blank"&gt;Windows Azure AppFabric 16 Jun 10 4:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;As one of the key platform providers for enterprise solutions today, Microsoft realizes that there are very many assets in corporate datacenters that are not easily moved to the cloud. A lot of data is subject to government or corporate regulation about data protection that does not allow for the data to be hosted off-site and over the past decade, companies have made enormous investments in streamlining and integrating their applications in ways that make it not particularly attractive to break out parts of that integration chain and move them off into the cloud as insular solutions. The Windows Azure AppFabric??s Service Bus and Access Control services which Clemens will introduce in this session are about bridging these gaps and to provide application-to-application connectivity and access control federation across network and trust scopes.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9A5D8386-E0F0-4431-9742-5FF75638EDEA"&gt;iPod Video 320x240 (120.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FE8D7A25-2ACB-4A45-AFB7-90515A70C7E5"&gt;MPEG-4 for QuickTime Medium 1280x480 (379.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/F6DB36EA-62CE-466E-9E8F-778261513777/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=F6DB36EA-62CE-466E-9E8F-778261513777" target="_blank"&gt;The Parallel Task Library in .NET 4.0 16 Jun 10 5:b40PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Introduced with .Net 4.0, the Parallel Task Library gives a new approach to task-parallel programming. In this session we dive into the details of the library, looking at the various ways that it can be utilised. We will cover a number of areas, including:? Data Parallelism, using Parallel.For and Parallel.ForEach? Task Parallelism, using Parallel.Invoke and the Task class? Exception Handling? Cancellation? Asynchronous continuation patterns? PLinq The session is aimed at the intermediate developer - a good understanding of basic threading principles is a requirement.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/768EFCE0-884D-4CA1-8D73-A53554862A6F"&gt;iPod Video 320x240 (153.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/F9F4FFFE-C760-4DD0-A127-494D0BD94F0B"&gt;MPEG-4 for QuickTime Medium 1280x480 (651.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/EC40F1C9-6A3D-4920-B57C-42316D7B5875/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=EC40F1C9-6A3D-4920-B57C-42316D7B5875" target="_blank"&gt;The Whole Team Approach to Testing 16 Jun 10 5:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Many test teams find themselves ??squeezed to the end??, with code delivered too late to complete all the testing before the release deadline. They can??t get traction on critical activities such as test automation, and find themselves falling further and further behind. The software development organization is weighed down by increasing technical debt. Customers aren??t getting the software they wanted. Serious bugs get out to production, slowing the team down more as fixing one bug might cause two more. It can seem impossible to break out of this downward spiral.One way to turn this trend around is to get the whole team involved in building quality into the application, and solving testing problems together. In this session, Lisa Crispin will explain how to make quality a team responsibility, and testing a team??s problem to solve. She??ll explain how the ??whole team?? approach, leveraging multiple skill sets and planning in time for testing activities, leads to more testable code, better testing solutions. Participants will leave with some practical ideas to try right away.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C52CE5CD-5DE1-4ECA-B797-09FB737ACE4F"&gt;iPod Video 320x240 (101.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/BB31F99E-1E3D-4D27-AF4F-EEB02030306D"&gt;MPEG-4 for QuickTime Medium 1280x480 (422.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/302740C4-D6FD-40D1-BF97-A0D21392EEFD/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=302740C4-D6FD-40D1-BF97-A0D21392EEFD" target="_blank"&gt;Real-World Design with the Visual Studio 2010 Modelling Tools 16 Jun 10 3:21 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Visual Studio 2010 introduces new Layer, Class, Activity, Use Case, Sequence, and Component diagrams. This session provides an overview of each of these diagrams and demonstrates practical examples of when they are useful, and when they are not! You will see how VS 2010 allows you to reverse-engineer your existing .NET code and generate sequence diagrams that graphically depict the object interaction in your applications. You??ll also get a tour of the new Architecture Explorer, and learn how you can best fit these design tools into your software development processes - including agile software processes.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0FC91B77-AB31-49A9-AFD9-030364B418B4"&gt;iPod Video 320x240 (103.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9EF81C68-C1B9-4A45-80AF-F997FA49EF38"&gt;MPEG-4 for QuickTime Medium 1280x480 (299.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/903C3339-7F84-4626-B30D-476E74517929/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=903C3339-7F84-4626-B30D-476E74517929" target="_blank"&gt;Building a Real-World, E-Commerce, Data-Driven Web Site 16 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In this session, I'll take you on a tour of what it took to go from an empty space in my ISPs web farm and turn it into a functional ecommerce web site. We'll include a discussion of how the domain was attached, how the site was built in ASP.NET, where the graphics and layout came from, how the products and site settings were managed in SQL Server, how the site was published, how shipping, taxes and handling are calculated, how money is collected and how the site is maintained. If you've ever wanted to see what it takes to start from scratch and build a real-world ecommerce web site, this is the place for you.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/95BED923-EE0E-475B-BB86-0F59EC1AC206"&gt;iPod Video 320x240 (103.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/1412E8B9-3876-45AD-83FA-FC43356886BD"&gt;MPEG-4 for QuickTime Medium 1280x480 (325.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/5E52750E-D4FC-41AA-9164-427A00BBC49D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=5E52750E-D4FC-41AA-9164-427A00BBC49D" target="_blank"&gt;Distributed Computing 2.0 16 Jun 10 4:19 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7188E52A-58A4-40AA-A1C3-03186BD0E40C"&gt;iPod Video 320x240 (164.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AFAE6D66-4F22-4C3C-940F-C706DE5CB28A"&gt;MPEG-4 for QuickTime Medium 1280x480 (864.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/80B9DFA5-672A-4773-BDA2-72B2B9ED3B68/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=80B9DFA5-672A-4773-BDA2-72B2B9ED3B68" target="_blank"&gt;Windows Azure Platform Compute &amp;amp; Storage 16 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;At the heart of Windows Azure are its compute and management capabilities, which are foundational not only for ISV and enterprise solutions, but also for the Windows Azure platform components themselves. In this session Clemens will introduce Windows Azure??s notion of service and configuration models, the deployment and upgrade mechanisms, and introduce the diagnostics and management capabilities. You will furthermore learn about the various storage capabilities including the relational SQL Azure database service. Clemens will also show how Windows Azure??s different roles provide an architectural guidance framework for building scalable apps.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/14DF94D5-04AD-4513-BEAA-9FE7C50C2666"&gt;iPod Video 320x240 (147.7 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AC095D5A-B799-4FEA-8CE1-2CFBA2952047"&gt;MPEG-4 for QuickTime Medium 1280x480 (40.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/7F971DA3-985E-48B0-A31E-3A3E50B2DF61"&gt;MPEG-4 for QuickTime Medium 1280x480 (690.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/84E5914B-BC2F-44B7-8276-C19C65833313/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=84E5914B-BC2F-44B7-8276-C19C65833313" target="_blank"&gt;Noda time: An Alternative Date &amp;amp; Time Framework 16 Jun 10 3:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The date and time API within the .NET framework has certainly improved over time, but it's still not as rich as it might be. Noda Time is an open source project porting the popular "Joda Time" framework from Java to .NET, and improving it along the way. I'll explain why DateTime and DateTimeOffset aren't always enough, the main concepts in Noda Time, and some lessons we've learned about .NET, porting in general, and running an open source project.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8B620277-9794-4EB9-B903-67D889CA9F66"&gt;iPod Video 320x240 (140 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/3683519C-A600-4FCB-9DC3-4875371D74A7"&gt;MPEG-4 for QuickTime Medium 1280x480 (611.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/7242BEA2-BE20-4A74-B47C-33DF1C20F4F5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=7242BEA2-BE20-4A74-B47C-33DF1C20F4F5" target="_blank"&gt;Does Self-Organization Actually Work &amp;amp; Are Agile Teams Actually Motivated By It? 16 Jun 10 4:20 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Self-organising teams are a key component of all of the agile methodologies, but do teams and organisations know what they are actually letting themselves in for when signing up for things like Scrum? In my experience the definition of "self-organisation" is widely different from organisation to organisation, team to team and even individual to individual within a team. This leads to fear, sub-optimisation and in a lot of cases subversion of the principle such that teams are being directed to work in sprints or iterations. In this session, Geoff will share his experiences of self-organisation, the common characteristics that are required for self-organisation to work and how commonly these characteristics are found in organisations that say that are "doing agile".&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/18AC3332-46AA-4F9C-BEFB-E12B9C4244ED"&gt;iPod Video 320x240 (103.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E951C1EA-774E-4DFA-AE5B-5B74B712EA87"&gt;MPEG-4 for QuickTime Medium 1280x480 (439.4 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/E0F27D82-039B-4CE8-839C-DD69D641CB58/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=E0F27D82-039B-4CE8-839C-DD69D641CB58" target="_blank"&gt;Mono - A Great Platform for .NET in the Cloud 16 Jun 10 2:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;ASP.NET WebForms and MVC apps run out-of-the-box on Mono on a variety of platforms. With so many options for hosting Linux servers on services such as EC2 and Linode, Mono has become an attractive platform for cloud deployment. This presentation will walk through several approaches to developing, debugging, and deploying .NET applications to the cloud with Mono.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FA27ACC7-3833-43A0-AA41-361861EFD3F4"&gt;iPod Video 320x240 (92.9 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/604C5AAA-F90B-4B4C-BA1C-74DDD385800A"&gt;MPEG-4 for QuickTime Medium 1280x480 (294.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D034F11E-646B-4A8A-94F7-64231F2C882D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D034F11E-646B-4A8A-94F7-64231F2C882D" target="_blank"&gt;I Can't Hear You - There's an ORM In My Ear: Effective Use of NoSQL In an ASP.NET MVC Web Application 16 Jun 10 2:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The NoSQL movement is gaining momentum (to many people's annoyance) and gaining many fans. For developers who haven't used a NoSQL solution (Object Database, Document Database, etc) it's a bit awkward to conceive of how you might do common tasks - like querying or generating application reports. In this talk Rob Conery shows how you can build an effective data access strategy using both NoSQL and traditional Relational Databases, focusing on using each system for its strengths rather than compensating for its weaknesses.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/CD9C9B96-6AD6-4682-901F-DECFC8963A2B"&gt;iPod Video 320x240 (364.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/30B6030D-0B5A-4EAE-BA35-730484A52D45"&gt;MPEG-4 for QuickTime Medium 320x240 (230.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/10ED66AB-C227-41DF-9943-03DF3F34686A/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=10ED66AB-C227-41DF-9943-03DF3F34686A" target="_blank"&gt;Lean Quality Assurance 16 Jun 10 3:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Quality Assurance (QA) in software worldwide has in fact degenerated into testing alone. Software?IT management has ignorantly allowed this to happen.Of course many parts of the industry have been well-aware of more cost-effective ways of delivering required quality in practice, but this has in fact been largely ignored; while granting very large resources to testing alone.It is time for a wakeup call!This manifesto is here to tell the industry that;testing alone is 10x more costly than doing Real QA. testing alone is not good enough, this can and need not go on. We know how to do real QA much much better than testing alone, using smarter upstream engineering practices, based on design, prevention and upstream inspections.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9108952D-9CB6-4E5E-A227-95735D90C744"&gt;iPod Video 320x240 (145 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/FACABF23-1BF7-4CFB-B631-BB0E6D8DA904"&gt;MPEG-4 for QuickTime Medium 1280x480 (399.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EEF24F50-5532-4F29-8313-3E5A87166282"&gt;MPEG-4 for QuickTime Medium 1280x480 (689.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/376DFD42-5231-48E0-AC24-0FB883A2166A/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=376DFD42-5231-48E0-AC24-0FB883A2166A" target="_blank"&gt;Design, Don't Decorate 16 Jun 10 3:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Putting the advanced capabilities of WPF and Silverlight to full use requires collaboration, experimentation, and iterative prototyping. In this session, you??ll see all five sequential prototypes for the acclaimed StaffLynx application (as seen on .NET Rocks TV), and discuss practices that worked and didn't work in real-world advanced UI development. We'll also discuss the role of visual and interactive designers in creating new era user interfaces, give some tips on how to think about using WPF and Silverlight capabilities to make interfaces feel natural and less stressful to users, and cover the most valuable lessons learned from a real-world project using advanced, next generation user interface technology.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/11B44CD9-7A55-4FDE-BA57-AEACA6F3F1E7"&gt;iPod Video 320x240 (109.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/CD0B714C-FC9B-49C6-9E7A-63BACAF1A4BD"&gt;MPEG-4 for QuickTime Medium 1280x480 (457.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/317A8278-F06C-4D24-AB31-330565EC0AE3/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=317A8278-F06C-4D24-AB31-330565EC0AE3" target="_blank"&gt;Inside modern JavaScript 16 Jun 10 2:00 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;JavaScript is a dynamic, functional, ubiquitous language that has many hidden secrets. In this session we will take a deep look at the core JavaScript features that many contemporary libraries leverage, including constructor functions, prototypical inheritance, closures, hash parameters, method chaining, and more. Having a solid grasp of these features will not only help you write more maintainable JavaScript code, but also allow you to take greater advantage of today??s JavaScript libraries.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/6B6DBBAE-B439-4845-97B0-61962D8A2D20"&gt;iPod Video 320x240 (94.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/774FBBD6-2282-4E7A-9071-7B01CB835FE9"&gt;MPEG-4 for QuickTime Medium 1280x480 (390.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/2377CA9F-F9F8-418F-879A-17BCE95ED916/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=2377CA9F-F9F8-418F-879A-17BCE95ED916" target="_blank"&gt;CHESS - Finding &amp;amp; Reproducing Heisenbugs in Concurrent Programs 16 Jun 10 2:55 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;CHESS is a tool from Microsoft Research that aims to find and reproduce hard to locate concurrency bugs. It does so by repeatedly running tests, systematically enumerating how the various threads are interleaved on each run. When (if) errors occur, CHESS can also then re--?]run the specific interleaving that resulted in the error, massively simplifying the debugging effort.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/1F52A581-6AF6-42BD-9CA3-CAD83E92CC42"&gt;iPod Video 320x240 (155.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/0B9F8812-C9A7-45FC-8E0B-FB90219E646A"&gt;MPEG-4 for QuickTime Medium 1280x480 (663.1 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/806323A0-D35F-4F60-A71B-37347C89176D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=806323A0-D35F-4F60-A71B-37347C89176D" target="_blank"&gt;Windows Azure Overview 16 Jun 10 1:54 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;HASH(0x1060cd48)&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/95568687-743E-4041-9FCB-ED4E5D92F335"&gt;iPod Video 320x240 (149.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/EA907797-D580-4234-98E5-0546C7F1022F"&gt;MPEG-4 for QuickTime Medium 1280x480 (709.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/95F528ED-572D-41ED-AEFD-C014731A55FE/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=95F528ED-572D-41ED-AEFD-C014731A55FE" target="_blank"&gt;What has Mono done for .NET developers Lately? 16 Jun 10 12:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Mono is an open-source, cross-platform implementation of the .NET framework based on the ECMA standards for C# and the Common Language Infrastructure. With Mono, users can run .NET applications written and compiled in Visual Studio on Windows, Linux, and Mac OS X. This session will provide an introduction to cross-platform development and deployment with Mono. Participants will see how they can leverage their existing skills and tools to write .NET applications that will run on multiple platforms and architectures with Mono. The presentation will also include a discussion of cross-platform considerations for leveraging Mono, and demonstrate how to use MoMA, the Mono Migration Analyzer, to determine how ready anapplication is for cross-platform deployment. Additional demonstrations will examine how best to leverage Visual Studio to develop and deploy to Linux and OS X, and take a peek at the current state of Moonlight 3 and 4.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/186869AD-5769-470B-8BEE-9A81A9874E72"&gt;iPod Video 320x240 (78.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2ECAEE91-16B8-47E3-9E6D-9BC940047FD4"&gt;MPEG-4 for QuickTime Medium 1280x480 (245.7 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/D191A552-A38A-4870-8AB8-DE96203A581B/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=D191A552-A38A-4870-8AB8-DE96203A581B" target="_blank"&gt;Patterns for Parallel Programming 16 Jun 10 1:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Every five to ten years the world of computer programming is facing now a new paradigm shift, like GUI, object orientation, or generics. Right now we are facing a new paradigm shift, the multi-core one. Successful research in this area has been done for the past 30 years, but we are still not using the results efficiently. A pattern is a working solution to a recurring problem, and parallel?multi-core programming has its own problems which led to a set of patterns. Come and see in this session about which patterns exists in the area of parallel?multi-core programming and how they can be used with Visual Studio 2010.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/C58B6D44-2803-4907-BA44-72A1B06768B5"&gt;iPod Video 320x240 (176.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/965B1D6A-C262-41C0-941B-FF2750620274"&gt;MPEG-4 for QuickTime Medium 1280x480 (936.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/464F8EB9-9399-432A-A0B7-BCB17757BE46/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=464F8EB9-9399-432A-A0B7-BCB17757BE46" target="_blank"&gt;Programming with GUTs on 16 Jun 10 1:38 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/CE3C1B66-A831-4FFD-8AFF-D877781A30B6"&gt;iPod Video 320x240 (112.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/F7B14893-DF7E-43FB-8ABD-44F11A648009"&gt;MPEG-4 for QuickTime Medium 1280x480 (483 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/91A8D9BC-4D17-4DB0-85DF-D8E573C0461D/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=91A8D9BC-4D17-4DB0-85DF-D8E573C0461D" target="_blank"&gt;CouchDB for .NET Developers 16 Jun 10 1:37 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;RDBMS has been the standard for many years, when it has come to data storage. However, recently there has been an increase in document databases. In this talk we are going to cover the basics of CouchDB and how to consume this kind of database from .NET applications.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/4DFFDE4B-FBA8-43E9-9BEB-672B9BE36807"&gt;iPod Video 320x240 (132.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/DACDB1ED-CEF6-47EB-9180-6458BD4EA1C0"&gt;MPEG-4 for QuickTime Medium 1280x480 (684.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/F8D7D4BC-686F-4865-A4B2-C3211D246C8E/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=F8D7D4BC-686F-4865-A4B2-C3211D246C8E" target="_blank"&gt;How to Get the Best ROI From a Scrum Team as a Product Owner 16 Jun 10 12:37 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The Product Owner is probably the role that has received the least attention and help of all the three Scrum roles but it is paramount to an organisation's success with Scrum. While most projects benefit immediately from switching or adopting Scrum, to get the best out of the project a Product Owner really needs to understand both the team and the Scrum framework. In this tutorial Geoff will share his views and experiences of working with Scrum teams to provide you with a number of tips to improve your ROI.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B8080CFA-585F-459D-8779-4A200D49FFF5"&gt;iPod Video 320x240 (205.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AADA154E-622A-4EB1-BB0A-167A0D32794F"&gt;MPEG-4 for QuickTime Medium 1280x480 (516.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/1C166DD2-560F-4CE2-97CB-608C4F78866C/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=1C166DD2-560F-4CE2-97CB-608C4F78866C" target="_blank"&gt;.NET Design Patterns for Agile Software Processes 16 Jun 10 12:40 PM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;In the world of agile programming techniques, one of your best tools is design patterns. This session provides practical examples and implementation of design patterns in .NET. Familiarizing yourself with patterns such as Model-View-Controller, Observer, Abstract and Concrete Factories, and concepts such as programming to an interface rather than an implementation will help you build applications quickly that can easily adapt to your customer??s changing needs.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/52A75902-5E75-4D24-9C1D-59D5F09B2629"&gt;iPod Video 320x240 (131 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/29C798E1-AD5B-4275-A746-F64A3098BD72"&gt;MPEG-4 for QuickTime Medium 1280x480 (578 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/E915B78B-D9B7-4CE9-96DA-2B794391AD2F/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=E915B78B-D9B7-4CE9-96DA-2B794391AD2F" target="_blank"&gt;C# Puzzlers 16 Jun 10 12:32 PM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/CD2DBF38-EECD-4B27-829E-4CEB2C10B189"&gt;iPod Video 320x240 (105.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9908F342-50FD-4606-8AB2-59A5EFD97AC1"&gt;MPEG-4 for QuickTime Medium 1280x480 (505.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/5C145BA1-0861-478D-A467-AFF3A5089E03/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=5C145BA1-0861-478D-A467-AFF3A5089E03" target="_blank"&gt;Architecture of the Client Tier for WPF &amp;amp; Silverlight 16 Jun 10 10:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Stateful client-based technologies for user interfaces, such as WPF and Silverlight, require more sophisticated design and architecture for the client tier than typical web applications. This session discusses the construction of a client-based navigation shell that replaces server-based navigation for a cleaner, more responsive user experience in multi-page applications. Implementation of capabilities such as data validation, temporary caching of unsaved data to allow for network downtime, and graceful shutdown will all be discussed. The session features a working model that attendees can use as a starting point for their own projects.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E0A937F9-4078-44AD-B59C-E84FA66EEE76"&gt;iPod Video 320x240 (118.4 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B5F1412F-0761-4935-8023-4824F42A6A21"&gt;MPEG-4 for QuickTime Medium 1280x480 (340.6 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/1272299E-1DD1-4C11-B7A3-EBDE5B11A5B7/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=1272299E-1DD1-4C11-B7A3-EBDE5B11A5B7" target="_blank"&gt;Individuals &amp;amp; Interaction Over Processes and Tools 16 Jun 10 10:40 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Although it is a simple value, the idea that individuals and interactions are more significant than processes and tools is overlooked perhaps more often than it is valued. Of course, processes and tools make a difference -- sometimes a very big difference -- but what determines whether a process or tool is effective is related to the individuals and interactions. To best achieve agility you need to start with the current context and understand how people actually behave in response to their environment, their beliefs and one another. What actually motivates and demotivates people, developers in particular? What actually makes their work easier or harder? Does making "business value" the centrepiece of what they do actually motivate the people who ultimately produce such business value? Or is it more about the individuals and interactions?&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/20B40A5F-AF56-4849-81A7-A7A0735C1CE5"&gt;iPod Video 320x240 (119.2 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/60AC1636-0002-45EB-B6BA-C299B106EFE5"&gt;MPEG-4 for QuickTime Medium 1280x480 (385.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/2C3F6822-1694-48A6-9C49-8B3E3D4C6CE5/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=2C3F6822-1694-48A6-9C49-8B3E3D4C6CE5" target="_blank"&gt;Reactive Extensions (Rx) 16 Jun 10 10:37 AM&lt;/a&gt;&lt;/p&gt;

        

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/E91A35E6-2221-41A1-B88D-37AA6C35A79C"&gt;iPod Video 320x240 (123.3 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/8AFD1B19-6EED-45CF-B34D-CC3046ABB684"&gt;MPEG-4 for QuickTime Medium 1280x480 (534.9 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/38AD648E-835D-4FD4-91E8-B34E1D437D89/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=38AD648E-835D-4FD4-91E8-B34E1D437D89" target="_blank"&gt;Practical IronRuby 16 Jun 10 9:21 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Ruby has been a home for some great innovative frameworks like Ruby on Rails, Cucumber and Rake. IronRuby version 1.0 will soon be released, unleashing the power of Ruby to the .NET world. 
          &lt;br&gt;In this session you will get familiar with the Ruby language and its amazing ecosystem and you will learn to take advantage of it in your everyday development tasks. Come and see how this great new addition to the .NET family makes your development process faster, clearer and happier!&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/445362FF-8CA1-494C-976F-06864CABA19D"&gt;iPod Video 320x240 (90.8 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/09B77798-02C0-4A6C-A167-9B1CC0765A62"&gt;MPEG-4 for QuickTime Medium 1280x480 (308.2 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/4E353FF7-ACDA-428B-99C9-9EACBC7C471C/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=4E353FF7-ACDA-428B-99C9-9EACBC7C471C" target="_blank"&gt;Strategies Against Architecture 16 Jun 10 9:18 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Good architecture requires good vision and good people, but there are times when actions and decisions taken with the best of intent begin to work against an architecture. Instead of helping a system achieve a long life, speculation about reuse, flexibility and generality can bring a system to an early grave, weighing the codebase down with accidental complexity that invites workarounds and, ultimately, a new ad hoc architectural style. Documentation intended to be helpful becomes shelfware, ignored equally by its authors and its prospective readership. Dysfunctional memes in code and tests go unchecked because the detail of code is not considered a part of the architecture. Instead of stability and responsiveness, an architecture achieves stasis and loses reflex.This session looks at the reality of how development process and practices interact with the grander vision of architecture, the pitfalls of "architect as cop" and how to employ speculation and uncertainty to a system's advantage.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/AB9B343F-8332-4028-A7B5-CABE45BB2053"&gt;iPod Video 320x240 (113.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/B28AFB59-FDCD-4F26-AC2D-3C33C0508BAE"&gt;MPEG-4 for QuickTime Medium 1280x480 (36 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/9F36B1C3-E917-41BC-AF9F-2E8E0FB4D3E4"&gt;MPEG-4 for QuickTime Medium 1280x480 (376.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/C21C13ED-8657-4D10-9A21-213BA1031F90/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=C21C13ED-8657-4D10-9A21-213BA1031F90" target="_blank"&gt;A Style of Programming 16 Jun 10 9:17 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;This talk describes a set of coding principles that constitutes a style of programming that focuses on ease of coding, ease of changing the code, and ease of testing the code. The presentation aims at helping programmers understand how the principles relate to actual code. Every principle is made concrete through examples. This style of programming is influenced by leading object oriented and agile developers, and it is perfect for applying on object oriented languages such as C#.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/63D6EAB4-1ABD-4D6D-9D1D-84513B4BF893"&gt;iPod Video 320x240 (154.5 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/82055901-F9EF-4181-8A10-6CA548166E95"&gt;MPEG-4 for QuickTime Medium 1280x480 (753.5 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/0F97309E-B237-4CEF-A4D9-9E4DCD790F4B/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=0F97309E-B237-4CEF-A4D9-9E4DCD790F4B" target="_blank"&gt;Multicore - The Future of Computing 16 Jun 10 10:16 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Since hitting both the memory and power wall about 3-5 years ago, all cpu vendors have been forced to turn from making the fastest possible single-thread cpu to instead putting several (sometimes simpler) cores on a single chip.Reducing the clock frequency by 50% can reduce power usage by 80%, so actual throughput per watt can improve a lot, as long as the code can be parallelized.Existing GPUs from Nvidia and AMD, Cell processors as used in the PS3 and Intel's announced Larrabee architecture can all supply an order of magnitude more fp processing power per watt than what is currently available from normal cpus.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/2785FDD1-BC5B-4754-856D-E26C1CCCA73F"&gt;iPod Video 320x240 (116.6 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/385CAC19-A22D-4C8C-A599-D3D468D9018F"&gt;MPEG-4 for QuickTime Medium 1280x480 (571.8 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr style="vertical-align: top"&gt;
      &lt;td&gt;&lt;img src="http://streaming.ndc2010.no/tcs/slides/DD7F9AD4-50AF-4B66-8A05-A9465C4F19BF/thumbnails/thumbnail.jpg"&gt;&lt;/td&gt;

      &lt;td&gt;
        &lt;p&gt;&lt;a href="http://streaming.ndc2010.no/tcs/?id=DD7F9AD4-50AF-4B66-8A05-A9465C4F19BF" target="_blank"&gt;Seven Key Factors for Agile Testing Success 16 Jun 10 10:13 AM&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;Agile development approaches present unique challenges for testers and test teams. Working in short iterations, often with limited written requirements, agile development teams can leave traditional testers behind. Common testing-related activities such as user acceptance testing, testing inter-product relationships, and installation testing need different approaches to fit into agile projects. Lisa Crispin explains seven key factors for testing success within agile projects that you can also apply to more traditional methodologies. Using a whole team approach and adopting an agile testing mindset are among the important components of a successful agile testing strategy. Learn how to overcome cultural and organizational obstacles and barriers to success in areas such as test automation. Discover the seven critical factors that provide a foundation for building your team's focus on quality and that deliver maximum value to your business.&lt;/p&gt;

        

        &lt;ul&gt;
          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/415AABE2-F055-406F-94F5-2B5C25A63FA4"&gt;iPod Video 320x240 (126 MB)&lt;/a&gt;&lt;/li&gt;

          &lt;li&gt;&lt;a href="http://streaming.ndc2010.no/tcs/download/D5225EC6-A679-4F7B-B4F8-9A27B3B9EAB2"&gt;MPEG-4 for QuickTime Medium 1280x480 (661.3 MB)&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;

        
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;</description>
      <comments>http://blog.zhaojie.me/2010/07/ndc-2010-videos.html#comments</comments>
      <pubDate>Tue, 13 Jul 2010 07:13:12 GMT</pubDate>
      <lastBuildDate>Tue, 13 Jul 2010 07:13:12 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <title>Why Java Sucks and C# Rocks（补2）：标准事件模型</title>
      <link>http://blog.zhaojie.me/2010/07/more-why-java-sucks-and-csharp-rocks-2-standard-event-model.html</link>
      <guid>http://blog.zhaojie.me/2010/07/more-why-java-sucks-and-csharp-rocks-2-standard-event-model.html</guid>
      <description>&lt;p&gt;这又是一篇“补”，本来并不想写这方面的内容，因为这并非完全是“语言”相关。打个比方，如果您觉得.NET中的事件模型不友好，那么就按Java的做法去做咯（反之就做不到了）。不过既然正好看到有些涉及到这方面的讨论，那么我也趁此机会发表一下自己的看法吧。这次谈的是两种语言（其实在这个话题上也是平台）下“标准”的事件模型。“标准”二字意味着是被双方社区各自接受的模型，而不仅仅是为了实现“事件”这一理念而使用的任意做法。&lt;/p&gt;

&lt;h1&gt;.NET中的事件&lt;/h1&gt;

&lt;p&gt;还是从两种事件模型开始介绍。首先是.NET中的事件模型。.NET里的“事件”是一等公民，换句话说，这是平台中所直接定义和描述的概念，我们利用反射相关的API（如GetEvent方法）可以直接获取到某个“事件”对象，然后对其进行各类操作（例如添加或删除处理器）。.NET中的事件基于“委托”，这也是.NET有别于Java平台的概念之一，&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-5-anonymous-method.html#delegate"&gt;在上一篇文章中也有过简单介绍&lt;/a&gt;，事实上委托在.NET 1.0中似乎完全是为事件量身定做的，例如在System.Windows.Forms.Form类中便定义了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Form &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;Component
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseEventHandler &lt;/span&gt;MouseMove;
    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseEventHandler &lt;/span&gt;MouseDown;
    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseEventHandler &lt;/span&gt;MouseUp;
    &lt;span style="color: blue"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseEventHandler &lt;/span&gt;MouseWheel;
    ...
}&lt;/pre&gt;

&lt;p&gt;当然“事件”这东西不光是UI组件独有的，事实上&lt;a href="http://msdn.microsoft.com/en-us/library/wewwczdw.aspx"&gt;在.NET中有一种异步模式便是基于事件的&lt;/a&gt;——例如WebClient类，在使用时我们可以为一个WebClient对象的DownloadProgressChanged事件注册事件处理方法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;void &lt;/span&gt;Download()
{
    &lt;span style="color: #2b91af"&gt;WebClient &lt;/span&gt;client = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;WebClient&lt;/span&gt;();
    client.DownloadProgressChanged +=
        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DownloadProgressChangedEventHandler&lt;/span&gt;(OnDownloadProgressChanged);
    ...
}

&lt;span style="color: blue"&gt;void &lt;/span&gt;OnDownloadProgressChanged(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af"&gt;DownloadProgressChangedEventArgs &lt;/span&gt;e)
{
    ...
}&lt;/pre&gt;

&lt;p&gt;在.NET中，事件的处理器是一个符合委托签名方法，单个事件之间完全分离，它们各自的事件处理器也完全独立。&lt;/p&gt;

&lt;h1&gt;Java中的事件&lt;/h1&gt;

&lt;p&gt;Java中并没有特定的“事件”对象，&lt;a href="http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html"&gt;这和它的“属性”一样&lt;/a&gt;，都是属于纯粹“概念”上的内容，事实上它们完全是由普通的方法，接口等基本事物形成的。例如，同样作为UI组件中的窗口，javax.swing.JFrame有一套这样的API（继承自java.awt.Component）：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Component&lt;/span&gt; {
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;addMouseListener(&lt;span style="color: #2b91af"&gt;MouseListener &lt;/span&gt;l);
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;removeMouseListener(&lt;span style="color: #2b91af"&gt;MouseListener &lt;/span&gt;l);
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseListener&lt;/span&gt;[] getMouseListeners();
    ...
}

&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseListener&lt;/span&gt; {
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mouseClicked(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mouseEntered(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mouseExited(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mousePressed(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mouseReleased(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
}&lt;/pre&gt;

&lt;p&gt;Java中的事件不是“对象”，如它的属性一样，都是“一组API”，不过事件比属性更复杂一些。Java中的事件模型由几个部分组成：一个addXxxListener方法，用于添加事件处理器；一个removeXxxListener方法用于删除一个事件处理器；还有一个getXxxListners方法用于获得当前已经添加的所有的事件处理器。此外，在Java中的事件处理器是由接口XxxListener表示的，一个接口中包含了多个事件，换句话说，Java是对事件进行了“分组”，例如在Component对象中还有另外一组与鼠标有关的事件：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Component&lt;/span&gt; {
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;addMouseMotionListener(&lt;span style="color: #2b91af"&gt;MouseMotionListener &lt;/span&gt;l);
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;removeMouseMotionListener(&lt;span style="color: #2b91af"&gt;MouseMotionListener &lt;/span&gt;l);
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseMotionListener&lt;/span&gt;[] getMouseMotionListeners();
    ...
}

&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseMotionListener&lt;/span&gt; {
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mouseDragged(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;mouseMoved(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e);
}&lt;/pre&gt;

&lt;p&gt;在使用事件时，往往是利用匿名类型添加事件处理器，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
component.addMouseMotionListener(
    &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseMotionListener&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;mouseMoved(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e) {
            &lt;span style="color: green"&gt;// do this, do that...
        &lt;/span&gt;}
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;mouseDragged(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e) { &lt;span style="color: green"&gt;/* empty */ &lt;/span&gt;}
    });&lt;/pre&gt;

&lt;p&gt;使用匿名类型，我们可以内联地创建一个实现了XxxListener接口的对象，这样就避免了创建一个新的类型（在Java语言中这意味着还要创建新的源文件），也方便形成一个能够访问上下文成员的闭包。不过您可以发现，如果我们只是要监听mouseMoved事件，也需要实现整个分组，即MouseMontionListener接口，只不过要将无需实现的方法留空罢了。这么做产生的问题就是，例如像MouseListener这样的接口，其中包含5个成员，那么如果我只想实现mouseClicked单个事件的话，留空其他4个方法还是太过麻烦了。因此Java也提供了对应的XxxAdaptor类，让我们可以写出这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
component.addMouseListener(
    &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseAdaptor&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;mouseClicked(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e) {
            &lt;span style="color: green"&gt;// do this, do that...
        &lt;/span&gt;}
    });&lt;/pre&gt;

&lt;p&gt;XxxAdaptor类会实现XxxListener接口，并且实现其中的所有方法，并全部留空。于是在使用时，我们便可以基于Adaptor类创建一个匿名类型，并选择我们需要的方法来覆盖（override）。例如上面这段代码，我们既然只要关注mouseClicked事件，那么也只要覆盖这一个方法就行了。&lt;/p&gt;

&lt;h1&gt;Java事件模型的缺点&lt;/h1&gt;

&lt;p&gt;一句话：我不喜欢Java的事件模型。&lt;/p&gt;

&lt;p&gt;首先，Java的事件模型比较零散。一个事件要包含三个方法，这三个方法组成一个完整的事件，缺一不可。那么，这三个方法为什么就不能“一体化”，统一成单个对象呢？.NET在这方面做的比较好，事件本身是一个独立的对象，无论是添加、删除，还是获得当前所有的事件处理器，都是从“事件对象”本身出发的功能。这与“属性”一样，我认为.NET的设计更为紧凑，优雅。当然，要实现这一点，并非一定要.NET中“委托”这样有些特殊的类型，一个普通的接口或是抽象类也可以满足“单一对象”的要求。只是，我不是十分接受Java这种“松散”的事件模型。&lt;/p&gt;

&lt;p&gt;其次，Java的事件之间不是独立，而是经过“分组”的——当然，也有像MouseWheelListener那样的只包含mouseWheel单个事件的“分组”，但毕竟大部分分组中还是包含多个事件。这就出现一个问题，我们难以单独处理单个事件，在添加单个事件的处理器时必然要涉及到其他事件。我们来设想这样一种情况：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
component.addMouseListener(
    &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseAdaptor&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;mouseClicked(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e) {
             &lt;span style="color: green"&gt;// do this, do that...
        &lt;/span&gt;}
    });

component.addMouseListener(
    &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseAdaptor&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;public void &lt;/span&gt;mousePressed(&lt;span style="color: #2b91af"&gt;MouseEvent &lt;/span&gt;e) {
             &lt;span style="color: green"&gt;// do this, do that...
        &lt;/span&gt;}
    });&lt;/pre&gt;

&lt;p&gt;以上两段代码分别为mouseClicked和mousePressed事件各自添加了一个事件处理器。那么请问，当mouseClicked事件触发时，将会执行几个事件处理器？答案是2个，一个是我们添加的逻辑，还有一个是随mousePressed事件一起携带而来的“空白逻辑”。而且事实上，即便是理应“置身事外”的mouseEntered或mouseExited事件，它们也被各自添加了两个空白的处理器。对于一个对性能极度苛刻的程序员来说，这样的“浪费”可能是无法忍受的（虽然我觉得这里并不会有什么性能问题）。&lt;/p&gt;

&lt;p&gt;此外您是否想过，为什么MouseListener和MouseMotionListener会是两个“事件组”而不合并为同一个呢？据说也是性能方面的缘故，因为MouseMotionListener中的事件都是“连续触发”的，换句话说，它们执行事件处理器的密度很高，如果将它和MouseListener合并，那么一个如mouseClicked这样的“普通型”事件处理器，也会让mouseMoved这样的“密集型”事件执行无谓的方法。由于执行密度很高，可能对于性能的影响就比较可观了。&lt;/p&gt;

&lt;p&gt;可能您会说，把所有的事件处理逻辑实现在一个XxxAdaptor或是XxxListener中不就可以了吗？不过这就要求多个不同事件的处理器必须在同一段代码中添加，实在不够自由——这点在使用了&lt;a href="http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx"&gt;Reactive Framework&lt;/a&gt;的时候体会尤甚。&lt;/p&gt;

&lt;p&gt;不过，我认为Java事件模型最大的缺陷还是“扩展性”。Java中的事件大量依赖了接口，而在一个成熟的类库中，&lt;a href="http://blog.zhaojie.me/2009/08/more-on-class-and-interface.html"&gt;接口的使用应该是非常谨慎的&lt;/a&gt;，因为一旦发布了某个公开接口，它就不能进行任何修改，因为任何修改都会导致兼容性上的破坏，这方面在《&lt;a href="http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613"&gt;Framework Design Guildlines&lt;/a&gt;》一书中进行了详细论述。试想，现在MouseListener中有5个方法，表示5个事件，那么如果我在新版本的类库中希望增加另外一个事件（如mouseDoubleClicked），那么该怎么办？似乎也只有创建新的接口。但是如果每次需要添加新的事件时都要增加新的接口，而其中仅仅是包含一个接口的话，类库中的补丁痕迹就会很重。更何况，如mouseDoubleClicked这样的事件明显也应该属于MouseListener的一部分。&lt;/p&gt;

&lt;h1&gt;.NET事件模型的遗憾&lt;/h1&gt;

&lt;p&gt;.NET事件模型没有Java中的许多缺点，事实上如果有人说.NET的设计参考了Java的缺点，那么我认为“事件模型”可能的确是其中一个。在.NET的事件模型中，事件是一等公民，每个事件都是类的独立成员；它们的事件处理器完全独立，不会相互干涉；在类库升级时如果要增加新的事件，使用最普通最自然的方式增加便是，仅此而已。&lt;/p&gt;

&lt;p&gt;当然，.NET中的事件模型也不够完美。在我看来它的缺点在于，它虽然是对象，但还是有限制的对象。在C#中，我们无法将一个事件作为对象传递，无法使用一个抽象类对其进行统一处理（object类型自然除外），也难针对其利用“扩展方法”等常用特性。这个问题在某些情况下会限制某些开发模型，于是我们会为其&lt;a href="http://blog.zhaojie.me/2009/09/more-on-event-as-object.html"&gt;增加一些“事件即对象”的机制&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;微软自己其实也意识到这个问题，因此在F#中进行了一些特别的处理。F#编译器会自动将.NET中的事件视为一个IEvent&amp;lt;THandler, TEventArgs&amp;gt;，定义如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
&lt;span style="color: blue"&gt;type &lt;/span&gt;IDelegateEvent&amp;lt;'Delegate&amp;gt; =
  &lt;span style="color: blue"&gt;interface
    abstract &lt;/span&gt;this.AddHandler : 'Delegate &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;unit
    &lt;span style="color: blue"&gt;abstract &lt;/span&gt;this.RemoveHandler : 'Delegate &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;unit
&lt;span style="color: blue"&gt;end

type &lt;/span&gt;IEvent&amp;lt;'Delegate,'Args
  &lt;span style="color: blue"&gt;when &lt;/span&gt;'Delegate : &lt;span style="color: blue"&gt;delegate&lt;/span&gt;&amp;lt;'Args,unit&amp;gt; &lt;span style="color: blue"&gt;and &lt;/span&gt;'Delegate :&amp;gt; System.Delegate&amp;gt; =
  &lt;span style="color: blue"&gt;interface
    inherit &lt;/span&gt;IDelegateEvent&amp;lt;'Delegate&amp;gt;
&lt;span style="color: blue"&gt;end&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;在F#中，一个.NET中的事件便是一个标准的对象，它弥补了C#里的缺点，于是许多做法在F#中便显得自然或直接了一些。例如，在F#中内置了响应式编程模型，可以直接使用。而对于C#来说，使用&lt;a href="http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx"&gt;Reactive Framework&lt;/a&gt;相关功能时，则需要手动地将一个事件转化为IObservable对象——当然，有了一些辅助方法，这也就是一行代码的工作罢了。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;这篇文章中我简单介绍了.NET与Java中事件模型，并谈了谈自己的看法。总而言之，.NET的事件模型虽有遗憾，但较之Java的事件模型还是有很大优势的。即便是.NET中的事件模型，在某些人看来会成为“心智负担”，但比较之下我也不愿意让.NET或C#退回到Java的设计方式上——更何况，就这样一个简单的机制就能成为值得一提的心智负担吗？我对此持保留意见。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/speech-why-java-sucks-and-csharp-rocks.html"&gt;演讲预告：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-slides-final-version.html"&gt;幻灯片：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html"&gt;Why Java Sucks and C# Rocks（1）：比较的意义与目的&lt;/a&gt;&lt;span style="color: red"&gt;（重要）&lt;/span&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-2-primitive-types-and-object-orientation.html"&gt;Why Java Sucks and C# Rocks（2）：基础类型与对象&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-3-attribute-and-annotation.html"&gt;Why Java Sucks and C# Rocks（3）：Attribute与Annotation&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/why-java-sucks-and-csharp-rocks-4-generics.html"&gt;Why Java Sucks and C# Rocks（4）：泛型&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html"&gt;Why Java Sucks and C# Rocks（补1）：Reddit，兼谈C#属性&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-5-anonymous-method.html"&gt;Why Java Sucks and C# Rocks（5）：匿名方法&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;Why Java Sucks and C# Rocks（补2）：标准事件模型&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/07/more-why-java-sucks-and-csharp-rocks-2-standard-event-model.html#comments</comments>
      <pubDate>Tue, 13 Jul 2010 01:17:38 GMT</pubDate>
      <lastBuildDate>Tue, 13 Jul 2010 01:17:38 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>Why Java Sucks and C# Rocks（5）：匿名方法</title>
      <link>http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-5-anonymous-method.html</link>
      <guid>http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-5-anonymous-method.html</guid>
      <description>&lt;p&gt;确切地说，这里的标题应该是“C#中的匿名方法”，因为这是C#中特有的功能。在之前的文章里，虽然我都用长篇文字加代码示例来说明问题，但总有朋友认为我谈的只是C#和Java的“区别”，算不上优势。不过从这篇文章开始，我们将正式进入C# 2.0的时代，这也是C#大步甩开Java语言的开端——可以看出，&lt;a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg"&gt;Anders Hejlsberg&lt;/a&gt;从此开始实现他对于编程语言的各种理想，而并非纠缠于与Java所谓的“竞争”中。例如这篇文章要讨论的“匿名方法”特性，以及随之而来的“函数式编程”痕迹，便开始引领C#在开发理念上的进步。&lt;/p&gt;

&lt;h1 id="delegate"&gt;委托&lt;/h1&gt;

&lt;p&gt;委托（Delegate），事实上这是在.NET 1.0（请注意不是C#，而是.NET平台的概念）时代便有的东西。不过，因为在C# 1.0中并没有提供一个“改变编程思维”的特性来体现这一概念，便没有多提。不过到了C# 2.0，既然我们要开始谈匿名方法了，便不得不提“委托”这个非常关键的概念。如果您没有接触过这个概念，不妨可以简单地将“委托”理解为一种“类型安全”的函数指针：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public delegate void &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Action&lt;/span&gt;&amp;lt;T&amp;gt;(T arg);

&lt;span style="color: blue"&gt;public delegate &lt;/span&gt;T &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T&amp;gt;();

&lt;span style="color: blue"&gt;public delegate &lt;/span&gt;TResult &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T, TResult&amp;gt;(T arg);

&lt;span style="color: blue"&gt;public delegate void &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MouseEventHandler&lt;/span&gt;(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af"&gt;MouseEventArgs&lt;/span&gt; e);&lt;/pre&gt;

&lt;p&gt;在C#中定义委托对象时需要用到delegate关键字，然后便像声明一个方法那样指定委托名称，参数名和返回值得名称等等。委托可以带有泛型参数，这样便可以定义十分通用的委托类型，如上面的Action委托及两个Func委托。提供这种通用的委托类型对于某些编程实践有着十分重要的意义，这点在以后的文章中也会提到。不过，在还没有提供泛型支持的.NET 1.0，或者说是在C# 1.0时代，所有的委托都是如上面MouseEventHandler那样拥有的具体类型委托。&lt;/p&gt;

&lt;p&gt;在.NET中，委托作用是引用一个“方法”，以及其调用时所需要的完整上下文，换句话说，有了一个委托对象之后，我们便可以直接“调用”这个方法了。自然，委托所引用的方法必须与委托的签名完全相同，这也是上文中“类型安全函数指针”所表示的含义。委托在调用时的开销和一个虚方法差不多，可以说它的性能非常高，因此它也是在很多情况下优化“反射调用”性能的常用手段。&lt;/p&gt;

&lt;p&gt;在.NET中，“事件”是委托的一个重要使用场景，最近有人质疑.NET的事件是个设计上的错误，它完全应该像Java那样基于普通的接口来实现“事件”概念。对此我有不同的看法，不过这是一个较大的话题，因此我将其从现在这篇文章中剥离开来，独立成篇。而现在我先讨论其他一些委托的典型使用场景。&lt;/p&gt;

&lt;h1&gt;匿名方法及其典型使用场景&lt;/h1&gt;

&lt;p&gt;在.NET中，我们可以将委托对象作为方法的参数或是返回值来使用，例如： &lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T1, &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T2, TResult&amp;gt;&amp;gt; Curry&amp;lt;T1, T2, TResult&amp;gt;(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T1, T2, TResult&amp;gt; f) { ... }&lt;/pre&gt;

&lt;p&gt;您可以已经意识到了，这便是所谓的“高阶函数”。&lt;a href="http://blog.zhaojie.me/2009/04/csharp-higher-order-function.html"&gt;高阶函数的优势&lt;/a&gt;有许多，简单概括一下便是“更好的抽象和组合能力”。只是在C# 1.0中，我们必须独立定义一个方法之后，才能将其构造为一个委托对象，不过从C# 2.0开始，我们可以使用“匿名方法”来构造一个委托对象，例如上面的Curry方法可以实现为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T1, &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T2, TResult&amp;gt;&amp;gt; Curry&amp;lt;T1, T2, TResult&amp;gt;(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T1, T2, TResult&amp;gt; f)
{
    &lt;span style="color: green"&gt;// in C# 3.0: x =&amp;gt; y =&amp;gt; f(x, y)
    &lt;/span&gt;&lt;span style="color: blue"&gt;return delegate&lt;/span&gt;(T1 x)
    {
        &lt;span style="color: blue"&gt;return delegate&lt;/span&gt;(T2 y) { &lt;span style="color: blue"&gt;return &lt;/span&gt;f(x, y); };
    };
}&lt;/pre&gt;

&lt;p&gt;在代码中使用delegate关键字可在代码中内联地创建一个委托对象，并会在需要时形成一个闭包，您可以简单理解为调用这个匿名方法所需要的完整上下文。例如在上面这段代码中，内层的匿名函数可以访问到外层匿名函数的参数x，以及Curry方法的参数f。在C#中使用匿名函数时，可以访问字面范围内（lexical scope）的所有成员，这也逐渐让C#有了函数式编程的意味，当然这一切都还得等到C# 3.0阶段才会真正发扬光大，目前还只是C# 2.0。&lt;/p&gt;

&lt;p&gt;匿名方法是语言的特性，和运行时没有任何关系，完全是编译器施展的魔法，于是有些人便认为这就是个无足轻重的语法糖。语法糖没错，但是“无足轻重”的评价我无法赞同。匿名函数带来了许多编程模式上的改变。由于语法特性的缺失，这些编程模式在C# 1.0或是Java语言中是麻烦到几乎无法使用的，更别提“推广”开来。关于这方面的文章我写过不少，它们都是真正用于产品开发的案例：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2009/08/from-delegate-to-others.html#simplify-callback"&gt;简化回调&lt;/a&gt;：在异步编程中回调函数是十分常见的。有了匿名方法之后，创建一个回调函数十分容易，并且可以利用闭包直接使用回调函数中所需要的成员，在简化开发的同时，依旧保证了强类型的静态检查能力。 &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2009/08/from-delegate-to-others.html#lazy-initializer"&gt;延迟初始化器&lt;/a&gt;：我们可以使用匿名函数提供一个对象的初始化逻辑，并交由一个线程安全的初始化器使用。这里利用了高阶函数来封装逻辑，在传统的面向对象语言中实现这点，则往往需要利用工厂方法模式，这需要创建各种抽象类及具体类。事实上，利用匿名方法及高阶函数之后，GoF23中的许多模式，如“工厂方法”、“策略”及“模板方法”等等，都有了更加简单的实现方式，甚至完全成为自然而然的编程方法。 &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2008/04/can-you-use-anonymous-method-properly.html"&gt;缓存容器辅助方法&lt;/a&gt;：使用缓存容器时往往有着固定的模式，如“检查缓存，如果没有则访问数据库，将结果放入缓存后并返回”。有了匿名方法之后，我们可以将“访问数据库”这个操作通过参数交由缓存容器的辅助方法，辅助方法仅仅在缓存失效的情况下采取执行这个操作，这样既封装了重复的逻辑，又保证了代码的流畅性。 &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2009/02/simplify-async-programming-2-asynctaskdispatcher.html"&gt;AsyncTaskDispatcher&lt;/a&gt;：这是一个用于简化多个异步操作之间协作关系的组件，我们只要将异步操作之间的依赖关系提供给Dispatcher，则Dispatcher便会自动调配异步操作的执行顺序。这里使用利用到匿名函数来表示各个异步操作，并利用闭包在多个异步操作之间共享状态。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;自然，利用高阶函数或是匿名方法也会带来一些额外的问题，例如&lt;a href="http://blog.zhaojie.me/2009/06/laziness-traps.html"&gt;延迟带来的陷阱&lt;/a&gt;，但瑕不掩瑜，匿名方法依旧是C#中最重要的语言特性之一，也是如Scala，Python，Ruby等高级语言中的标准配置。&lt;/p&gt;

&lt;h1&gt;C#的匿名方法与Java的匿名类型&lt;/h1&gt;

&lt;p&gt;说起来，Java语言从1.4版本开始也加入了&lt;a href="http://docstore.mik.ua/orelly/java-ent/jnut/ch03_12.htm"&gt;匿名类型&lt;/a&gt;的特性。简单地说，匿名类型是指以“内联”的方式在代码中定义一个抽象类型（即接口、抽象类甚至任何非final类）的具体实例。例如&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html"&gt;之前某篇文章&lt;/a&gt;中用Java实现了生成一个minInclusive到maxExclusive之间数列的迭代器：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Range &lt;/span&gt;&lt;span style="color: blue"&gt;implements &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterable&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt; {

    &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_maxExclusive;
    &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_current;
    
    &lt;span style="color: blue"&gt;public &lt;/span&gt;Range(&lt;span style="color: blue"&gt;int &lt;/span&gt;minInclusive, &lt;span style="color: blue"&gt;int &lt;/span&gt;maxExclusive) {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_maxExclusive = maxExclusive;
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = minInclusive;
    }

    @Override
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterator&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt; iterator() {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterator&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt;() {
            &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: blue"&gt;boolean &lt;/span&gt;hasNext() {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;m_current &amp;lt; m_maxExclusive;
            }
            
            &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt; next() {
                &lt;span style="color: blue"&gt;int &lt;/span&gt;current = m_current;
                m_current = m_current + 1;
                &lt;span style="color: blue"&gt;return &lt;/span&gt;current;
            }

            &lt;span style="color: blue"&gt;public void &lt;/span&gt;remove() {
                &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;UnsupportedOperationException&lt;/span&gt;();
            }
        };
    }
}&lt;/pre&gt;

&lt;p&gt;在Range类的iterator方法中，我们直接返回了一个Iterator&amp;lt;Integer&amp;gt;接口的实例，这个实例直接内联地提供了接口中hasNext、next和remove三个方法的实现，并且使用了外部的m_maxInclusive及m_current字段。那么这不也是个闭包吗？没错，Java中的匿名类型的确也有一定这方面的特性，虽然使用起来比较麻烦，也不利于单元测试等等，因此一些开发实践中都不太提倡使用匿名类型（某些标准场景除外）。平心而论，我并不觉得这是个没有意义的特性，毕竟它提供了另一种选择，而且在C# 2.0之前我有时也会怀念Java语言的这个特性。&lt;/p&gt;

&lt;p&gt;既然C#中的匿名方法和Java的匿名类型有一定的共性，那么我们便可以寻找两者之间的差异。除了语法之外，我认为两者最大的区别在于对匿名方法（类型）外的“局部变量”的操作能力上。闭包的典型使用场景之一是支持简单的并行计算。例如.NET 4.0提供了一个并行库，其中包含类似于如下接口的Parallel.For方法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;static void &lt;/span&gt;ParallelFor(&lt;span style="color: blue"&gt;int &lt;/span&gt;minInclusive, &lt;span style="color: blue"&gt;int &lt;/span&gt;maxExclusive, &lt;span style="color: #2b91af"&gt;Action&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; body) { ... }&lt;/pre&gt;

&lt;p&gt;显然在.NET 2.0中我们便可以自行编写这样的方法，并配合匿名方法可以很轻松的开展简单的并行计算。例如一个并行的n * n的矩阵加法，我们便可以写作：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;static int &lt;/span&gt;ParallelSum(&lt;span style="color: blue"&gt;int&lt;/span&gt;[,] array, &lt;span style="color: blue"&gt;int &lt;/span&gt;n)
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;processorCount = &lt;span style="color: #2b91af"&gt;Environment&lt;/span&gt;.ProcessorCount;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;sum = 0;

    ParallelFor(0, processorCount, &lt;span style="color: blue"&gt;delegate&lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;part)
    {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;minInclusive = part * n / processorCount;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;maxExclusive = minInclusive + n / processorCount;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;partSum = 0;

        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;x = minInclusive; x &amp;lt; maxExclusive; x++)
        {
            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;y = 0; y &amp;lt; n; y++)
            {
                partSum += array[x, y];
            }
        }

        &lt;span style="color: #2b91af"&gt;Interlocked&lt;/span&gt;.Add(&lt;span style="color: blue"&gt;ref &lt;/span&gt;sum, partSum);
    });

    &lt;span style="color: blue"&gt;return &lt;/span&gt;sum;
}&lt;/pre&gt;

&lt;p&gt;从代码上看，sum是ParallelSum方法的“局部变量”，不过在匿名方法内部也可以对它进行修改，例如上面的代码中就对其进行了CAS加法，因此我们可以认为在C#中的闭包在使用上是完全透明的。在Java中，如果要在匿名类型里访问外部的局部变量，则必须在局部变量声明时增加final关键字，这意味着这个局部变量是无法修改的。这么做可以避免&lt;a href="http://blog.zhaojie.me/2009/06/laziness-traps.html#false-sharing"&gt;错误共享&lt;/a&gt;之类的问题，但也限制我们在需要的时候必须用一点特殊的方式回避这种限制。例如在编写之前的并行矩阵相加以及AsyncTaskDispatcher代码时，则可能需要借助于这样一个包装类：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class&lt;/span&gt; Wrapper&amp;lt;T&amp;gt; {
    &lt;span style="color: blue"&gt;public&lt;/span&gt; T value;
}&lt;/pre&gt;

&lt;p&gt;这样即便是引用Wrapper对象的局部变量不能修改，我们也能修改Wrapper对象的value字段的值。我不喜欢这样的设计，我认为这部分灵活性交由程序员来控制。C#虽然理论上有着误用的可能，但这也只是十分少见的情况，而且有了检查工具之后，误用几乎可以完全避免了。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/speech-why-java-sucks-and-csharp-rocks.html"&gt;演讲预告：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-slides-final-version.html"&gt;幻灯片：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html"&gt;Why Java Sucks and C# Rocks（1）：比较的意义与目的&lt;/a&gt;&lt;span style="color: red"&gt;（重要）&lt;/span&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-2-primitive-types-and-object-orientation.html"&gt;Why Java Sucks and C# Rocks（2）：基础类型与对象&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-3-attribute-and-annotation.html"&gt;Why Java Sucks and C# Rocks（3）：Attribute与Annotation&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/why-java-sucks-and-csharp-rocks-4-generics.html"&gt;Why Java Sucks and C# Rocks（4）：泛型&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html"&gt;Why Java Sucks and C# Rocks（补1）：Reddit，兼谈C#属性&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;Why Java Sucks and C# Rocks（5）：匿名方法&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-5-anonymous-method.html#comments</comments>
      <pubDate>Tue, 06 Jul 2010 06:22:36 GMT</pubDate>
      <lastBuildDate>Tue, 06 Jul 2010 06:22:36 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/discussion/">思考讨论</category>
      <title>Why Java Sucks and C# Rocks（补1）：Reddit，兼谈C#属性</title>
      <link>http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html</link>
      <guid>http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html</guid>
      <description>&lt;p&gt;最近博客冷清了不少，主要是事情较多，一是&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;.NET交流会&lt;/a&gt;，二是工作，三是几篇暂时无法发在博客上的文章。周末在家，发现邮箱里经常收到&lt;a href="http://www.slideshare.net/"&gt;SlideShare&lt;/a&gt;的邮件，说是我的&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-slides-final-version.html"&gt;Why Java Suck and C# Rocks幻灯片&lt;/a&gt;在推特上很火热。而今天早上我忽然发现，&lt;a href="http://www.reddit.com/r/programming/comments/cjhzk/why_java_sucks_and_c_rocks_final/"&gt;它被人发到Reddit的编程版块了&lt;/a&gt;，讨论地颇为热烈。关于讨论内容，您可以亲自阅读一下。最近的讨论也让我想要补充一些关于C#属性的问题。&lt;/p&gt;

&lt;p&gt;关于这个话题，在我&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html"&gt;不断强调讨论的目的&lt;/a&gt;之后，在我的博客上还是有许多朋友没有明白我在说什么，热血上涌当即给予反击，这些从后面的评价中便可以看出。不过在Reddit上，当围观人群看到相同的内容，甚至&lt;a href="http://www.slideshare.net/jeffz/why-java-sucks-and-c-rocks-final"&gt;只有幻灯片&lt;/a&gt;，没有更多解释说明，但是大家都能理解我的意思，都能意识到我是希望“用Scala代替Java语言”。而即便是没有看到幻灯片的最后，或是对此有不同意见，也都是在用正常的方式提出质疑。这样的差别让我感到很讽刺，有些话我已经说过很多次了，现在只是引一个例证，也就不再重复了。&lt;/p&gt;

&lt;p&gt;在Reddit上的讨论中，有人提出C#里的属性和事件其实都只是方法的语法糖，意义不大。最近在一些人的文章里也出现了类似的说法。我在这个系列的中原本并不打算着重分析C#里的“属性”概念，因为它对于“编程思维”的影响可能并不如其他方面来的明显。不过如果真要深究的话，我还是非常看重这个语言特性的（不过其实属性和事件都是.NET特性）。那么现在我也来简单谈一下“属性”，而“事件”则在后面的文章里讨论吧——那篇文章的草稿已经躺了大半个月了。&lt;/p&gt;

&lt;p&gt;属性，在C#里以get/set方法对的形式出现，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyClass
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_count;
    &lt;span style="color: blue"&gt;public int &lt;/span&gt;Count
    {
        &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return this&lt;/span&gt;.m_count; }
        &lt;span style="color: blue"&gt;set &lt;/span&gt;{ &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_count = &lt;span style="color: blue"&gt;value&lt;/span&gt;; }
    }
}&lt;/pre&gt;

&lt;p&gt;而在Java中的等价代码则是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MyClass&lt;/span&gt;
{
    &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_count;
    &lt;span style="color: blue"&gt;public int &lt;/span&gt;getCount() { &lt;span style="color: blue"&gt;return this&lt;/span&gt;.m_count; }
    &lt;span style="color: blue"&gt;public void &lt;/span&gt;setCount(&lt;span style="color: blue"&gt;int &lt;/span&gt;value) { &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_count = value; }
}&lt;/pre&gt;

&lt;p&gt;在Java中并非没有“属性”的概念，而是将它直接拆成了getXxx和setXxx两个方法而已，而对于一些工具（如IDE）、框架（如序列化框架）来说，它会忽略方法之前的get/set，直接将后面的字符识别为一种“成员字段”，而在.NET对象中，“属性”天生就是一种表示成员字段的标准方案。&lt;/p&gt;

&lt;p&gt;在我看来，从使用上来说，属性比分离的方法要方便一些。例如我们要将MyClass对象的Count属性加1，则可以这么写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
c.Count++;&lt;/pre&gt;

&lt;p&gt;而在Java里则必须这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
c.setCount(c.getCount() + 1);&lt;/pre&gt;

&lt;p&gt;当然您可能会觉得这方面的差距并不大。那么我们换一个场景，现在有一个User数组，我们要根据它的年龄进行排序：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
users.Sort(u =&amp;gt; u.Age);

&lt;span style="color: green"&gt;// Java&lt;/span&gt;
users.sort(#(&lt;span style="color: #2b91af"&gt;User&lt;/span&gt; u)(u.getAge()));&lt;/pre&gt;

&lt;p&gt;这里我们暂且忽略Java中古怪的Lambda表达式写法，只关注“属性”和“get方法”之间的区别。在我看来，C#的代码体现了声明式编程的理念，从语义上讲可以清晰地视为“将u按u.Age字段”排序；与之想法，一旦我们只能利用getAge方法时，便又重新回到了“命令式”思维方式上。为什么这么说？因为调用getAge方法，意味着“获取”这个“Age”字段，它代表的是一个“动作”，表明排序时去“获取”这样一个数值，也就是体现了“怎么做（how to do）”，而不像C#代码中体现的是“做什么（what to do）”。&lt;/p&gt;

&lt;p&gt;下面一个例子可能更为典型。例如在使用ORM框架，如(N)Hibernate时，我们时常会在复杂的XML配置中迷失方向。后来有了Fluent NHibernate，让我们可以使用“代码”来进行配置，这样无论是从可读性还是可维护性来说都有了质的飞跃。目前在项目中，由于没有使用关系型数据库，于是我便遵循Fluent Interface的理念，设计了这样的API：&lt;/p&gt;

&lt;pre class="code"&gt;Property(c =&amp;gt; c.ArticleID).Identity();
Property(c =&amp;gt; c.Content).Name(&lt;span style="color: #a31515"&gt;&amp;quot;Html&amp;quot;&lt;/span&gt;);
Property(c =&amp;gt; c.Tags);
Property(c =&amp;gt; c.Keywords).ChangeWith(c =&amp;gt; c.Content).ChangeWith(c =&amp;gt; c.Tags);&lt;/pre&gt;

&lt;p&gt;以上代码是Article对象的映射规则，四行代码表明：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;将ArticleID映射为标识符 &lt;/li&gt;

  &lt;li&gt;将Content映射为存储中的Html字段。 &lt;/li&gt;

  &lt;li&gt;将Tags映射至存储，名称不变。 &lt;/li&gt;

  &lt;li&gt;将Keywords字段映射至存储中，并随Content及Tags字段改变。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我写的映射框架支持传统ORM框架的状态跟踪功能，不过还提供了只读字段的“按需改变”。如Keywords，它是只读属性，框架会读取它的值，并将其放入存储内（以便进行查询），但是我们不需要将其写回对象中去。同时，在Contents和Tags改变的时候，框架也会重新获取Keywords的值，并跟新至存储内。利用C#的表达式树特性，我们可以十分轻松愉快地“声明”出这样的规则。在这个规则中，我们利用“属性”来标明字段之间的关系，这可以说是一种DSL。但如果没有属性，一切都是get方法，那么这段代码又会是什么样子呢？&lt;/p&gt;

&lt;p&gt;所以，相对C#来说Java语言中很难写出优雅的声明式代码及Fluent NHibernate这样的框架。原因有多种，如Lambda的古怪语法形式是为一、缺少表达式树功能是为二，而不直接支持“属性”这样的概念也是另一个原因吧。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/speech-why-java-sucks-and-csharp-rocks.html"&gt;演讲预告：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-slides-final-version.html"&gt;幻灯片：Why Java Sucks and C# Rocks&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html"&gt;Why Java Sucks and C# Rocks（1）：比较的意义与目的&lt;/a&gt;&lt;span style="color:red;"&gt;（重要）&lt;/span&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-2-primitive-types-and-object-orientation.html"&gt;Why Java Sucks and C# Rocks（2）：基础类型与对象&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-3-attribute-and-annotation.html"&gt;Why Java Sucks and C# Rocks（3）：Attribute与Annotation&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/why-java-sucks-and-csharp-rocks-4-generics.html"&gt;Why Java Sucks and C# Rocks（4）：泛型&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;Why Java Sucks and C# Rocks（补1）：Reddit，兼谈C#属性&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/more-why-java-sucks-and-csharp-rocks-1-reddit-and-property.html#comments</comments>
      <pubDate>Mon, 28 Jun 2010 06:18:19 GMT</pubDate>
      <lastBuildDate>Mon, 28 Jun 2010 06:18:19 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助首届.NET技术交流会 - 演讲录像及下载</title>
      <link>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html</link>
      <guid>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html</guid>
      <description>&lt;p&gt;经过几天的努力，终于将&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;赞助的&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;首届.NET技术交流会&lt;/a&gt;的演讲录像制作完成了。本来在现在的高清视频以外，我还想像Channel 9一样提供一些低码率的格式下载，但多次尝试都以失败告终，各中滋味难以言喻。因此目前只能给大家提供mov格式的高清视频下载，对于Windows下各类强大的播放器都不成问题。您也可以在线观看这些视频，不过上传至优酷后，发现除了清晰度较低外，甚至还有音画不同步的问题。我正在联系&lt;a href="http://www.ku6.com/"&gt;酷六网&lt;/a&gt;，会尽快用上质量更好的视频。&lt;/p&gt;

&lt;h1&gt;F#语言对异步程序设计的支持&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;赵劼，盛大创新院，研究员&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;如今的Web应用、 Silverlight以及各种分布式系统让异步解决方案有了更进一步的需求。F#是微软.NET平台上的函数式编程语言，并添加了不少让并行及异步编程变得有趣且轻松的特性。本次演讲将讨论F#的核心概念，并探讨F#中的不可变性、函数式设计、异步工作流、代理等特性是如何应对真实应用中的异步挑战的。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMjEzMDM1MDgw/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100624/1/fsharp-async-20100619-high.mov"&gt;高清视频下载&lt;/a&gt;（mov格式，1280 * 720，495M）&lt;/p&gt;

&lt;h1&gt;Rails: Better Framework, Better Life&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;吕国宁，Intridea.com，高级工程师&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;RoR是Ruby on Rails的缩写，是一个用于编写Web应用的框架。它基于Ruby语言，给开发人员提供了强大便利的框架支持。Ruby有很多优点，但是一直以来其流行范围仅局限于日本。2004年，当Rails框架横空出世，让人们认识到了一个更符合实际需要并且高效的web框架，在其出现不久就受到了业内的广泛关注。吕国宁将结合自己三年的Rails开发经验，给大家介绍一些Rails的优点，背后的设计文化，以及Rails的前景发展等内容。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMTg0MTI0NjU2/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100624/1/ror-20100619-high.mov"&gt;高清视频下载&lt;/a&gt;（mov格式，1280 * 720，432M）&lt;/p&gt;

&lt;h1&gt;大众点评网的技术变迁之路&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;王宏，大众点评网，架构师&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;大众点评网从2003年创建以来，已经经历了7个年头，在技术方面从最初构建时期的简单的、低成本的方案，到发展阶段不断“痛苦”的转型演变，到目前比较复杂的技术架构，大众点评网的技术团队一直在关注业界新技术，力求提高可用性、降低成本、优化用户体验，并针对“点评”这一第三方参与的特点，摸索出一些特有的解决方案，借此机会希望能够分享给大家。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMTg0MTA5MTY0/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100624/1/dianping-20100619-high.mov"&gt;高清视频下载&lt;/a&gt;（mov格式，1280 * 720，486M）&lt;/p&gt;

&lt;h1&gt;Q &amp;amp; A&lt;/h1&gt;

&lt;p&gt;按照计划，原本还会有一场关于C#的演讲，但该场的讲师由于突然有急事只得作罢。于是我在最后增加了“演讲嘉宾问答”的环节，您可以在&lt;a href="http://www.ku6.com/"&gt;酷六网&lt;/a&gt;上进行在线观看。&lt;/p&gt;

&lt;p&gt;第1段：&lt;/p&gt;
&lt;embed src="http://player.ku6.com/refer/QFX3kb6ZJ8Wqge-w/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="always" allowfullscreen="true" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;第2段：&lt;/p&gt;
&lt;embed src="http://player.ku6.com/refer/wmD_kenujbB6gxnM/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="always" allowfullscreen="true" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;盛大创新院赞助首届.NET技术交流会开始报名了！&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html"&gt;盛大创新院赞助首届.NET技术交流会即将召开&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;盛大创新院赞助首届.NET技术交流会 - 各场演讲幻灯片&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会 - 演讲录像及下载 &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html#comments</comments>
      <pubDate>Thu, 24 Jun 2010 06:40:51 GMT</pubDate>
      <lastBuildDate>Thu, 24 Jun 2010 06:40:51 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助首届.NET技术交流会 - 各场演讲幻灯片</title>
      <link>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html</link>
      <guid>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html</guid>
      <description>&lt;p&gt;今天是近期最热的一天，气温高达35度，异常闷热，但是依然有160多位朋友冒着酷暑参加了&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;赞助的&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;首届.NET技术交流会&lt;/a&gt;，这让我感到很欣慰，因此这里首先要感谢大家的支持。我刚才浏览了一下三场演讲的桌面录像，可谓异常完美，现在只等酷六网的摄影师的讲师录像到手，便可以合成为最终的演讲视频了，希望能够尽快展示给大家。不过现在，大家可以在第一时间浏览本次活动新鲜出炉的幻灯片。&lt;/p&gt;

&lt;h1&gt;F#语言对异步程序设计的支持&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;赵劼，盛大创新院，研究员 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;如今的Web应用、 Silverlight以及各种分布式系统让异步解决方案有了更进一步的需求。F#是微软.NET平台上的函数式编程语言，并添加了不少让并行及异步编程变得有趣且轻松的特性。本次演讲将讨论F#的核心概念，并探讨F#中的不可变性、函数式设计、异步工作流、代理等特性是如何应对真实应用中的异步挑战的。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_4546454"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="F#语言对异步程序设计的支持" href="http://www.slideshare.net/jeffz/f-4546454"&gt;F#语言对异步程序设计的支持&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse4546454" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=f-100619111719-phpapp01&amp;stripped_title=f-4546454" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse4546454" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=f-100619111719-phpapp01&amp;stripped_title=f-4546454" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;h1&gt;Rails: Better Framework, Better Life&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;吕国宁，Intridea.com，高级工程师 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;RoR是Ruby on Rails的缩写，是一个用于编写Web应用的框架。它基于Ruby语言，给开发人员提供了强大便利的框架支持。Ruby有很多优点，但是一直以来其流行范围仅局限于日本。2004年，当Rails框架横空出世，让人们认识到了一个更符合实际需要并且高效的web框架，在其出现不久就受到了业内的广泛关注。吕国宁将结合自己三年的Rails开发经验，给大家介绍一些Rails的优点，背后的设计文化，以及Rails的前景发展等内容。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_4545483"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="Better Framework Better Life" href="http://www.slideshare.net/jeffz/better-framework-better-life"&gt;Better Framework Better Life&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse4545483" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=better-framework-better-life-100619101625-phpapp01&amp;stripped_title=better-framework-better-life" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse4545483" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=better-framework-better-life-100619101625-phpapp01&amp;stripped_title=better-framework-better-life" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;h1&gt;大众点评网的技术变迁之路&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;王宏，大众点评网，架构师 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;大众点评网从2003年创建以来，已经经历了7个年头，在技术方面从最初构建时期的简单的、低成本的方案，到发展阶段不断“痛苦”的转型演变，到目前比较复杂的技术架构，大众点评网的技术团队一直在关注业界新技术，力求提高可用性、降低成本、优化用户体验，并针对“点评”这一第三方参与的特点，摸索出一些特有的解决方案，借此机会希望能够分享给大家。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_4545503"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="大众点评网的技术变迁之路" href="http://www.slideshare.net/jeffz/ss-4545503"&gt;大众点评网的技术变迁之路&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse4545503" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-100619101718-phpapp02&amp;stripped_title=ss-4545503" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse4545503" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-100619101718-phpapp02&amp;stripped_title=ss-4545503" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;

&lt;/div&gt;&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;盛大创新院赞助首届.NET技术交流会开始报名了！&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html"&gt;盛大创新院赞助首届.NET技术交流会即将召开&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会 - 各场演讲幻灯片 &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;盛大创新院赞助首届.NET技术交流会 - 演讲录像及下载&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html#comments</comments>
      <pubDate>Sat, 19 Jun 2010 15:48:23 GMT</pubDate>
      <lastBuildDate>Sat, 19 Jun 2010 15:48:23 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助首届.NET技术交流会即将召开</title>
      <link>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html</link>
      <guid>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html</guid>
      <description>&lt;p&gt;由&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;赞助的首届.NET技术大会将于6月19号下午1点召开，本次交流会请到了四位讲师，议题覆盖了F#、C#、Rails及架构等多个方面。我已经看过了各场演讲的幻灯片草稿，也很期待各位讲师在正式演讲中的表现。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/conf-1-600x850.jpg" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/conf-1-600x850.jpg" width="300" /&gt;&lt;/a&gt; 

&lt;p&gt;本次大会中，我们还获得了人民邮电出版社&lt;a href="http://www.turingbook.com/Homepage/Default.aspx"&gt;图灵教育&lt;/a&gt;赠送的20册图书，将会作为奖品赠送给在交流会中表现积极的听众。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/turingbook.jpg" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/turingbook.jpg" width="300" /&gt;&lt;/a&gt; 

&lt;p&gt;此外，我们还请到了&lt;a href="http://www.ku6.com/"&gt;酷六网&lt;/a&gt;的专业摄影师对演讲过程进行全程拍摄，并配合各位讲师自身的屏幕录像，将在后期合成为适合独立观看的演讲视频，让不能到场的朋友在线或是下载后观看。&lt;/p&gt;

&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/ku6-logo.jpg" /&gt; 

&lt;p&gt;本次会议的邀请函已经发给各位报名者，请携带邀请函至会议现场签到，&lt;span style="color:red;"&gt;没有报名的朋友可以在现场直接报名&lt;/span&gt;。&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;关于会议的时间、地点、交通、议程等更多信息，请关注会议的报名信息&lt;/a&gt;。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;盛大创新院赞助首届.NET技术交流会开始报名了！&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会即将召开 &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;盛大创新院赞助首届.NET技术交流会 - 各场演讲幻灯片&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;盛大创新院赞助首届.NET技术交流会 - 演讲录像及下载&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html#comments</comments>
      <pubDate>Thu, 17 Jun 2010 03:45:06 GMT</pubDate>
      <lastBuildDate>Thu, 17 Jun 2010 03:45:06 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>趣味编程：在JavaScript中实现简单的yield功能（2 - 循环支持及解释执行）</title>
      <link>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-2-loop-and-interpreter.html</link>
      <guid>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-2-loop-and-interpreter.html</guid>
      <description>&lt;p&gt;在&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-1-yield-and-yieldseq.html"&gt;上一篇文章&lt;/a&gt;里，我们构造了$yield和$yieldSeq两个基础编程组件，于是我们可以“在一定程度上”使用C#中的yield功能来构造一个迭代器。从表面上看来似乎不错，不过它的实际价值还是有值得推敲的。例如，我们目前还必须使用递归来代替循环，那么我们有办法改变这种变成方式吗？此外，在评论中有朋友谈到，这样写代码其实不是一个格式化就混乱了吗？至少，您是否觉得这种编程方法的function和括号实在多了些呢？这次我们就来探索这些问题的解决办法。&lt;/p&gt;

&lt;h1&gt;想法来源&lt;/h1&gt;

&lt;p&gt;为此，还是先谈一下我这种想法的来源吧——这便是F#里的“序列”：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
&lt;span style="color: blue"&gt;let &lt;/span&gt;fibSeq() = seq {
    &lt;span style="color: blue"&gt;let rec &lt;/span&gt;fibSeq' a b = seq {
        &lt;span style="color: blue"&gt;let &lt;/span&gt;next = a + b
        &lt;span style="color: blue"&gt;yield &lt;/span&gt;next
        &lt;span style="color: blue"&gt;yield! &lt;/span&gt;fibSeq' b next
    }

    &lt;span style="color: blue"&gt;yield &lt;/span&gt;0
    &lt;span style="color: blue"&gt;yield &lt;/span&gt;1
    &lt;span style="color: blue"&gt;yield! &lt;/span&gt;fibSeq' 0 1
}&lt;/pre&gt;

&lt;p&gt;瞧，这个无限的斐波那契序列实现方式，和之前的JavaScript做法是否几乎完全一致？其实在F#中，seq本身（从理念上讲）并不是个语言特性，而是基于一种更为抽象的语言特性“&lt;a href="http://msdn.microsoft.com/en-us/library/dd233182.aspx"&gt;（计算表达式）Computation Expressions&lt;/a&gt;”而实现的功能。换句话说，seq只是一个“类库”，和我&lt;a href="http://blog.zhaojie.me/2010/03/async-and-parallel-design-patterns-in-fsharp-1-parallelizing-cpu-and-io-computations.html"&gt;以前经常强调的async一样&lt;/a&gt;，都不是语言特性而是类库功能。我们基于这个语言特性也可以写出我们自己的实现，这样我们也可以用上xyz { ... }等等，而不仅仅是seq { ... }或async { ... }等“内置”的功能了。&lt;/p&gt;

&lt;p&gt;Computation Expressions其实是一种“语法糖”，如刚才的fibSeq在“解糖”之后就变成了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
&lt;span style="color: blue"&gt;let &lt;/span&gt;fibSeq() = seq {
    &lt;span style="color: blue"&gt;let rec &lt;/span&gt;fibSeq' a b =
        &lt;span style="color: blue"&gt;let &lt;/span&gt;next = a + b
        seq.Combine(
            seq.Yield(next),
            seq.Delay(&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt; 
                &lt;/span&gt;seq.YieldFrom(fibSeq' next b)))

    seq.Combine(
        seq.Yield(0),
        seq.Delay(&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt;&lt;/span&gt;
            seq.Combine(
                seq.Yield(1),
                seq.Delay(&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt;&lt;/span&gt;
                    seq.YieldFrom(fibSeq' 0 1)))))
}&lt;/pre&gt;

&lt;p&gt;总体来说，它们都是把顺序的语法拆成了一段一段，再通过回调函数进行连接的结果。事实上，“计算表达式”便是定义了一系列的“基础控制元素”，然后将一系列的表达式“解糖”为这些基础控制元素的调用。有些朋友可能发现这其实便是Monad（类似的东西），这话一点没错，F#的设计者Don Syme便&lt;a href="http://blogs.msdn.com/b/dsyme/archive/2007/09/22/some-details-on-f-computation-expressions-aka-monadic-or-workflow-syntax.aspx"&gt;曾经写道&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;... Likewise the kinds of operations used under the hood are much like the operations used in both LINQ and Haskell monads. Indeed, computation expressions can be seen as a general monadic syntax for F#.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果不看实际的代码样式，只看“感觉”，您是否发现这和前文的做法十分接近？其实刚才我之前的JS其实也是利用了这种思路，只是直接写出了“解糖”后语法（且解糖的具体方式有所不同），于是会显得比较丑陋——因此，谁说语法糖不重要？&lt;/p&gt;

&lt;h1&gt;while循环&lt;/h1&gt;

&lt;p&gt;其实F#的计算表达式中也提供了对于while循环的基础构造元素，这对我们现在要做的事情来说像是一个指导。那么我们来看看F#是如何“解糖”while语句的吧。假如我们有这样一个计算表达式：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
foo {
    &lt;span style="color: blue"&gt;let &lt;/span&gt;i = ref 0;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(i.Value &amp;lt; 10) &lt;span style="color: blue"&gt;do&lt;/span&gt;
        i.Value &amp;lt;- i.Value + 1
    ...
}&lt;/pre&gt;

&lt;p&gt;那么它解糖后的结果便是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
&lt;span style="color: blue"&gt;let &lt;/span&gt;i = ref 0;
bar.While(
    (&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;i.Value &amp;lt; 10),
    (&lt;span style="color: blue"&gt;fun &lt;/span&gt;()  &lt;span style="color: blue"&gt;-&amp;gt; &lt;/span&gt;i.Value &amp;lt;- i.Value + 1))&lt;/pre&gt;

&lt;p&gt;换句话说，当我们使用这样的表达式时：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
&lt;span style="color: blue"&gt;while&lt;/span&gt; (&lt;i&gt;cond-expr&lt;/i&gt;) &lt;span style="color: blue"&gt;do&lt;/span&gt;
    &lt;i&gt;loop-body&lt;/i&gt;&lt;/pre&gt;

&lt;p&gt;实际上我们执行的是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
&lt;span style="color: green"&gt;// member While: (unit -&amp;gt; bool) * Result&amp;lt;unit&amp;gt; –&amp;gt; Result&amp;lt;unit&amp;gt;&lt;/span&gt;
foo.While(
    (&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt;&lt;/span&gt; &lt;i&gt;cond-expr&lt;/i&gt;),
    (&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt;&lt;/span&gt; &lt;i&gt;loop-body&lt;/i&gt;))&lt;/pre&gt;

&lt;p&gt;看到了没？我们不妨也这么做吧。例如当我们在C#里写这样的代码时：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; Infinite(&lt;span style="color: blue"&gt;int &lt;/span&gt;start)
{
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) &lt;span style="color: blue"&gt;yield return &lt;/span&gt;start++;
}&lt;/pre&gt;

&lt;p&gt;它所对应的JavaScript便是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;infinite(start) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$while(
        &lt;span style="color: blue"&gt;function &lt;/span&gt;() { &lt;span style="color: blue"&gt;return true&lt;/span&gt;; },
        &lt;span style="color: blue"&gt;function &lt;/span&gt;() { &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(start++); });
}&lt;/pre&gt;

&lt;p&gt;其中$while方法的实现是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;$while(cond, body, rest) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(cond()) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;iter = body();
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(iter) {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(iter, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$while(cond, body, rest);
            });
        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$while(cond, body, rest);
        }
    } &lt;span style="color: blue"&gt;else if &lt;/span&gt;(rest) {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;rest();
    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
        &lt;span style="color: blue"&gt;return null&lt;/span&gt;;
    }
}&lt;/pre&gt;

&lt;p&gt;$while本身也是返回一个迭代器，不过它并不会像$yield或$yieldSeq那样“构造”出一个迭代器，而更像是在做一个“协调”的工作。例如，当cond为false时，它将直接返回后续的迭代器；否则，便会从body生成一个迭代器对象，并将下一个$while，也就是需要重新执行的内容使用$yieldSeq函数“拼接”在最后。由此我们可以看出（至少我感觉如此），使用无副作用的编程方式，在业务逻辑的表现上会比“状态改变”更为清晰一些。&lt;/p&gt;

&lt;h1&gt;先解析，后执行&lt;/h1&gt;

&lt;p&gt;现在，我们就试着使用刚才定义的$while函数来重写原来的rangeSeq函数吧：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;rangeWhileSeq(minInclusive, maxExclusive) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;i = minInclusive;
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$while(
        &lt;span style="color: blue"&gt;function &lt;/span&gt;() { &lt;span style="color: blue"&gt;return &lt;/span&gt;i &amp;lt; maxExclusive; },
        &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(i, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                i++;
            });
        });
}&lt;/pre&gt;

&lt;p&gt;额，您看明白这段代码的含义了没？总之我是看的够呛，光有yield还好说，一旦加上$while就容易让人神智不清了。更重要的是，我这里真不知道怎么格式化才能显得清晰一些。因此，$while的可用性远不如$yield和$yieldSeq来的好。那么，我们该怎么办才好？先不谈这个，先想想我们究竟希望怎么样的编程方式吧……这样如何？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;rangeWhile(minInclusive, maxExclusive) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;i = minInclusive;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(i &amp;lt; maxExclusive) {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(i);
        i++;
    }
}&lt;/pre&gt;

&lt;p&gt;这完全就是一种最为理想的方式，不是么？易于编写，语意良好，格式美观，几乎没有缺点。那么，您有办法将这个rangeWhile方法“改写”成上面rangeWhileSeq的形式吗？还有，您能把这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;fibSeq() {
    &lt;span style="color: blue"&gt;function &lt;/span&gt;fibSeq$(a, b) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;next = a + b;
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(next);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(fibSeq$(b, next));
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(0);
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(1);
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(fibSeq$(0, 1));
}&lt;/pre&gt;

&lt;p&gt;改写成如下形式吗？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;fibSeq() {
    &lt;span style="color: blue"&gt;function &lt;/span&gt;fibSeq$(a, b) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;next = a + b;
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(next, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(fibSeq$(b, next));

        });
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(0, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(1, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(fibSeq$(0, 1));

    });
    });
}&lt;/pre&gt;

&lt;p&gt;我相信这实现起来不会过于困难。&lt;/p&gt;

&lt;p&gt;事实上，我认为这可以说是JavaScript语言最终极的黑魔法。由于JavaScript的语言特性，我们完全可以调用一个函数的toString方法，再进行“改造”，最后再eval回去。利用这种方式，JavaScript可以说拥有了几乎无限的语法糖能力。事实上，类似的事情一直有人在做。例如，已经有人用JavaScript写出&lt;a href="http://paulirish.com/work/gordon/demos/"&gt;Flash播放器&lt;/a&gt;以及&lt;a href="http://logand.com/sw/wps/index.html"&gt;PostScript解释器&lt;/a&gt;。因此，我们为什么不把这种方式直接用在JavaScript自身呢？&lt;/p&gt;

&lt;h1&gt;提高JavaScript的开发效率&lt;/h1&gt;

&lt;p&gt;再次回到F#吧。刚才我们提到，计算表达式的试用性很广，除了seq以外，F#的核心库还实现了“&lt;a href="http://msdn.microsoft.com/en-us/library/dd233250.aspx"&gt;异步工作流（Asynchronous Workflows）&lt;/a&gt;”。例如下面的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:green;"&gt;// F#&lt;/span&gt;
async {
    &lt;span style="color: blue"&gt;let &lt;/span&gt;req = WebRequest.Create(&lt;span style="color: maroon"&gt;&amp;quot;http://blog.zhaojie.me/&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;let! &lt;/span&gt;resp = req.GetResponseAsync()
    &lt;span style="color: blue"&gt;use &lt;/span&gt;stream = resp.GetResponseStream()
    &lt;span style="color: blue"&gt;let &lt;/span&gt;reader = &lt;span style="color: blue"&gt;new &lt;/span&gt;StreamReader(stream)
    &lt;span style="color: blue"&gt;let! &lt;/span&gt;html = reader.ReadToEndAsync()
    &lt;span style="color: blue"&gt;return &lt;/span&gt;html
}&lt;/pre&gt;

&lt;p&gt;便会被“解糖”为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// F#&lt;/span&gt;
async.Delay(&lt;span style="color: blue"&gt;fun &lt;/span&gt;() &lt;span style="color: blue"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span style="color: blue"&gt;let &lt;/span&gt;req = WebRequest.Create(&lt;span style="color: maroon"&gt;&amp;quot;http://blog.zhaojie.me/&amp;quot;&lt;/span&gt;)&lt;span style="color: blue"&gt;
    &lt;/span&gt;async.Bind(req.GetResponseAsync(), (&lt;span style="color: blue"&gt;fun &lt;/span&gt;resp –&lt;span style="color: blue"&gt;&amp;gt;
        &lt;/span&gt;async.Using(resp.GetResponseStream(), (&lt;span style="color: blue"&gt;fun &lt;/span&gt;stream –&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;
            &lt;span style="color: blue"&gt;let &lt;/span&gt;reader = &lt;span style="color: blue"&gt;new &lt;/span&gt;StreamReader(stream)&lt;span style="color: blue"&gt;
            &lt;/span&gt;async.Bind(reader.ReadToEndAsync(), (&lt;span style="color: blue"&gt;fun &lt;/span&gt;html &lt;span style="color: blue"&gt;-&amp;gt;
                &lt;/span&gt;async.Return(html))))))))&lt;/pre&gt;

&lt;p&gt;这里let!会被视作一次Bind函数的调用，F#在此会发起一个异步操作（如一个基于IOCP的异步操作），并会在操作结束后执行回调函数。这样，我们便可以使用异步工作流轻松地编写异步程序，而不需要在纷繁的回调函数中纠缠不清。循环也好，递归也罢，异步工作流都会帮我们保留各式上下文，按照我们的需要进行逻辑控制。&lt;/p&gt;

&lt;p&gt;这就是了。刚才我们提出在JavaScript中解析“迭代器”的生成代码，那么我们能用同样的方式实现一个完整的“计算表达式”特性吗？事实上，这样JavaScript就几乎获得了完整的，与F#一样强大且优雅的特性。更重要的是，“异步操作”是JavaScript的应用领域中最为常见的使用场景（没有之一），如果JavaScript拥有了F#中的“异步工作流”，那么JavaScript的开发体验一定会大大增强。&lt;/p&gt;

&lt;p&gt;我认为，首先于语言特性，JavaScript本身的生产力似乎已经遇到了瓶颈，必须借助“外力”才能带来更好的开发效率。例如&lt;a href="http://code.google.com/webtoolkit/"&gt;GWT&lt;/a&gt;和&lt;a href="http://www.nikhilk.net/"&gt;Script#&lt;/a&gt;，都是通过编写服务器端的代码（如Java和C#）再编译成JavaScript以获得更好的开发效率（虽然&lt;a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg"&gt;Anders Hejlsberg&lt;/a&gt;认为&lt;a href="http://blog.zhaojie.me/2010/05/trends-and-future-directions-in-programming-languages-by-anders-4-dynamic-languages.html"&gt;并不认为这是正途&lt;/a&gt;）。在这方面，我认为走的更远的是莫过于&lt;a href="http://www.intellifactory.com/products/Home.aspx"&gt;WebSharper&lt;/a&gt;，它是一个基于F#构建的Web开发平台，使用F#构造从前到后的一整套内容。其中利用到F#中许多高级的开发特性，并可以将F#代码直接转化JavaScript，这样服务器端和客户端的通信也变得透明起来。事实上我很看好这种方式，尤其是在HTML 5出现之后，越来越多的东西可以使用JavaScript编写，我认为这是增强Web平台开发效率的方向之一。&lt;/p&gt;

&lt;p&gt;当然，如WebSharper这类与平台（.NET，F#）密切相关的产品，其实适用性会受到限制。不过，如我上面说的那样，如果可以用JavaScript开发出一套“解释器”，或是一种可以大大提高生产力的语法糖，其通用性一定会更高，且更容易被前端开发人员所接受。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html"&gt;趣味编程：在JavaScript中实现简单的yield功能（问题）&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-1-yield-and-yieldseq.html"&gt;趣味编程：在JavaScript中实现简单的yield功能（1 - yield与yieldSeq）&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;趣味编程：在JavaScript中实现简单的yield功能（2 - 循环支持及解释执行）&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-2-loop-and-interpreter.html#comments</comments>
      <pubDate>Fri, 11 Jun 2010 12:41:32 GMT</pubDate>
      <lastBuildDate>Fri, 11 Jun 2010 12:41:32 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>趣味编程：在JavaScript中实现简单的yield功能（1 - yield与yieldSeq）</title>
      <link>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-1-yield-and-yieldseq.html</link>
      <guid>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-1-yield-and-yieldseq.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html"&gt;上文&lt;/a&gt;我谈到了迭代器及其生成器，即C#或Python中的yield功能，它们极大地简化了创建一个迭代器的工作，让代码的语义和可读性有了很大提高。虽然在&lt;a href="https://developer.mozilla.org/en/New_in_JavaScript_1.7#Generators"&gt;JavaScript 1.7&lt;/a&gt;中已经有了相同的功能，可惜目前我们还无法用到这种强大的能力。那么，我们能不能为JavaScript提供如C#中一样的功能？在文章的评论里许多朋友也给出了他们的解决方案，也让我获得了许多启发。因此，我也打算在以后的文章中总结一下各位的做法。不过在这篇文章里，我先来阐述一下我个人的想法。&lt;/p&gt;

&lt;h1&gt;实现简单的yield功能&lt;/h1&gt;

&lt;p&gt;先从简单的做起，例如实现C#中的这样一个迭代器：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; OneToThree()
{
    &lt;span style="color: blue"&gt;yield return&lt;/span&gt; 1;
    &lt;span style="color: blue"&gt;yield return&lt;/span&gt; 2;
    &lt;span style="color: blue"&gt;yield return&lt;/span&gt; 3;
}&lt;/pre&gt;

&lt;p&gt;在执行阶段，代码自然不会像表面上那样“一蹴而就”，从逻辑上讲它们是“一个阶段一个阶段”进行下去的。调用OneToThree时，方法中的代码不会执行；在迭代器的MoveNext方法第一次被调用时，代码才从头执行至第一个yield return处，便立即并返回；接下来每次调用MoveNext时，都会从上一个yield return的下一行代码开始继续执行下去，直到下一个yield return，或是方法结束为止。&lt;/p&gt;

&lt;p&gt;在JavaScript中，我们自然无法让方法从某一行代码处立即返回，并且在需要的时候立即执行下去。因此我们要做的只能是，让代码在需要停止的地方“立即返回”，真正地返回，不留情面的返回。那么，yield之后的接下去的代码又该怎么办呢？那就必须用一种“回调”的方式来“继续”我们的工作了。因此，我们的“开发模式”大约是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;oneToThree() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(1, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(2, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(3);

    });
    });
}&lt;/pre&gt;

&lt;p&gt;这段代码其实就体现了我们上面描述的编程模式。这里虽然有好几行代码，但事实上oneToThree的第一行代码便是个return，它会立即返回$yield方法的执行结果。我们为第一个$yield方法调用传递了两个参数，一是0，表示迭代器要输出的值，而第二个参数则是个回调函数，表示需要继续执行的代码，其中包含了第二个return $yield，此时又有一个回调函数，包含了最后一个return。我们就是利用了这种方法实现了简单的yield功能。&lt;/p&gt;

&lt;p&gt;很显然，我在这里强制破坏了JavaScript的缩进规则，让三句return $yield看上去是平行的，而不是嵌套的关系。同时，我将每个函数调用或是回调函数的大小括号放在了最后，远离主体代码。如果您的眼睛略有近视，或者像我一样能够“屏蔽”其他“架子代码”，就能感受到这种做法的美妙。&lt;/p&gt;

&lt;p&gt;至于实现，感觉还是比较容易的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// JavaScript
&lt;/span&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;$yield(value, rest) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        value: value,
        _rest: rest,
        next: $yield._next
    };
}
$yield._next = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;._rest) {
        &lt;span style="color: blue"&gt;return this&lt;/span&gt;._rest();
    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
        &lt;span style="color: blue"&gt;return null&lt;/span&gt;;
    }
}&lt;/pre&gt;

&lt;p&gt;说实话，这也就是单向链表罢了。只不过这个单向链表的next指针不是一个普通的引用，而是在需要的时候，根据一个回调函数获得的。“在需要的时候”，这便是“延迟”，这为我们生成“无限”序列奠定了基础。&lt;/p&gt;

&lt;h1&gt;循环？递归！&lt;/h1&gt;

&lt;p&gt;那么现在就有个问题了，比如说，在之前C#里的Infinite或Range方法中利用到了循环，那么在上面的yield模式中也可以这样吗？显然不行，JavaScript遇到return就直接跳出方法了，虽然我们可以执行回调函数，但是我们做不到让代码跟着循环一遍遍地前进。&lt;/p&gt;

&lt;p&gt;“不能使用循环”，这个问题其实很普通，也很容易解决，因为“循环”本来就不是每种编程范式都拥有的东西。“循环”则意味着要改变某个状态，这是个“副作用”，因此在一些无副作用的函数式编程语言来说，便从来就没有“循环”这种东西。不过大家过的还都是好好的，因为我们还有“递归”。假如我们要编写一个numSeq方法，给定一个n，要求生成一个无限的序列，那么可以怎么做？如果使用递归的思路来思考这个问题，则它可以分解为两个步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;输出n； &lt;/li&gt;

  &lt;li&gt;依次输出numSeq(n + 1)中的每个元素。 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;同样，range方法也可以分解为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果minInclusive大于等于maxInclusive，则返回一个空序列。 &lt;/li&gt;

  &lt;li&gt;输出minInclusive。 &lt;/li&gt;

  &lt;li&gt;依次输出range(minInclusive + 1, maxInclusive)中的每个元素。 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以，我们需要的是“依次输出每个序列”这样一个功能，在这里我把它称作是$yieldSeq。这样我们便可以写出这样numSeq函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;numSeq(n) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(n, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(numSeq(n + 1));

    });
}&lt;/pre&gt;

&lt;p&gt;由于没有递归出口，numSeq将会生成一个无限的序列，但是其中每个元素都是“按需”生成的，我们可以仅仅打印出其中N项：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// print 0 .. 9
&lt;/span&gt;&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;iter = numSeq(0); iter; iter = iter.next()) {
    document.write(iter.value + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(iter.value &amp;gt;= 9) &lt;span style="color: blue"&gt;return&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;与$yield相同，$yieldSeq返回的也是一个序列，它们之间的区别是：yield返回的序列包含单个元素和剩下的部分，而$yieldSeq返回的是“一个序列中的所有元素”，再接上剩余的部分：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;$yieldSeq(iter, rest) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(!rest) &lt;span style="color: blue"&gt;return &lt;/span&gt;iter;
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(!iter) &lt;span style="color: blue"&gt;return &lt;/span&gt;rest();

    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        value: iter.value,
        _iter: iter,
        _rest: rest,
        next: $yieldSeq._next
    };
}
$yieldSeq._next = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(&lt;span style="color: blue"&gt;this&lt;/span&gt;._iter.next(), &lt;span style="color: blue"&gt;this&lt;/span&gt;._rest);
}&lt;/pre&gt;

&lt;p&gt;现在我们便可以使用$yieldSeq来写出rangeSeq这样的函数了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;rangeSeq(minInclusive, maxExclusive) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(minInclusive &amp;gt;= maxExclusive) &lt;span style="color: blue"&gt;return null&lt;/span&gt;;

    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(minInclusive, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(range(minInclusive + 1, maxExclusive));

    });
}&lt;/pre&gt;

&lt;p&gt;甚至是个斐波那契数列：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;fibSeq() {
    &lt;span style="color: blue"&gt;function &lt;/span&gt;fibSeq$(a, b) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;next = a + b;
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(next, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(fibSeq$(b, next));

        });
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(0, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yield(1, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$yieldSeq(fibSeq$(0, 1));

    });
    });
}&lt;/pre&gt;

&lt;p&gt;在fibSeq内部我们定义了一个fibSeq$函数，它的作用是输出a、b两项以后的“无限长”的序列。因此在fibSeq方法中，我们先yield出去0和1两个元素，再依次输出fibSeq$序列中的元素。得到了无限长的斐波那契数列之后，我们便可以做一些有趣的事情了，例如500以内的元素之和：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;sum = 0;
&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;iter = fibSeq(); iter; iter = iter.next()) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(iter.value &amp;lt;= 500) {
        sum = sum + iter.value;
    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
        &lt;span style="color: blue"&gt;break&lt;/span&gt;;
    }
}

document.write(sum + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

&lt;p&gt;但是这么做实在太丑，因为我们还少了许多必要的基础元素，如果我们的目标是写出这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; sum = toIter(fibSeq()).takeWhile(&lt;span style="color: blue"&gt;function&lt;/span&gt; (i) { &lt;span style="color: blue"&gt;return&lt;/span&gt; i &amp;lt;= 500; }).sum();&lt;/pre&gt;

&lt;p&gt;那么您可以实现toIter及takeWhile吗？此外，take，skip，where，filter&lt;strike&gt;&lt;/strike&gt;等常见功能，您可以一并实现吗？其实这就十分简单了，就留给感兴趣的朋友工作之余用来放松神经吧。&lt;/p&gt;

&lt;h1&gt;可是我还是想要循环……&lt;/h1&gt;

&lt;p&gt;说实话，我喜欢“递归”的方式，毕竟这是种可读性更好的，无副作用的声明式解法；与此相对，“循环”是一种&lt;a href="http://blog.zhaojie.me/2010/04/trends-and-future-directions-in-programming-languages-by-anders-2-declarative-programming-and-dsl.html"&gt;描述“how to do”&lt;/a&gt;，依赖可变状态的命令式解法。不过，毕竟JavaScript主要还是一门命令式的编程语言，它的特性并不能写出十分优雅的函数式代码。而且，从上面一些例子中看，如numSeq，rangeSeq来说，能够使用循环会更为直观一些。那么，我们又该如何实现呢？&lt;/p&gt;

&lt;p&gt;这又是一个值得讨论话题了，我原本打算一篇写完，但是发现篇幅有些太长，那还是分开进行吧。而且，这还需要从其他一些东西谈起，我们下次再继续吧。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html"&gt;趣味编程：在JavaScript中实现简单的yield功能（问题）&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;趣味编程：在JavaScript中实现简单的yield功能（1 - yield与yieldSeq） &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-1-yield-and-yieldseq.html#comments</comments>
      <pubDate>Thu, 10 Jun 2010 01:36:01 GMT</pubDate>
      <lastBuildDate>Thu, 10 Jun 2010 01:36:01 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>趣味编程：在JavaScript中实现简单的yield功能（问题）</title>
      <link>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html</link>
      <guid>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html</guid>
      <description>&lt;p&gt;说起迭代器（Iterator）大家一定都不陌生，无论是是Java，C#或是Python等语言都有内置标准的迭代器结构，它们也都提供了内置的for或foreach关键字简化迭代器的“使用”。不过对于迭代器的“生成”，不同语言之间的就会有很大差距。例如，在C#和Python中都提供了yield来简化迭代器的“创建”，此时生成一个迭代器便再简单不过了。但对于Java程序员来说，即使到了Java 7还必须为在迭代器内部手动维护状态，非常痛苦。而更重要的一点是，利用yield我们可以轻松地创建一个“延迟”的，“无限”的序列。那么，我们能否在JavaScript中享受到这样的yield生成器呢？&lt;/p&gt;

&lt;h1&gt;yield的功效&lt;/h1&gt;

&lt;p&gt;先来演示一下yield的作用，例如我们要写一个C#方法，返回一个迭代器（即.NET中的IEnumerable），它会生成从minInclusive到maxExclusive之间的整数序列：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; Range(&lt;span style="color: blue"&gt;int &lt;/span&gt;minInclusive, &lt;span style="color: blue"&gt;int &lt;/span&gt;maxExlusive)
{
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = minInclusive; i &amp;lt; maxExlusive; i++) &lt;span style="color: blue"&gt;yield return &lt;/span&gt;i;
}&lt;/pre&gt;

&lt;p&gt;而在Java中便会麻烦许多：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// Java&lt;/span&gt;
&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Range &lt;/span&gt;&lt;span style="color: blue"&gt;implements &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterable&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt; {

    &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_maxExclusive;
    &lt;span style="color: blue"&gt;private int &lt;/span&gt;m_current;
    
    &lt;span style="color: blue"&gt;public &lt;/span&gt;Range(&lt;span style="color: blue"&gt;int &lt;/span&gt;minInclusive, &lt;span style="color: blue"&gt;int &lt;/span&gt;maxExclusive) {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_maxExclusive = maxExclusive;
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.m_current = minInclusive;
    }

    @Override
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterator&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt; iterator() {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Iterator&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;&amp;gt;() {
            &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: blue"&gt;boolean &lt;/span&gt;hasNext() {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;m_current &amp;lt; m_maxExclusive;
            }
            
            &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt; next() {
                &lt;span style="color: blue"&gt;int &lt;/span&gt;current = m_current;
                m_current = m_current + 1;
                &lt;span style="color: blue"&gt;return &lt;/span&gt;current;
            }

            &lt;span style="color: blue"&gt;public void &lt;/span&gt;remove() {
                &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;UnsupportedOperationException&lt;/span&gt;();
            }
        };
    }
}&lt;/pre&gt;

&lt;p&gt;其实就最终执行的代码来说，C#和Java是差不多的，只不过C#的编译器帮助开发人员节省了许多工作。事实上，在这个简单的示例中其实还不能体现出yield的优越性，您可以想象一下，如果我们迭代器中需要有更多的逻辑，如while，if，for，break等配合使用，那么yield会带来多大的可读性。嗯，就说可读性，如果给您上面编写的Range类，您多久可以理解它的作用呢？再观察一下C#代码呢？&lt;/p&gt;

&lt;p&gt;可能您会说，完全可以在Range类中先创建一个数组，保存所有元素，然后在next()时一一返回，这可读性就强了。没错，只可惜这样做会占用太多内存。假如我传入-10&lt;sup&gt;8&lt;/sup&gt;和10&lt;sup&gt;8&lt;/sup&gt;作为参数，那程序十有八九会出现问题（顺便一问，创建这样一个数组需要占用多少内存？）。yield的一个重要特性便是“延迟”，便是“按需生成”。我们完全可以创建一个“范围”十分巨大——甚至是无限大的迭代器：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; Infinite(&lt;span style="color: blue"&gt;int &lt;/span&gt;start)
{
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) &lt;span style="color: blue"&gt;yield return &lt;/span&gt;start++;
}&lt;/pre&gt;

&lt;p&gt;然后只取其前N项使用：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// sum of 0 .. 99
&lt;/span&gt;&lt;span style="color: blue"&gt;int &lt;/span&gt;sum = Infinite(0).Take(100).Sum();&lt;/pre&gt;

&lt;p&gt;此外yield还有一些十分重要的使用模式，不过这方面话题我打算留在《&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html"&gt;Why Java Sucks and C# Rocks（6）：yield及其作用&lt;/a&gt;》一文中进行详细讨论了。&lt;/p&gt;

&lt;h1&gt;JavaScript中的迭代器&lt;/h1&gt;

&lt;p&gt;JavaScript中有迭代器吗？有迭代器的生成器（即yield类似功能）吗？有，还真有，您可以关注一下&lt;a href="https://developer.mozilla.org/en/New_in_JavaScript_1.7#Generators"&gt;MDN中的JavaScript 1.7说明&lt;/a&gt;，其中就有几乎和C#完全一样的功能，例如这样生成并使用一个无限的斐波那契数列：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;fib() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0, j = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        yield i;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;t = i;
        i = j;
        j += t;
    }
}

&lt;span style="color: blue"&gt;var &lt;/span&gt;g = fib();
&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; 10; i++) {
    document.write(g.next() + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;quot;&lt;/span&gt;);
}&lt;/pre&gt;

&lt;p&gt;只不过我们目前还没法编写这样的JavaScript代码，如果要使用迭代器，那么还得自己动手丰衣足食。&lt;/p&gt;

&lt;p&gt;其实如果不谈“生成器”，光“迭代器”本身是没有任何神秘之处的，它们只是一种定义好的“编程模型”罢了，例如Java里的Iterable和Iterator或是.NET里的IEnumerable和IEnumerator。因此，如果要说建立一个迭代器，那么其实只是确定一个编程模型：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;range(minInclusive, maxExclusive) {
    &lt;span style="color: #006400"&gt;// ...
&lt;/span&gt;}

&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;iter = range(0, 10); iter; iter = iter.next()) {
    document.write(iter.value + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;quot;&lt;/span&gt;);
}&lt;/pre&gt;

&lt;p&gt;range方法会返回一个迭代器，然后可以在一个for里进行使用。您会发现这个模型和.NET或是Java里的迭代器都不一样，它们在遍历过程中使用的都是同一个迭代器，而在我的模型中，一个迭代器其实更像是一个单向链表，它包含两个成员，value字段用于表示当前的值，next方法则返回下一个节点。这么做的原因是，我比较懒的写一些包含副作用的逻辑，因为维护状态实在是一件麻烦的事情，远不如创建并返回新对象来的简单。&lt;/p&gt;

&lt;p&gt;如果使用“创建数组”的方法来实现一个迭代器，那么range的确很简单：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// JavaScript&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;ArrayIterator(array, index) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;.value = array[index];
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._array = array;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._index = index;
}
ArrayIterator.prototype.next = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;newIndex = &lt;span style="color: blue"&gt;this&lt;/span&gt;._index + 1;
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(newIndex &amp;gt;= &lt;span style="color: blue"&gt;this&lt;/span&gt;._array.length) &lt;span style="color: blue"&gt;return null&lt;/span&gt;;
    &lt;span style="color: blue"&gt;return new &lt;/span&gt;ArrayIterator(&lt;span style="color: blue"&gt;this&lt;/span&gt;._array, newIndex);
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;range(minInclusive, maxExclusive) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;array = [];
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = minInclusive; i &amp;lt; maxExclusive; i++) {
        array.push(i);
    }

    &lt;span style="color: blue"&gt;return new &lt;/span&gt;ArrayIterator(array, 0);
}&lt;/pre&gt;

&lt;p&gt;这段代码您一定能够理解，我就不多做解释了。这种做法的优势在于十分容易实现，但是缺点也很明显，之前已经谈过就不再重复了。&lt;/p&gt;

&lt;h1&gt;题目要求&lt;/h1&gt;

&lt;p&gt;那么，我们又能否为JavaScript添加“生成器”？我的意思是，像yield那样的生成器。要求很简单，只要能用它实现最简单的功能即可，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; OneToThree()
{
    &lt;span style="color: blue"&gt;yield return&lt;/span&gt; 1;
    &lt;span style="color: blue"&gt;yield return&lt;/span&gt; 2;
    &lt;span style="color: blue"&gt;yield return&lt;/span&gt; 3;
}&lt;/pre&gt;

&lt;p&gt;如果您觉得实现这个非常简单，不妨再尝试着使用更好的做法来实现刚才的range函数。此外，您也可以实现如刚才C#的Infinite那样的无限数列，或者……那著名的斐波那契数列也可以咯。这样“求出斐波那契数列中，所有N以内的元素之和”便可以简单地：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function&lt;/span&gt; fibSeq() { &lt;span style="color:green;"&gt;/* ... */&lt;/span&gt; }

&lt;span style="color: blue"&gt;function&lt;/span&gt; sumOfFib(max) {
    &lt;span style="color: blue"&gt;return&lt;/span&gt; toIter(fibSeq()).takeWhile(&lt;span style="color: blue"&gt;function &lt;/span&gt;(i) { return i &amp;lt;= max}).sum();
}&lt;/pre&gt;

&lt;p&gt;如何，一起来尝试一下吧！&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html#comments</comments>
      <pubDate>Tue, 08 Jun 2010 07:33:11 GMT</pubDate>
      <lastBuildDate>Tue, 08 Jun 2010 07:33:11 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>浅谈Java 7的闭包与Lambda表达式之优劣</title>
      <link>http://blog.zhaojie.me/2010/06/first-version-of-lambda-and-closures-in-java-7.html</link>
      <guid>http://blog.zhaojie.me/2010/06/first-version-of-lambda-and-closures-in-java-7.html</guid>
      <description>&lt;p&gt;前几天&lt;a href="http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/"&gt;Oracle推出了Java 7官方的闭包与Lambda表达式的第一个实现&lt;/a&gt;，这基本上也是最终在正式版中的样式了。看了这个实现之后，我的第一感觉便是“丑”，当然不排除这是因为看惯了其他语言中实现的缘故。后来再仔细看了看又想了想，发现Java 7的实现也并非毫无可取之处，但似乎又感到某些做法上有一些问题。总之整个过程颇为有趣，决定将我的想法记录下来，希望可以吸引人来一起讨论一下。&lt;/p&gt;

&lt;h1&gt;Java 7中的Lambda表达式&lt;/h1&gt;

&lt;p&gt;Java 7中的Lambda表达式有两种形式，首先是第一种：&lt;/p&gt;

&lt;pre class="code"&gt;#&lt;span style="color: blue"&gt;int&lt;/span&gt;() func1 = #()(3); &lt;span style="color: green"&gt;// &amp;quot;func1.()&amp;quot; returns 3&lt;/span&gt;
#&lt;span style="color: blue"&gt;int&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;) func2 = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x)(x + 1); &lt;span style="color: green"&gt;// &amp;quot;func2.(3)&amp;quot; returns 4&lt;/span&gt;
#&lt;span style="color: blue"&gt;int&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;) func3 = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x, &lt;span style="color: blue"&gt;int&lt;/span&gt; y)(x - y); &lt;span style="color: green"&gt;// &amp;quot;func3.(5, 3)&amp;quot; returns 2&lt;/span&gt; &lt;/pre&gt;

&lt;p&gt;然后是第二种，含义与上面等价：&lt;/p&gt;

&lt;pre class="code"&gt;#&lt;span style="color: blue"&gt;int&lt;/span&gt;() func1 = #(){ &lt;span style="color: blue"&gt;return&lt;/span&gt; 3; };
#&lt;span style="color: blue"&gt;int&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;) func2 = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x){ &lt;span style="color: blue"&gt;return&lt;/span&gt; x + 1; };
#&lt;span style="color: blue"&gt;int&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;) func3 = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x, &lt;span style="color: blue"&gt;int&lt;/span&gt; y){ &lt;span style="color: blue"&gt;return&lt;/span&gt; x – y; };&lt;/pre&gt;

&lt;p&gt;如果Lambda的body是“单个表达式”的话，便可以使用“小括号”，并省去最后的return关键字；如果body中需要包含多条语句的话，则必须使用“大括号”，而大括号内部可以包含多条语句，就像一个普通的方法体一样。这两种写法在C#中也有对应物，如在“单个表达式”的情况下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func1 = () =&amp;gt; 3; &lt;span style="color: green"&gt;// &amp;quot;func1()&amp;quot; returns 3&lt;/span&gt;
&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func2 = x =&amp;gt; x + 1; &lt;span style="color: green"&gt;// &amp;quot;func2(3)&amp;quot; returns 4&lt;/span&gt; 
&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func3 = (x, y) =&amp;gt; x - y; &lt;span style="color: green"&gt;// &amp;quot;func3(5, 3)&amp;quot; returns 2&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;第二种，即多条语句：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func1 = () =&amp;gt; { &lt;span style="color: blue"&gt;return&lt;/span&gt; 3; };
&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func2 = x =&amp;gt; { &lt;span style="color: blue"&gt;return&lt;/span&gt; x + 1; };
&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func3 = (x, y) =&amp;gt; { &lt;span style="color: blue"&gt;return&lt;/span&gt; x – y; };&lt;/pre&gt;

&lt;p&gt;Java和C#的Lambda表达式都由两部分组成：“参数列表”和“表达式体”，但是它们有如下区别：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在Java中参数列表和表达式体之间没有分隔符号，而C#使用“=&amp;gt;”分隔。 &lt;/li&gt;

  &lt;li&gt;对于“单个表达式”的Lambda来说，C#可以无需使用括号包含表达式体，而Java必须使用小括号。 &lt;/li&gt;

  &lt;li&gt;如果只有单个参数，那么C#的参数列表可以省去小括号，而Java必须保留。 &lt;/li&gt;

  &lt;li&gt;C#对参数列表会进行“类型推断”，而Java必须写清参数类型。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些区别说大可大，说小可小，但是Java语言的设计的确让我感觉较C#为“丑”，这可能是个人主观因素，但我认为也不尽然。例如，如果我们需要对一个用户对象数组按照“年龄”进行排序，在C#里可以写作：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// C#&lt;/span&gt;
users.Sort(u =&amp;gt; u.Age);&lt;/pre&gt;

&lt;p&gt;而在Java中则必须写为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Arrays&lt;/span&gt;.sort(users, #(&lt;span style="color: #2b91af"&gt;User &lt;/span&gt;u)(u.Age));&lt;/pre&gt;

&lt;p&gt;这句C#代码语义清晰：按照“u的Age进行排序”，而在Java代码中便显得比较累赘，语义似乎也不够清晰。&lt;a href="http://blog.zhaojie.me/2010/04/trends-and-future-directions-in-programming-languages-by-anders-2-declarative-programming-and-dsl.html"&gt;Anders在设计C#语法的时候非常注重“声明式”代码&lt;/a&gt;，由此可见一斑。此外，我不明白为什么Java选择不对参数进行类型推断，在我看来这对于写出优雅代码十分重要（关于这点，在“&lt;a href="http://blog.zhaojie.me/2010/04/why-java-sucks-and-csharp-rocks-1-thoughts-and-goals.html"&gt;Why Java Sucks and C# Rocks&lt;/a&gt;”系列中会有更详细的讨论）。不过Java也不是没有“推断”，例如从上面的代码片断中可以得知，Java对于Lambda表达式的返回值还是进行了类型推断。事实上，Java还推断了“异常类型”，这点稍后会有更多讨论。&lt;/p&gt;

&lt;p&gt;当然，Java中可以“无中生有”地定义“匿名函数类型”（这点和VB.NET相对更为接近），而不需要像C#一样需要基于特定的“委托类型”，显得更为灵活。&lt;/p&gt;

&lt;h1&gt;SAM类型支持及闭包&lt;/h1&gt;

&lt;p&gt;SAM的全称是Single Abstract Method，如果一个类型为SAM类型，则意味着它 1) 是抽象类型（即接口或抽象类），且 2) 只有一个未实现的方法。例如这样一个Java接口便是个SAM类型：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface&lt;/span&gt; &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T, R&amp;gt; {
    R invoke(T arg);
}&lt;/pre&gt;

&lt;p&gt;于是我们便可以：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;[] array = &lt;span style="color: blue"&gt;new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;[10];
&lt;span style="color: blue"&gt;for&lt;/span&gt; (&lt;span style="color: blue"&gt;int&lt;/span&gt; i = 0; i &amp;lt; array.length; i++) {
    &lt;span style="color: blue"&gt;final int&lt;/span&gt; temp = i;
    array[i] = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x)(x + temp);
}&lt;/pre&gt;

&lt;p&gt;可见，我们使用Lambda表达式创建了Func接口的实例，这点是C#所不具备的。这点十分关键，因为在Java类库中已经有相当多的代码使用了SAM类型。不过我发现，在某些使用SAM的方式下似乎会产生一些“歧义”，例如这段代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class&lt;/span&gt; &lt;span style="color: #2b91af"&gt;MyClass&lt;/span&gt; {
    @Override
    &lt;span style="color: blue"&gt;public int &lt;/span&gt;hashCode() {
        &lt;span style="color: blue"&gt;throw new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;RuntimeException&lt;/span&gt;();
    }

    &lt;span style="color: blue"&gt;public void &lt;/span&gt;MyMethod() {
        &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x)(x * hashCode());
        &lt;span style="color: blue"&gt;int&lt;/span&gt; r = func.invoke(5); &lt;span style="color: green"&gt;// throw or not?&lt;/span&gt;
    }
}&lt;/pre&gt;

&lt;p&gt;在这里我们覆盖（override）了MyClass的hashCode方法，使它抛出RuntimeException，那么在调用MyMethod中定义的func1对象时会不会抛出异常？&lt;a href="http://hg.openjdk.java.net/lambda/lambda/langtools/file/7704dcd17e0b/test/tools/javac/lambda/LambdaScope01.java"&gt;答案是否定的&lt;/a&gt;，因为在这个Lambda表达式中，隐藏的“this引用”代表了func对象，调用它的hashCode不会抛出RuntimeException。那么，假如我们要调用MyClass的hashCode怎么办？那就稍微有些麻烦了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x)(x * &lt;span style="background-color: yellow"&gt;&lt;span style="color: #2b91af"&gt;MyClass&lt;/span&gt;.&lt;span style="color: blue"&gt;this&lt;/span&gt;&lt;/span&gt;.hashCode());&lt;/pre&gt;

&lt;p&gt;不过从&lt;a href="http://hg.openjdk.java.net/lambda/lambda/langtools/file/7704dcd17e0b/test/tools/javac/lambda/LambdaCapture01.java"&gt;另一段示例代码&lt;/a&gt;上看：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class&lt;/span&gt; &lt;span style="color: #2b91af"&gt;MyClass&lt;/span&gt; {

    &lt;span style="color: blue"&gt;public int&lt;/span&gt; n = 3;

    &lt;span style="color: blue"&gt;public void &lt;/span&gt;MyMethod() {
        &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; func = #(&lt;span style="color: blue"&gt;int&lt;/span&gt; x)(x + n);
        &lt;span style="color: blue"&gt;int&lt;/span&gt; r = func.invoke(5); &lt;span style="color: green"&gt;// 8&lt;/span&gt;
    }
}&lt;/pre&gt;

&lt;p&gt;由于Func对象上没有n，因此这里的n便是MyClass类里定义的n成员了。因此，Java的闭包并非不会捕获字面上下文里的成员，只是在SAM类型的情况下，字面范围内（lexical scope）成员的优先级会低于目标抽象类型的成员。&lt;/p&gt;

&lt;p&gt;总体来说，对于SAM类型的支持上，我认为Java是有可取之处的，只是我始终认为这个做法会产生歧义，因为我印象中其他语言里的Lambda表达式似乎都是捕获字面上下文的（当然它们可能也没有SAM支持）。但是，如何在“歧义”和“优雅”之间做出平衡，我一时也找不到令人满意的答案。&lt;/p&gt;

&lt;h1&gt;硬伤：Checked Exception&lt;/h1&gt;

&lt;p&gt;Java相当于其他常见语言有一个特别之处，那就是Checked Exception。Checked Exception意味着每个方法要标明自己会抛出哪些异常类型（RuntimeException及其子类除外），这也是方法契约的一部分，编译器会强制程序员写出满足异常契约的代码。例如某个类库中定义了这样一个方法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public void&lt;/span&gt; myMethod() &lt;span style="color: blue"&gt;throws&lt;/span&gt; &lt;span style="color: #2b91af"&gt;AException&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;BException&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;其中throws后面标注的便是myMethod可能会抛出的异常。于是如果我们要写一个方法去调用myMethod，则可能是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public void&lt;/span&gt; myMethodCaller() &lt;span style="color: blue"&gt;throws&lt;/span&gt; &lt;span style="color: #2b91af"&gt;AException&lt;/span&gt; {
    &lt;span style="color: blue"&gt;try&lt;/span&gt; {
        myMethod();
    } &lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;BException&lt;/span&gt; ex) {
        &lt;span style="color: blue"&gt;throw new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;AException&lt;/span&gt;(ex);
    }
}&lt;/pre&gt;

&lt;p&gt;当我们写一个方法A去调用方法B时，我们要么在方法A中使用try...catch捕获B抛出的方法，要么在方法A的签名中标记“会抛出同样的异常”。如上面的myMethodCaller方法，便在内部处理了BException异常，而只会对外抛出AException。Java便使用这种方法严格限制了类库的异常信息。&lt;/p&gt;

&lt;p&gt;Checked Exception是一个有争议的特性。它对于编写出高质量的代码非常重要，因为在哪些情况抛出异常其实都是方法契约的一部分（不仅仅是签名或返回值的问题），应该严格遵守，在类库升级时也不能破坏，否则便会产生兼容性的问题。例如，您关注MSDN里的文档时，就会看到异常的描述信息，只不过这是靠“文档”记录的，而Java则是强制在代码中的；但是，从另一个角度说，Checked Exception让代码编写变得非常麻烦，这导致的一个情况便是许多人在写代码时，自定义的异常全都是RuntimeException（因为不需要标记），每个方法也都是throws Exception的（这样代码中就不需要try...catch了），此时Checked Exception特性也基本形同虚设，除了造成麻烦以外几乎没有带来任何好处。&lt;/p&gt;

&lt;p&gt;我之前常说：一个特性如果要被人广泛接受，那它一定要足够好用。现在如Scala和Grovvy等为Java设计的语言中都放弃了Checked Exception，这也算是从侧面印证了Checked Exception的尴尬境地吧。&lt;/p&gt;

&lt;p&gt;而Checked Exception对于如今Lambda或闭包来说，在我看来更像是一种硬伤。为什么这么说？举个例子吧，假如有这么一个map方法，可以把一个数组映射成另一个类型数组：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;R[] map(T[] array, &lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;T, R&amp;gt; mapper) { ... }&lt;/pre&gt;

&lt;p&gt;好，那么比如这样一个需求：给定一个字符串数组，保存着文件名，要求获得它的&lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/File.html#getCanonicalPath()"&gt;标准路径&lt;/a&gt;。从表面上看来，我们可以这样写：&lt;/p&gt;

&lt;pre class="code"&gt;map(files, #(&lt;span style="color: #2b91af"&gt;String&lt;/span&gt; f)(&lt;span style="color: blue"&gt;new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;(f).getCanonicalPath())&lt;/pre&gt;

&lt;p&gt;但事实上，这么做无法编译通过。为什么？因为getCanonicalPath方法会抛出IOException，我们在调用时必须显式地使用try...catch进行处理。那么这段代码该怎么写？还真没法写。如果没有Checked Exception的话（如C#），我们还可以这么做（处理第一个抛出的IOException）：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;try&lt;/span&gt; {
    map(files, #(&lt;span style="color: #2b91af"&gt;String&lt;/span&gt; f)(&lt;span style="color: blue"&gt;new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;(f).getCanonicalPath())
&lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;IOException&lt;/span&gt; ex) {
    ...
}&lt;/pre&gt;

&lt;p&gt;但是，如果我们要写出之前那种“漂亮”的写法，就不能使用Func&amp;lt;T, R&amp;gt;而必须是这样的接口类型：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface&lt;/span&gt; &lt;span style="color: #2b91af"&gt;FuncThrowsIOException&lt;/span&gt;&amp;lt;T, R&amp;gt; {
    R invoke(T arg) &lt;span style="color: blue"&gt;throws&lt;/span&gt; &lt;span style="color: #2b91af"&gt;IOException&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;或者是这样的“匿名函数类型”：&lt;/p&gt;

&lt;pre class="code"&gt;#&lt;span style="color: #2b91af"&gt;String&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;String&lt;/span&gt;)(&lt;span style="color: blue"&gt;throws&lt;/span&gt; &lt;span style="color: #2b91af"&gt;IOException&lt;/span&gt;) &lt;span style="color: green"&gt;// toCanonicalPath = #(String f)(new File(f).getCanonicalPath())&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;但是，作为Lambda和闭包的常用场景，如map，filter，fold等“函数式”元素，是不可能为某种特定的“异常类型”而设计的——异常类型千变万化，难道这也要用throws Exception来进行“统一处理”吗？Java虽然已经支持对异常类型的“推断”，但Checked Exception还是对Lambda和闭包的适用性造成了很大影响。&lt;/p&gt;

&lt;p&gt;因此，我认为Checked Exception是一个“硬伤”。&lt;/p&gt;

&lt;h1&gt;其他&lt;/h1&gt;

&lt;p&gt;Java的Lambda和闭包还有一些特性，例如参数的“泛化”：&lt;/p&gt;

&lt;pre class="code"&gt;#&lt;span style="color: blue"&gt;boolean&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;) f = #(&lt;span style="color: #2b91af"&gt;Number &lt;/span&gt;n)(n.intValue() &amp;gt; 0);&lt;/pre&gt;

&lt;p&gt;由于Number是Integer的基类，因此我们可以使用Number来构造一个接受Integer参数的匿名函数类型。由于示例较少，我还不清楚这个特性的具体使用场景和意义所在——不过我猜想，在Java中可能允许这样做吧：&lt;/p&gt;

&lt;pre class="code"&gt;#&lt;span style="color: blue"&gt;boolean&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Number&lt;/span&gt;) f = #(&lt;span style="color: #2b91af"&gt;Number &lt;/span&gt;n)(n.intValue() &amp;gt; 0);
#&lt;span style="color: blue"&gt;boolean&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Integer&lt;/span&gt;) f1 = f; &lt;span style="color: green"&gt;// cast implicitly or explicitly&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;此外还有一些特性，例如与MethodHandle类型的转化，我就没有特别的看法了。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/first-version-of-lambda-and-closures-in-java-7.html#comments</comments>
      <pubDate>Sun, 06 Jun 2010 07:31:37 GMT</pubDate>
      <lastBuildDate>Sun, 06 Jun 2010 07:31:37 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/translation/">翻译引进</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>编程语言的发展趋势及未来方向（7）：总结</title>
      <link>http://blog.zhaojie.me/2010/06/trends-and-future-directions-in-programming-languages-by-anders-7-conclusion.html</link>
      <guid>http://blog.zhaojie.me/2010/06/trends-and-future-directions-in-programming-languages-by-anders-7-conclusion.html</guid>
      <description>&lt;p&gt;这是&lt;a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg"&gt;Anders Hejlsberg&lt;/a&gt;（不用介绍这是谁了吧）在&lt;a href="http://channel9.msdn.com/posts/adebruyn/TechDays-2010-Developer-Keynote-by-Anders-Hejlsberg/"&gt;比利时TechDays 2010所做的开场演讲&lt;/a&gt;。由于最近我在博客上关于语言的讨论比较多，出于应景，也打算将Anders的演讲完整地听写出来。在上一部分中，Anders谈论了“并发”，这也是他眼中编程语言发展的三种趋势之一，并演示了.NET 4.0中并行库的神奇效果。现在则是此次演讲的最后一部分，Anders对整场演讲内容进行了总结和回顾（本文较短，主要内容请参考之前的文章）。&lt;/p&gt;

&lt;p&gt;如果没有特别说明，所有的文字都直接翻译自Anders的演讲，并使用我自己的口语习惯表达出来，对于Anders的口误及反复等情况，必要时在译文中自然也会进行忽略。为了方便理解，我也会将视频中关键部分进行截图，而某些代码演示则会直接作为文章内容发表。 &lt;/p&gt;

&lt;p&gt;（听写开始，接&lt;a href="http://blog.zhaojie.me/2010/05/trends-and-future-directions-in-programming-languages-by-anders-6-concurrency.html"&gt;上篇&lt;/a&gt;）&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/prog-lang-trends-anders/49.png" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/prog-lang-trends-anders/49-thumbnail.png" /&gt;&lt;/a&gt; 

&lt;p&gt;OK，我想现在已经讲的差不多了，我来做个总结吧。&lt;/p&gt;

&lt;p&gt;在我看来，对于编程语言来说，现在出现了许多有趣的东西，也是令人激动的时刻。在过去，大约1995-2005年，的确可以说是一个有些特别的编程语言的黄金时期。你知道，当Java出现的时候，编程语言的门槛变得平坦了，一切都是Java，天啊其他编程语言都完蛋了，我们也没什么可做的了。然后我们又逐渐发现，这远没有结束，现在回顾起来，会发现又出现了许多有趣的编程语言。我很兴奋，因为新语言代表了我们在编程领域上的进步。 &lt;/p&gt;

&lt;p&gt;如果要我概括在未来十年编程语言会变成什么样，首先，我认为编程语言应该变得更加“声明式”，我们需要设法为语言引入一些如元编程，函数式编程的能力，同时可能也要寻找让用户有办法扩展语法，使他们可以构造领域特定语言等等。我想在十年以后，动态语言和静态语言的区别也差不多会消失了，这两者会合并为一种单一的常见的编程范式。在并发方面，语言会采纳一些特性，可以利用起隔离性，函数式的纯粹性，以及更好的不可变数据类型的编写方式。不过总体来说我想强调的是，对于编程语言，新的范式则是“多范式”编程语言。 &lt;/p&gt;

&lt;p&gt;这就是我现在对编程语言的看法，希望我没有给你带来过多内容。如果你对C# 4.0有更多兴趣，今天下午一点我还有一个讲座。多谢捧场，希望你对这次会议感到满意。谢谢。&lt;/p&gt;

&lt;p&gt;（全场演讲到此结束）&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/trends-and-future-directions-in-programming-languages-by-anders-1-history-and-trends.html"&gt;编程语言的发展趋势及未来方向（1）：历史回顾及趋势概述&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/04/trends-and-future-directions-in-programming-languages-by-anders-2-declarative-programming-and-dsl.html"&gt;编程语言的发展趋势及未来方向（2）：声明式编程与DSL&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/trends-and-future-directions-in-programming-languages-by-anders-3-functional-programming-and-fsharp.html"&gt;编程语言的发展趋势及未来方向（3）：函数式编程&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/trends-and-future-directions-in-programming-languages-by-anders-4-dynamic-languages.html"&gt;编程语言的发展趋势及未来方向（4）：动态语言&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/trends-and-future-directions-in-programming-languages-by-anders-5-meta-programming.html"&gt;编程语言的发展趋势及未来方向（5）：元编程&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/05/trends-and-future-directions-in-programming-languages-by-anders-6-concurrency.html"&gt;编程语言的发展趋势及未来方向（6）：并发&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;编程语言的发展趋势及未来方向（7）：总结&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/06/trends-and-future-directions-in-programming-languages-by-anders-7-conclusion.html#comments</comments>
      <pubDate>Sat, 05 Jun 2010 07:53:13 GMT</pubDate>
      <lastBuildDate>Sat, 05 Jun 2010 07:53:13 GMT</lastBuildDate>
    </item>
  </channel>
</rss>
