﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-麒麟.NET</title><link>http://www.cnblogs.com/kirinboy/</link><description>一半是Java，一半是.NET</description><language>zh-cn</language><lastBuildDate>Mon, 06 Jul 2009 11:14:37 GMT</lastBuildDate><pubDate>Mon, 06 Jul 2009 11:14:37 GMT</pubDate><ttl>60</ttl><item><title>不一样的多态</title><link>http://www.cnblogs.com/kirinboy/archive/2009/07/06/unusual-polymorphism.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 06 Jul 2009 07:56:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/07/06/unusual-polymorphism.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1517772.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/07/06/unusual-polymorphism.html#Feedback</comments><slash:comments>20</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1517772.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1517772.html</trackback:ping><description><![CDATA[<h1>一样的多态</h1>
<p>多态（Polymorphism）一词源于生物学，顾名思义就是指多种形态。在面向对象世界里，多态与继承和封装一起构成了三大核心概念。</p>
<p>在.NET中，多态通常意味着子类对于父类一种衍变。子类继承自父类，拥有父类所定义的一切（public或protected）成员。但同时，它又可以修改（重写或复写）这些成员，使其实现与父类以及其他子类完全不同。我们可以说，<span style="color: #0000a0;"><strong>继承体现了类的多态性</strong></span>。</p>
<p>大家应该很熟悉Duck的例子了吧？</p>
<pre class="code"><span style="background: #eeefe6; color: blue">public abstract class </span><span style="background: #eeefe6; color: #2b91af">Duck
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">public abstract void </span><span style="background: #eeefe6">Quack();
}

</span><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">MallardDuck </span><span style="background: #eeefe6">: </span><span style="background: #eeefe6; color: #2b91af">Duck
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">public override void </span><span style="background: #eeefe6">Quack()
    {
        </span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.WriteLine(</span><span style="background: #eeefe6; color: #a31515">"Quack, quack, quack..."</span><span style="background: #eeefe6">);
    }
}

</span><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">RubberDuck </span><span style="background: #eeefe6">: </span><span style="background: #eeefe6; color: #2b91af">Duck
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">public override void </span><span style="background: #eeefe6">Quack()
    {
        </span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.WriteLine(</span><span style="background: #eeefe6; color: #a31515">"Squeak, squeak, squeak..."</span><span style="background: #eeefe6">);
    }
}

</span><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">Program
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">public static void </span><span style="background: #eeefe6">Main()
    {
        </span><span style="background: #eeefe6; color: #2b91af">Duck </span><span style="background: #eeefe6">duck = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">MallardDuck</span><span style="background: #eeefe6">();
        duck.Quack();
        duck = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RubberDuck</span><span style="background: #eeefe6">();
        duck.Quack();
        </span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.ReadLine();
    }
}</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>MallardDuck和RubberDuck虽然都继承自抽象类Duck，同样拥有Quack()方法，但它们却有不同的实现，产生不同的结果。在声明Duck类型时，既可以实例化为Mallard，也可以实例化为RubberDuck，或者是其他继承自Duck的类，在运行时，将自动调用各个子类的实现。</p>
<p>多态的这些特性使依赖注入和面向抽象编程成为可能，其重要性不言而喻。</p>
<h1>不一样的多态</h1>
<p>然而，既然多态是指同一类事物之间的不同形态，那么我们为什么要把对于多态的理解局限于类的继承关系呢？在.NET中是否还存在着非继承关系的多态性呢？</p>
<p><span style="color: #0000a0;"><strong>泛型体现了参数的多态性</strong></span></p>
<p>类型参数在泛型中通常解释为占位符，而我更愿意将其理解为对参数的一种抽象。以最常见的List&lt;T&gt;为例，List&lt;string&gt;和List&lt;int&gt;在语法上完全相同，仅仅是类型参数有所不同，然而它们却是两个完全不同的类。也就是说，是类型参数的不同，导致了不同的类的形态。</p>
<pre class="code"><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">MyList</span><span style="background: #eeefe6">&lt;T&gt;
{
    </span><span style="background: #eeefe6; color: blue">private </span><span style="background: #eeefe6">T[] items;
    </span><span style="background: #eeefe6; color: blue">private int </span><span style="background: #eeefe6">size;
    </span><span style="background: #eeefe6; color: blue">public void </span><span style="background: #eeefe6">Add(T item)
    {
        </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(size == items.Length)
        {
            </span><span style="background: #eeefe6; color: green">// modify capacity
        </span><span style="background: #eeefe6">}
        items[size++] = item;
    }
}</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>如果我们使用MyList&lt;string&gt;，在内部就会声明一个字符串数组，Add方法的参数也必须为string。如果使用MyList&lt;int&gt;，在内部就会声明一个int数组，Add方法的参数也必须为int。这看上去就像是T是string和int的&ldquo;基类&rdquo;，在使用MyList&lt;T&gt;时（相当于客户端代码），T既可以是string也可以是int，或者是其他符合约束的类型，但在设计时，我们对这一切毫无所知。</p>
<p>您是否也觉得这是多态性的一种体现呢？</p>
<p>再来看看十分经典的Swap&lt;T&gt;的例子。</p>
<pre class="code"><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">Swapper
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">private static void </span><span style="background: #eeefe6">Swap&lt;T&gt;(</span><span style="background: #eeefe6; color: blue">ref </span><span style="background: #eeefe6">T o1, </span><span style="background: #eeefe6; color: blue">ref </span><span style="background: #eeefe6">T o2)
    {
        T temp = o1;
        o1 = o2;
        o2 = temp;
    }
}</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>Swap&lt;T&gt;泛型方法就像是封装了N个非泛型的Swap方法，如Swap(ref int o1, ref int o2)、Swap(ref string o1, ref string o2)等等。在类型推断特性的支持下，您甚至可以像使用非泛型方法一样来使用泛型方法。参数T在某种程度上体现了不同的参数形态，因此我们有理由认为，泛型类型T体现了参数的多态性。</p>
<p><strong><span style="color: #0000a0;">委托体现了方法的多态性</span></strong></p>
<p>委托是对拥有相同参数和返回值的所有方法的封装。只要方法拥有同样的参数列表和返回值，委托都认为它们属于同一&ldquo;类型&rdquo;的方法，可以添加到同一个委托链表中。</p>
<pre class="code"><span style="background: #eeefe6; color: blue">public delegate void </span><span style="background: #eeefe6; color: #2b91af">FooDelegate</span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: #2b91af">List</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">&gt; list, </span><span style="background: #eeefe6; color: blue">string </span><span style="background: #eeefe6">str);

</span><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">DelegateTest
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">public void </span><span style="background: #eeefe6">AddToList(</span><span style="background: #eeefe6; color: #2b91af">List</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">&gt; list, </span><span style="background: #eeefe6; color: blue">string </span><span style="background: #eeefe6">strToAdd)
    {
        list.Add(strToAdd);
    }

    </span><span style="background: #eeefe6; color: blue">public static void </span><span style="background: #eeefe6">PrintIfContains(</span><span style="background: #eeefe6; color: #2b91af">List</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">&gt; list, </span><span style="background: #eeefe6; color: blue">string </span><span style="background: #eeefe6">strToCheck)
    {
        </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(list.Contains(strToCheck))
            </span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.WriteLine(</span><span style="background: #eeefe6; color: #a31515">"The list contains " </span><span style="background: #eeefe6">+ strToCheck);
    }
}

</span><span style="background: #eeefe6; color: blue">public class </span><span style="background: #eeefe6; color: #2b91af">Program
</span><span style="background: #eeefe6">{
    </span><span style="background: #eeefe6; color: blue">public static void </span><span style="background: #eeefe6">Main()
    {
        </span><span style="background: #eeefe6; color: #2b91af">List</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">&gt; list = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">List</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">&gt;();
        list.Add(</span><span style="background: #eeefe6; color: #a31515">"Kirin"</span><span style="background: #eeefe6">);
        </span><span style="background: #eeefe6; color: #2b91af">DelegateTest </span><span style="background: #eeefe6">delegateTest = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">DelegateTest</span><span style="background: #eeefe6">();
        </span><span style="background: #eeefe6; color: #2b91af">FooDelegate </span><span style="background: #eeefe6">fooDelegate = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">FooDelegate</span><span style="background: #eeefe6">(delegateTest.AddToList);
        fooDelegate += </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">FooDelegate</span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: #2b91af">DelegateTest</span><span style="background: #eeefe6">.PrintIfContains);
        fooDelegate(list, </span><span style="background: #eeefe6; color: #a31515">"麒麟.NET"</span><span style="background: #eeefe6">);
        </span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.ReadLine();
    }
}</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>在上例中，FooDelegate委托封装了参数为List&lt;string&gt;和string，并且没有返回值的方法。任何符合上述约束的方法，在FooDelegate中一视同仁。如，AddToList实例方法与PrintIfContains静态方法除了参数列表与返回值相同外，内部实现完全不同，但是它们却可以添加到同一个委托链表中。也就是说，同一个委托，可以定义并调用不同的方法（约束相同而实现不同）。</p>
<p>您是否也认为这是方法的多态性的一种体现呢？</p>
<h1>小结</h1>
<p>我们通常所讨论的多态，就是指子类对父类方法的重写（虚方法）或覆盖（非虚方法），这样的理解未免过于狭隘。.NET强大的特性能够实现其他语言中无法实现的多态性。如本文所列举的泛型和委托。您是否认同笔者的观点呢？如果认同，您是否还能举出其他的例子呢？</p>
<p>您可能觉得本文缺乏技术含量。的确是的，本文并不包含多么高深的技术和思想，而只是笔者最近学习过程中一些琐碎的领悟。如果能够帮助到您，将是笔者的荣幸。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1517772.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48008/" target="_blank">IBM发布全球首款开源智能编译器</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>Eclipse &amp;amp; Visual Studio Tips（不断更新）</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/22/Eclipse-and-VisualStudio-tips.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 22 Jun 2009 08:57:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/22/Eclipse-and-VisualStudio-tips.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1508394.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/22/Eclipse-and-VisualStudio-tips.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1508394.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1508394.html</trackback:ping><description><![CDATA[<p>习惯了Visual Studio（以下简称VS）的.NET程序员初用Eclipse时肯定会感到很不适应。我就是如此，觉得Eclipse实在是一款很垃圾的IDE。当然这种想法只是在恶心得我想砸电脑的时候一闪而过。Eclipse有Eclipse的优势，它免费、开源、可扩展性强，它的插件可以解决很多问题。</p>
<p>本文主要介绍VS中的常用功能（笔者常用的）在Eclipse下如何实现，希望对转战到Eclipse的原.NET程序员有所帮助。</p>
<p><strong>1.自动换行</strong></p>
<p>我相信很多VS使用者并不常用自动换行功能，而是通过手工输入回车的方式进行换行，这样的代码才够清晰。但本人实在是习惯了VS中的自动换行。因为并不是所有的程序员都会通过手工回车换行，而且即使手工换行了，不同分辨率的桌面看到的代码也可能是迥异的。这往往会导致令我深恶痛绝的横向滚动条。因此在我的VS里，必然选中自动换行并显示自动换行符号（Tools&mdash;&gt;Options&mdash;&gt;Text Editor&mdash;&gt;All Languages&mdash;&gt;Word wrap和Show visual glyphs for word wrap）。</p>
<p>令人失望的是，Eclipse中没有相应的功能，但我们可以通过格式化代码来实现。Window&mdash;&gt;Preferences&mdash;&gt;Java&mdash;&gt;Code Style&mdash;&gt;Formatter&mdash;&gt;点击Show按钮&mdash;&gt;Line Wapping选项卡&mdash;&gt;在Maximum line width中设置每行的最大行宽，然后用Ctrl+Shift+F来实现自动换行。</p>
<p>但是这样的自动换行方式会修改源文档，当您只想阅读代码而不想修改时，这样的方式必然会带来麻烦。</p>
<p><strong>2.Open Declaration</strong></p>
<p>我们在阅读代码时，最常使用的一个IDE功能就是跳转到当前成员（包括参数、字段、属性、方法等）或类（还包括结构、枚举、委托等）的定义处。在VS中，我们可以右键点击该成员或类，然后选择&ldquo;Go To Definition&rdquo;。在Eclipse中，我们可以右键点击成员或类，然后选择&ldquo;Open Declaration&rdquo;（快捷键F3）。此外，还可以选择&ldquo;Open Type Hierarchy&rdquo;（快捷键F4），查看类型的层次结构。</p>
<p><strong>3.References</strong></p>
<p>与&ldquo;Go To Definition&rdquo;相对应的，在VS中我们可以选择右键菜单中的&ldquo;Find All References&rdquo;来查找当前成员的所有引用。在Eclipse中，我们需要选中一个成员或类，选择右键菜单中的&ldquo;References&rdquo;菜单，然后选择一个查找级别（Workspace、Project或Hierarchy），查找的结果将显示在&ldquo;Search&rdquo;视图中。</p>
<p>然而Eclipse查找出的所有引用并不能显示具体的语句，而只能具体到方法名称。这给查找带来了一定的不便。比如我们想在解决方案中查找哪些类实现了IFoo这个接口，在Eclipse中查找出来的是所用引用了IFoo的地方，而在VS中我们可以一目了然地发现那些语句使用了&ldquo;: IFoo&rdquo;。</p>
<p><strong>4. Search</strong> </p>
<p>在编程时使用Ctrl+F查找某个关键字是不可避免的。VS中的Ctrl+F功能十分强大，它甚至允许我们选择查找的范围，如当前文档，还是整个解决方案。初用Eclipse时，会发现其Ctrl+F十分低能，只能检索当前文档的内容。其实，我们在Eclipse真正常用的不是Ctrl+F的&ldquo;Find/Replace&rdquo;，而是&ldquo;Search&rdquo;。</p>
<p>在Eclipse中点击&ldquo;Search&rdquo;菜单，选择&ldquo;Search&rdquo;选项（快捷键Ctrl+G），将打开Search界面。如下图所示：</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseTips_EEB6/image_2.png"><img border="0" width="594" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseTips_EEB6/image_thumb.png" alt="image" height="387" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" title="image" /></a> </p>
<p>你会发现其强大之处：通配符、搜索的类型、限制等等。你马上会联想到前面所说的Open Declarations和References都是实际上都是Search的一部分。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1508394.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48007/" target="_blank">IE颓势不减 微软下月公布最新浏览器架构</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>一本绝对值得期待的书：CLR via C#, 3rd Edition</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/18/1505604.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Thu, 18 Jun 2009 02:36:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/18/1505604.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1505604.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/18/1505604.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1505604.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1505604.html</trackback:ping><description><![CDATA[<p>Jeffrey Richter在其<a href="http://www.wintellect.com/CS/blogs/jeffreyr/archive/2009/06/17/clr-via-c-3rd-edition.aspx">最新的博文中</a>透露了已经开始编写CLR via C#, 3rd Edition。这本书基于.NET 4.0，涵盖CLR 4.0相对于2.0的新特性和改进，并且还包括C# 3.0和4.0的内容。该书有望在.NET 4.0发布的时候同步上市。</p>
<p>相信与本书前两个版本一样，第三版也是绝对值得期待的经典之作。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1505604.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48002/" target="_blank">竞争日趋激烈 微软欲借 Windows 7 扭转战局</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>深入浅出Eclipse RCP（2）：RCP简介</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/16/HeadFirstEclipseRcp2.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Tue, 16 Jun 2009 07:54:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/16/HeadFirstEclipseRcp2.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1504395.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/16/HeadFirstEclipseRcp2.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1504395.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1504395.html</trackback:ping><description><![CDATA[<p><a href="/kirinboy/archive/2009/05/25/HeadFirstEclipseRcp1.html">上一篇随笔</a>我们用Hello RCP模板创建了一个简单的RCP应用，并介绍了系统自动生成的5个类 。本文将重点介绍RCP的架构并对其中的一些概念进行阐述。</p>
<h1></h1>
<h1>Eclipse体系结构</h1>
<p>Eclipse的体系结构图如下。 </p>
<p><img width="273" src="http://www.uml.org.cn/j2ee/images/2006529114659189.jpg" height="217" /></p>
<p>Platform Runtime是整个架构的核心，其他部分实质上都属于插件范畴。Eclipse采用动态加载机制，即只有需要的才加载，这样可以降低启动时间，提高资源使用效率。</p>
<p>Workspace主要负责管理用户资源，如创建项目的管理、文件变更等，并通知其他插件关于资源的变更信息。</p>
<p>Workbench是Eclipse提供的用户界面，它使用SWT（标准窗口工具包）和JFace（建立在SWT之上的UI部件）构建。</p>
<p>Help允许以HTML文件形式添加文档，并提供了一个附加的导航结构以便用户使用帮助功能搜索相关信息。</p>
<p>Team负责提供版本控制和配置管理支持。它根据需要添加视图，以允许用户与所有使用的任何版本控制系统交互。大多数插件都不需要与Team交互，除非它们提供版本控制服务。</p>
<p>JDT提供了用于编辑、查看、编译、调试和运行Java代码的专门插件。PDE构建于Eclipse平台和JDT之上，提供了专门开发插件的工具。 </p>
<h1>什么是RCP</h1>
<p>RCP摆脱了失败的AWT和Swing，是一套基于SWT和JFace的图形界面引擎。RCP在本质上是Eclipse插件，它提供丰富的界面控件，使得基于 Java 开发桌面应用也变得容易了很多。 </p>
<h1>概念解析</h1>
<p>上一篇随笔中提到了很多RCP概念，如Workbench、WorkbenchWindow、Advisor等，它们具体是干什么的呢？下面我将试着对这些概念进行解释。</p>
<h1></h1>
<p><b>Display</b> </p>
<p>应用程序一般只需要一个Display对象，该对象实际上是一个SWT对象，代表了底层图形系统的实现。Display的主要任务是事件处理，它负责从操作系统的事件队列中读取事件，传递给RCP的事件监听器以便完成具体的任务。需要注意的是，Display对象不代表应用程序窗口的任何可视控件，在应用程序主窗口打开之前，Display是不可获得的。应用程序主窗口打开之后，才可以通过一个被称作Shell的对象获得。Display对象的获得方法如下。 </p>
<p>Display display = new Display(); </p>
<p>或者：Display display=Display.getCurrent(); </p>
<p><b>Shell</b> </p>
<p>每一个窗口都有一个Shell对象。Shell对象代表了与用户交互的窗口框架，并处理与窗口关联的诸如移动、改变大小等常见行为。Shell对象的获得方法： </p>
<p>Display display=Display.getCurrent(); </p>
<p>Shell shell = new Shell(display); </p>
<p>或者：Shell shell=Display.getCurrent().getActiveShell(); </p>
<p><b>Workbench</b> </p>
<p>Workbench是工作台，通俗地说就是代表用户界面的UI元素。工作台上有各种窗口、图标、按钮和控件，用户可以在工作台上做各种操作。获得工作台对象的方法： </p>
<p>IWorkbench wb=PlatformUI.getWorkbench(); </p>
<p>工作台对象有两个方法也许很有用： </p>
<p>&nbsp;&nbsp;&nbsp; wb.restart()，关闭应用程序并立即重新启动。 </p>
<p>&nbsp;&nbsp;&nbsp; wb.close()，正常关闭应用程序，等同于ActionFactory.QUIT_ACTION。</p>
<p><b>Advisor</b> </p>
<p>Advisor是建议者，用户主界面宽度、高度、图标、菜单、工具栏、颜色、操作等等配置方案需要Advisor来提出&ldquo;建议&rdquo;。Eclipse提供了3种类型的Advisor： </p>
<p>&nbsp;&nbsp;&nbsp; 1. WorkbenchAdvisor。应用程序级别，Eclipse3.0开始引入的全新类。每一个应用程序只有一个 Workbench，WorkbenchAdvisor负责该工作台生命周期的管理，例如启动、关闭工作。WorkbenchAdvisor也负责该 Workbench的异常处理，并负责向Workbench提供一些重要参数，例如可以指定该Workbench的初始透视图。 </p>
<p>&nbsp;&nbsp;&nbsp; 2. WorkbenchWindowAdvisor。窗口级别。每一个窗口都有一个WorkbenchWindow- Advisor 实例。WorkbenchWindowAdvisor 负责具体窗口生命周期的管理，例如状态栏、工具栏、菜单、窗口标题、窗口大小和各种控件等等，也可以处理窗口的各种事件例程。 </p>
<p>&nbsp;&nbsp;&nbsp; 3. ActionBarAdvisor。窗口级别。每一个窗口都有一个ActionBarAdvisor实例。ActionBarAdvisor负责管理窗口的菜单栏、状态栏、工具栏的外观和行为。</p>
<p><b>IWorkbenchSite</b> </p>
<p>workbench及其part，包括part中的page，的通用接口。 </p>
<p><b>IWorkbenchPartSite</b>：workbench和workbench part之间主要的接口，不直接实现。 </p>
<p><b>PartSite</b>：实现IWorkbenchPartSite。PartSite的生命周期： </p>
<blockquote>
<p>1. 构建site。 </p>
<p>2. 构建part并保存在part中？？ </p>
<p>3. site调用part.init()。 </p>
<p>4. 构建pane并保存在site中。 </p>
<p>5. 构建part的action bar并保存在site中。 </p>
<p>6. pane添加到presentation中。 </p>
<p>7. 创建pane和part的SWT widget。 </p>
<p>8. 激活site，使actions可见。</p>
</blockquote>
<p><b></b></p>
<p><b>IWorkbench</b>： </p>
<p>Workbench是Eclipse用户界面中最底层的对象，它建立在Display之上，包含1个或多个IWorkbenchWindow，用于向最终用户呈现信息。当你的workbench插件被激活的时候，eclipse平台将为之创建一个实例，在平台的整个生命周期中，只允许出现该workbench的唯一实例 </p>
<p><b>IWorkbenchWindow</b>： </p>
<p>上面getActiveWorkbenchWindow()得到的就是IWorkbenchWindow。IWorkbench利用其包含的IWorkbenchWindow向终端用户展现信息。workbench window是workbench中最顶级的window。包括菜单栏、工具栏、状态栏以及一个用来显示IWorkbenchPage的主区域。每个IWorkbenchWindow包含0个或多个IWorkbenchPage，但是同一时刻只允许一个IWorkbenchPage被激活并向最终用户显示。 </p>
<p><b>IWorkbenchPage</b> </p>
<p>IWorkbenchPage的功能就是组织一个或多个IWorkbenchPart（IEditorPart或IViewPart）显示在一个IWorkbenchWindow中,最终呈现给最终用户。IWorkbenchPage包含0个或多个IEditorPart或IViewPart，这些IEditorPart和IViewPart完全被包含在这个IWorkbenchPage并且不能与其它的IWorkbenchPage共享。IWorkbenchPage上定义的Layout和ActionSet就是我们常说的透视图&mdash;&mdash;perspective了。 </p>
<p><b>WorkbenchPage</b>：workbench中view和editor的集合。 </p>
<p>&nbsp;&nbsp;&nbsp; IWorkbenchPage.findView(viewName)可以得到IViewPart。 </p>
<p>&nbsp;&nbsp;&nbsp; IWorkbenchPage.findEditor(IEditorInput)可以得到IEditorPart。</p>
<p>获取WorkbenchPage的方法： </p>
<p>&nbsp;&nbsp;&nbsp; ViewPart.getViewSite().getPage()。（getViewSite()得到的是IWorkbenchPartSite） </p>
<p>&nbsp;&nbsp;&nbsp; getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage()。 </p>
<p>&nbsp;&nbsp;&nbsp; PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()。</p>
<p><b>IWorkbenchPart</b> </p>
<p>WorkbenchPart是所有workbench parts的基类，如ViewPart和EditPart。不直接继承。 </p>
<p>IWorkbenchPart就是上面提到的IEditorPart和IViewPart，它也只包含这两个子类型。 </p>
<p>IViewPart常常被用于导航信息的层次结构,例如我们的workspace,或者用来打开一个编辑器,或者用来显示这个编辑器的某些属性 </p>
<p>IEditorPart多被用来编辑或浏览一个文档或一个输入的对象,而这个输入的对象必须是一个IEditorInput,IEditorPart遵循的就是打开-保存-关闭的生命周期模型 </p>
<p>IWorkbenchPart的生命周期模型： </p>
<p>&nbsp;&nbsp;&nbsp; 创建IWorkbenchPart扩展点 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实例化IWorkbenchPart </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 创建一个IWorkbenchPartSite </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用part.init(site) </p>
<p>&nbsp;&nbsp;&nbsp; IWorkbenchPart成为可见: </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用part.createControl(parent)绘制 IWorkbenchPart </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 激活partOpened event </p>
<p>&nbsp;&nbsp;&nbsp; IWorkbenchPart被激活或者取得焦点: </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用part.setFocus() </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 激活partActivated event </p>
<p>&nbsp;&nbsp;&nbsp; IWorkbenchPart被关闭: </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如需要保存,完成保存 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解除IWorkbenchPart的激活状态 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 激活partClosed event </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从界面上移除IWorkbenchPart </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用part.dispose() </p>
<p>PartPane：ViewPane和EditorPane的基类。 </p>
<p>PartSite：ViewSite和EditorSite的基类，实现IWorkbenchPartSite。 </p>
<p>IEditorInput：是editor input轻量级的描述符（descriptor），类似文件名，但更加抽象。它并不是一个model，而是为IEditorPart的model源的描述。</p>
<h1>小结</h1>
<p>本文介绍了一些RCP的基本概念，在接下来的随笔中将通过实例讲解如何进行RCP开发。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1504395.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48001/" target="_blank">上海电信计划2012年80%用户实现100M带宽</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>扩展URL Routing：处理URL中的日期参数</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/05/url-routing-extension.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Fri, 05 Jun 2009 06:45:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/05/url-routing-extension.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1496970.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/05/url-routing-extension.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1496970.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1496970.html</trackback:ping><description><![CDATA[<p>对URL中的日期参数，老赵提供了一个非常经典的<a href="http://www.cnblogs.com/JeffreyZhao/archive/2009/03/05/fully-leverage-url-routing.html">解决方案</a>。但是这里的日期参数是以yyyy-MM-dd的形式出现的，用户往往会有这样的需求，即URL中的参数一定要为Demo/Date/2009/06/05的形式。由于RouteData是以/来划分segment的，老赵的方案似乎就不适用了。</p> <p>国外的大牛们大多推崇将日期分隔为年、月、日，通过<a href="http://nayyeri.net/blog/custom-route-constraint-in-asp-net-mvc/">扩展IRouteConstraint</a>来对年月日分别进行判断。引用老赵的话来说，“无论从易用性还是通用性等角度来看，这种做法都是下下之策。说实话，这样的做法其实并没有跳出框架既有功能给定的圈子，它只是通过“迎合框架”来满足自己的需求，而不是让框架为我们的需求服务。”</p> <p>其实要实现这样的需求也不难，只需要对老赵的方案稍微修改一下就可以了。我们将URL定义为Demo/Date/{year}/{month}/{day}，然后在生成RouteData时，将year、month、day组合成date；生成路径时，再将date划分为year、month、day。</p> <p>新建一个DateFormatRoute类，让它继承自RouteBase。在GetRouteData方法中拼接年月日，其余方法与老赵的相同。</p><pre class="code"><span style="background: #eeefe6; color: blue">public override </span><span style="background: #eeefe6; color: #2b91af">RouteData </span><span style="background: #eeefe6">GetRouteData(</span><span style="background: #eeefe6; color: #2b91af">HttpContextBase </span><span style="background: #eeefe6">httpContext)
{
    </span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">routeData = route.GetRouteData(httpContext);
    </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(routeData.Values[</span><span style="background: #eeefe6; color: #a31515">"year"</span><span style="background: #eeefe6">] != </span><span style="background: #eeefe6; color: blue">null</span><span style="background: #eeefe6">)
    {
        </span><span style="background: #eeefe6; color: blue">string </span><span style="background: #eeefe6">date = routeData.Values[</span><span style="background: #eeefe6; color: #a31515">"year"</span><span style="background: #eeefe6">].ToString() + routeData.Values[</span><span style="background: #eeefe6; color: #a31515">"month"</span><span style="background: #eeefe6">].ToString() + routeData.Values[</span><span style="background: #eeefe6; color: #a31515">"day"</span><span style="background: #eeefe6">].ToString();
        routeData.Values.Remove(</span><span style="background: #eeefe6; color: #a31515">"year"</span><span style="background: #eeefe6">);
        routeData.Values.Remove(</span><span style="background: #eeefe6; color: #a31515">"month"</span><span style="background: #eeefe6">);
        routeData.Values.Remove(</span><span style="background: #eeefe6; color: #a31515">"day"</span><span style="background: #eeefe6">);
        routeData.Values[</span><span style="background: #eeefe6; color: #a31515">"date"</span><span style="background: #eeefe6">] = date;
    }
    </span><span style="background: #eeefe6; color: blue">else
    </span><span style="background: #eeefe6">{
        </span><span style="background: #eeefe6; color: blue">return null</span><span style="background: #eeefe6">;
    }

    </span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">valuesModified = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">Dictionary</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: blue">object</span><span style="background: #eeefe6">&gt;();
    </span><span style="background: #eeefe6; color: blue">foreach </span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">pair </span><span style="background: #eeefe6; color: blue">in </span><span style="background: #eeefe6">routeData.Values)
    {
        </span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">key = pair.Key;
        </span><span style="background: #eeefe6; color: #2b91af">IRouteFormatter </span><span style="background: #eeefe6">formatter = </span><span style="background: #eeefe6; color: blue">null</span><span style="background: #eeefe6">;
        </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: blue">this</span><span style="background: #eeefe6">.formatters.TryGetValue(key, </span><span style="background: #eeefe6; color: blue">out </span><span style="background: #eeefe6">formatter))
        { 
            </span><span style="background: #eeefe6; color: blue">object </span><span style="background: #eeefe6">o;
            </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(formatter.TryParse(pair.Value, </span><span style="background: #eeefe6; color: blue">out </span><span style="background: #eeefe6">o))
            {
                valuesModified[key] = o;
            }
            </span><span style="background: #eeefe6; color: blue">else
            </span><span style="background: #eeefe6">{
                </span><span style="background: #eeefe6; color: blue">return null</span><span style="background: #eeefe6">;
            }
        }
    }

    </span><span style="background: #eeefe6; color: blue">foreach </span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">pair </span><span style="background: #eeefe6; color: blue">in </span><span style="background: #eeefe6">valuesModified)
    {
        routeData.Values[pair.Key] = pair.Value;
    }
    </span><span style="background: #eeefe6; color: blue">return </span><span style="background: #eeefe6">routeData;
}</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>在GetVirtualPath方法中，再将年月日分解到各个segment中。</p><pre class="code"><span style="background: #eeefe6; color: blue">public override </span><span style="background: #eeefe6; color: #2b91af">VirtualPathData </span><span style="background: #eeefe6">GetVirtualPath(</span><span style="background: #eeefe6; color: #2b91af">RequestContext </span><span style="background: #eeefe6">requestContext, </span><span style="background: #eeefe6; color: #2b91af">RouteValueDictionary </span><span style="background: #eeefe6">values)
{
    </span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">routeValues = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RouteValueDictionary</span><span style="background: #eeefe6">();
    </span><span style="background: #eeefe6; color: blue">foreach </span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">pair </span><span style="background: #eeefe6; color: blue">in </span><span style="background: #eeefe6">values)
    {
        </span><span style="background: #eeefe6; color: blue">var </span><span style="background: #eeefe6">key = pair.Key;
        </span><span style="background: #eeefe6; color: #2b91af">IRouteFormatter </span><span style="background: #eeefe6">formatter = </span><span style="background: #eeefe6; color: blue">null</span><span style="background: #eeefe6">;
        </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: blue">this</span><span style="background: #eeefe6">.formatters.TryGetValue(key, </span><span style="background: #eeefe6; color: blue">out </span><span style="background: #eeefe6">formatter))
        {
            </span><span style="background: #eeefe6; color: blue">string </span><span style="background: #eeefe6">path;
            </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(formatter.TryToString(pair.Value, </span><span style="background: #eeefe6; color: blue">out </span><span style="background: #eeefe6">path))
            {
                routeValues[key] = path;
            }
            </span><span style="background: #eeefe6; color: blue">else
            </span><span style="background: #eeefe6">{
                </span><span style="background: #eeefe6; color: blue">return null</span><span style="background: #eeefe6">;
            }
        }
        </span><span style="background: #eeefe6; color: blue">else
        </span><span style="background: #eeefe6">{
            routeValues[key] = pair.Value;
        }
    }
    </span><span style="background: #eeefe6; color: blue">if </span><span style="background: #eeefe6">(routeValues[</span><span style="background: #eeefe6; color: #a31515">"date"</span><span style="background: #eeefe6">] != </span><span style="background: #eeefe6; color: blue">null</span><span style="background: #eeefe6">)
    {
        </span><span style="background: #eeefe6; color: blue">string </span><span style="background: #eeefe6">s = routeValues[</span><span style="background: #eeefe6; color: #a31515">"date"</span><span style="background: #eeefe6">].ToString();
        routeValues.Remove(</span><span style="background: #eeefe6; color: #a31515">"date"</span><span style="background: #eeefe6">);
        routeValues[</span><span style="background: #eeefe6; color: #a31515">"year"</span><span style="background: #eeefe6">] = s.Substring(0, 4);
        routeValues[</span><span style="background: #eeefe6; color: #a31515">"month"</span><span style="background: #eeefe6">] = s.Substring(4, 2);
        routeValues[</span><span style="background: #eeefe6; color: #a31515">"day"</span><span style="background: #eeefe6">] = s.Substring(6, 2);
        
    }

    </span><span style="background: #eeefe6; color: blue">return </span><span style="background: #eeefe6">route.GetVirtualPath(requestContext, routeValues);
}</span></pre><a href="http://11011.net/software/vspaste"></a>
<p>最后，修改Global.asax</p><pre class="code"><span style="background: #eeefe6">routes.Add(
    </span><span style="background: #eeefe6; color: #a31515">"Demo.Date"</span><span style="background: #eeefe6">,
    </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">DateFormatRoute</span><span style="background: #eeefe6">(
        </span><span style="background: #eeefe6; color: #a31515">"{controller}/{action}/{year}/{month}/{day}"</span><span style="background: #eeefe6">,
        </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RouteValueDictionary</span><span style="background: #eeefe6">(), </span><span style="background: #eeefe6; color: green">// defaults
        </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">Dictionary</span><span style="background: #eeefe6">&lt;</span><span style="background: #eeefe6; color: blue">string</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: #2b91af">IRouteFormatter</span><span style="background: #eeefe6">&gt;
        {
            {</span><span style="background: #eeefe6; color: #a31515">"controller"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RegexFormatter</span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: #a31515">"Demo"</span><span style="background: #eeefe6">)},
            {</span><span style="background: #eeefe6; color: #a31515">"action"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RegexFormatter</span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: #a31515">"Date"</span><span style="background: #eeefe6">)},
            {</span><span style="background: #eeefe6; color: #a31515">"date"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">DateTimeFormatter</span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: #a31515">"yyyyMMdd"</span><span style="background: #eeefe6">)}
        },
        </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RouteValueDictionary</span><span style="background: #eeefe6">(), </span><span style="background: #eeefe6; color: green">// constaints
        </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">RouteValueDictionary</span><span style="background: #eeefe6">(), </span><span style="background: #eeefe6; color: green">// data tokens
        </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">MvcRouteHandler</span><span style="background: #eeefe6">()));</span></pre>
<p>打开浏览器测试一下，URL和链接都显示正常。</p>
<p>PS：对于URL Routing的扩展，实在是想找一个别的例子来进行探讨。但稍微复杂一点的类型也不可能通过URL体现出来，而稍微简单一点的，用正则表达式就可以约束了。所以最后只要生硬的来修改老赵的示例了……</p>
<p>也可能是我的思路还没有打开，大家有没有什么比较好的思路？欢迎在这里讨论。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1496970.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/48000/" target="_blank">数万名网友签名抗议星际争霸2取消局域网功能</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>下载并使用ASP.NET MVC v1.0 Futures</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/04/1496309.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Thu, 04 Jun 2009 08:24:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/04/1496309.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1496309.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/04/1496309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1496309.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1496309.html</trackback:ping><description><![CDATA[<p>在4月9日<a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=53289097-73ce-43bf-b6a6-35e00103cb4b&amp;displaylang=en">微软官方</a>推出的ASP.NET MVC 1.0正式版中，并没有包含<a href="http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471#DownloadId=61773">ASP.NET MVC v1.0 Futures</a>，这使得很多有用的扩展方法都无法使用。比如，Html.ActionLink&lt;Controller&gt;辅助方法。</p> <p>该程序集（Microsoft.Web.Mvc.dll）中包含众多扩展方法，以前的CTP版本都有，但在正式版发布的时候，去掉了这部分。据说是为了保持版本的稳定性。其实，可能是由于deadline到了，产品“减配”了吧：）。</p> <p>我们仍然可以<a href="http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471">手动下载</a>该程序集，并在项目中添加引用。然后在web.config里添加Microsoft.Web.Mvc命名空间。</p><pre class="code"><span style="background: #eeefe6; color: blue">&lt;</span><span style="background: #eeefe6; color: #a31515">namespaces</span><span style="background: #eeefe6; color: blue">&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">System.Web.Mvc</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">System.Web.Mvc.Ajax</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">System.Web.Mvc.Html</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">System.Web.Routing</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">System.Linq</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">System.Collections.Generic</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
    &lt;</span><span style="background: #eeefe6; color: #a31515">add </span><span style="background: #eeefe6; color: red">namespace</span><span style="background: #eeefe6; color: blue">=</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">Microsoft.Web.Mvc</span><span style="background: #eeefe6">"</span><span style="background: #eeefe6; color: blue">/&gt;
&lt;/</span><span style="background: #eeefe6; color: #a31515">namespaces</span><span style="background: #eeefe6; color: blue">&gt;</span></pre><a href="http://11011.net/software/vspaste"></a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1496309.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47999/" target="_blank">Silverlight打造杰克逊纪念专题</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>诡异的DateTime.TryParseExact方法</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/04/1496258.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Thu, 04 Jun 2009 07:40:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/04/1496258.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1496258.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/04/1496258.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1496258.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1496258.html</trackback:ping><description><![CDATA[<p>老赵在介绍<a href="http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?culture=zh-CN&amp;EventID=1032402074&amp;CountryCode=CN">Routing扩展</a>的WebCast中出了点“小状况”，即将DateTime.ToString(“yyyy-MM-dd”)修改为DateTime.ToString(“yyyy/MM/dd”)后，页面中仍然显示为yyyy-MM-dd样式的日期格式。相信看过WebCast的同学都还记得吧。我不解，将老赵代码中DateTimeFotmatter的Formate属性修改为yyyy/MM/dd，在页面中输入yyyy-MM-dd样式的日期，仍然没有抛出任何异常。这是怎么回事？难道日期转换失灵了吗？</p> <p>老赵在做日期转换时，使用了DateTime.TryParseExact，那么我们就来看看这个TryParseExact是否在正常工作。</p><pre class="code"><span style="background: #eeefe6; color: #2b91af">DateTime </span><span style="background: #eeefe6">output;
</span><span style="background: #eeefe6; color: #2b91af">DateTime</span><span style="background: #eeefe6">.TryParseExact(</span><span style="background: #eeefe6; color: #a31515">"2009-06-04"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: #a31515">"yyyy/MM/dd"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: blue">null</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: #2b91af">DateTimeStyles</span><span style="background: #eeefe6">.None, </span><span style="background: #eeefe6; color: blue">out </span><span style="background: #eeefe6">output);
</span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.WriteLine(output.ToString());</span></pre><a href="http://11011.net/software/vspaste"></a>
<p>以上代码的输出结果果然让人大跌眼镜，日期居然能正确转换！</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/0c14639ccecc_D536/image_2.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="88" alt="image" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/0c14639ccecc_D536/image_thumb.png" width="237" border="0"></a> </p>
<p>难道TryParseExact的第二个参数format没有作用吗？将/改成.，居然又不能转换了：</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/0c14639ccecc_D536/image_4.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="87" alt="image" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/0c14639ccecc_D536/image_thumb_1.png" width="191" border="0"></a> </p>
<p>看来是/这个符号的问题。请出Reflector，将mscorlib.dll反编译，找到DateTime.TryParseExact方法，一步一步跟进去。发现如果该方法的IFormatProvider参数为null，将获取当前线程的CultureInfo的DateTimeFormat属性作为IFormatProvider，然后在DateTimeParse.ParseByFormat方法中，遇到format参数的/字符时，会比较输入日期字符串的当前字符是否为当前DateTimeFormatInfo的DateSeperator，如果是，则返回true，即允许转换，如果不是则返回false。</p><pre class="code"><span style="background: #eeefe6; color: #2b91af">CultureInfo </span><span style="background: #eeefe6">cultureInfo = </span><span style="background: #eeefe6; color: #2b91af">Thread</span><span style="background: #eeefe6">.CurrentThread.CurrentCulture;
</span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.WriteLine(cultureInfo.DateTimeFormat.DateSeparator);</span></pre><a href="http://11011.net/software/vspaste"></a>
<p></p>
<p>而以上代码输出的恰恰为-，也就是说当前线程的区域信息中，日期分隔符即为-，因此，转换得以成功。</p>
<p>如果您使用</p><pre class="code"><span style="background: #eeefe6; color: #2b91af">DateTimeFormatInfo </span><span style="background: #eeefe6">dtfi = </span><span style="background: #eeefe6; color: blue">new </span><span style="background: #eeefe6; color: #2b91af">CultureInfo</span><span style="background: #eeefe6">(</span><span style="background: #eeefe6; color: #a31515">"zh-CN"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: blue">false</span><span style="background: #eeefe6">).DateTimeFormat;
</span><span style="background: #eeefe6; color: #2b91af">DateTime </span><span style="background: #eeefe6">output;
</span><span style="background: #eeefe6; color: #2b91af">DateTime</span><span style="background: #eeefe6">.TryParseExact(</span><span style="background: #eeefe6; color: #a31515">"2009-06-04"</span><span style="background: #eeefe6">, </span><span style="background: #eeefe6; color: #a31515">"yyyy/MM/dd"</span><span style="background: #eeefe6">, dtfi, </span><span style="background: #eeefe6; color: #2b91af">DateTimeStyles</span><span style="background: #eeefe6">.None, </span><span style="background: #eeefe6; color: blue">out </span><span style="background: #eeefe6">output);
</span><span style="background: #eeefe6; color: #2b91af">Console</span><span style="background: #eeefe6">.WriteLine(output.ToString());</span></pre><p>则转换失败，因为在初始化CultureInfo时，第二个参数为false意味着不使用用户选定的区域性设置，而使用默认的设置，这时的DateSeparator为/</p><p>至此，谜底全部解开。</p><p>// 小贴士：遇到.NET Framework内部实现的问题时，使用Reflector反编译类库并查看源代码的方式，往往可以解决您的问题：）</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1496258.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47999/" target="_blank">Silverlight打造杰克逊纪念专题</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>【翻译】ASP.NET MVC中你必须知道的13个扩展点</title><link>http://www.cnblogs.com/kirinboy/archive/2009/06/01/13-asp-net-mvc-extensibility-points-you-have-to-know.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 01 Jun 2009 02:38:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/06/01/13-asp-net-mvc-extensibility-points-you-have-to-know.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1493489.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/06/01/13-asp-net-mvc-extensibility-points-you-have-to-know.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1493489.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1493489.html</trackback:ping><description><![CDATA[<p><a href="http://weblogs.asp.net/scottgu/"><em>ScottGu</em></a><em>在其</em><a href="http://weblogs.asp.net/scottgu/archive/2009/05/30/may-30th-links-asp-net-ajax-asp-net-mvc-visual-studio.aspx"><em>最新的博文</em></a><em>中推荐了</em><a target="_blank" href="http://codeclimber.net.nz"><em>Simone Chiaretta</em></a><em>的文章</em><a href="http://codeclimber.net.nz/archive/2009/04/08/13-asp.net-mvc-extensibility-points-you-have-to-know.aspx"><em>13 ASP.NET MVC extensibility points you have to know</em></a><em>，该文章为我们简单介绍了ASP.NET MVC中的13个扩展点。</em><a href="http://nayyeri.net/"><em>Keyvan Nayyeri</em></a><em>（与Simone合著了</em><a href="http://www.amazon.com/Beginning-ASP-NET-MVC-Simone-Chiaretta/dp/047043399X/"><em>Beginning ASP.NET MVC 1.0</em></a><em>一书）又陆续发表了一些文章，对这13个扩展点分别进行深入的讨论。我将在以后的随笔中对这些文章逐一进行翻译，希望能对大家有所帮助。</em></p>
<p>&nbsp;</p>
<p>ASP.NET MVC设计的主要原则之一是可扩展性。处理管线（processing pipeline）上的所有（或大多数）东西都是可替换的。因此，如果您不喜欢ASP.NET MVC所使用的约定（或缺乏某些约定），您可以创建自己的服务来支持您的约定，并将其注入到主管线中。</p>
<p>在本文中，我们将从管线开始直到视图呈现，逐一向您展示每个ASP.NET MVC开发者都必须了解13个扩展点。</p>
<h3>1.RouteConstraint</h3>
<p>通常情况下你可以使用正则表达式对url参数进行约束，但如果您的约束不仅仅取决于单一参数，您可以实现<a href="http://msdn.microsoft.com/en-us/library/system.web.routing.irouteconstraint.aspx">IRouteConstrains</a>的方法，并在其中添加你的验证逻辑。</p>
<p>比如对日期的验证，url中可能会包含年、月、日，而你需要验证这三者是否可以组合成一个有效的日期。</p>
<h3>2.RouteHandler</h3>
<p><a href="http://msdn.microsoft.com/en-us/library/system.web.routing.iroutehandler.aspx">RouteHandler</a>是在路由选择之后进行处理的组件，它并不仅仅针对ASP.NET MVC。显然，如果您改变了RouteHandler，那么对请求的处理将不再使用ASP.NET MVC，但这在您使用其他HttpHandler或<a target="_blank" href="http://haacked.com/archive/2008/03/11/using-routing-with-webforms.aspx">经典的WebForm</a>进行路由处理时却是非常有用的。</p>
<h3>3.ControllerFactory</h3>
<p>ControllerFactory是基于路由的组件，它选择正确的controller并对其实例化。<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultcontrollerfactory.aspx">default factory</a>会查找实现了<strong><a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.icontroller.aspx">IController</a></strong>并且以Controller结尾的类，然后通过反射使用无参构造函数进行实例化。</p>
<p>但如果您希望使用依赖注入，就不能再使用default factory，而必须使用支持IoC的controller factory。<a href="http://www.codeplex.com/MVCContrib">MvcContrib</a>和<a href="http://codeclimber.net.nz/archive/2009/02/05/how-to-use-ninject-with-asp.net-mvc.aspx">Ninject Controller Factory</a>都包含支持IoC容器的controller factory。</p>
<h3>4.ActionInvoker</h3>
<p><a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.iactioninvoker.aspx">ActionInvoker</a>顾名思义是负责调用（invoke）action的。默认的action invoker通过方法名、action名或其他可能的selector attribute来查找action，然后调用action方法以及定义的filter，最终执行得到action result。</p>
<p>你会发现大部分执行管线存在于<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.controlleractioninvoker.aspx">ControllerActionInvoker</a>类的逻辑之中。因此，如果希望改变这些约定，如action方法的选择逻辑、http参数映射到action参数的方式、选择和执行filter的方式等，您需要扩展该类并重写需要修改的方法。</p>
<p>可以参阅<a href="http://codeclimber.net.nz/archive/2009/02/10/how-to-use-ninject-to-inject-dependencies-into-asp.net-mvc.aspx">NinjectActionInvoker I developed to allow injection of dependencies inside filters</a>。</p>
<h3>5.ActionMethodSelectorAttribute</h3>
<p>使用默认的action invoker时，action的选择是基于名称的。您也可以实现自己的<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.actionmethodselectorattribute.aspx">Method Selector</a>以改善对于action的选择。在框架中已经包含了<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.acceptverbsattribute.aspx">AcceptVerbs</a>特性，它允许您指定使用哪一个HTTP Verb来处理action的响应。</p>
<p>例如，您也许会希望基于浏览器所支持的语言或浏览器类型（如移动设备的浏览器或桌面浏览器）来进行action的选取。</p>
<h3>6.AuthorizationFilter</h3>
<p>这种过滤器是在action执行之前执行的，用来确保请求是有效的。</p>
<p>框架中已经包含了一些autorization过滤器，最有名的莫过于<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx">Authorize</a>特性，它用来检查当前用户是否允许执行该action。另一个是<a href="http://haacked.com/archive/2009/04/02/anatomy-of-csrf-attack.aspx">用来阻止CSRF攻击的ValidateAntiForgeryToken</a>。如果您希望实现自己的authorization，那么必须实现接口。例如，日期中的小时。</p>
<h3>7.ActionFilter</h3>
<p><a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.iactionfilter.aspx">Action Filters</a>在action执行前后执行。<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.outputcacheattribute.aspx">OutputCache</a>过滤器是几个核心过滤器之一。这可能是您最有可能使用的扩展点，并且在我看来，controller只关心它的主要工作，而view所需要的所有其他数据都必须从action过滤器内部获取，这样的实现对于一个组织良好的view来说，是十分关键的。</p>
<h3>8.ModelBinder</h3>
<p>默认的model binder使用参数名称进行HTTP参数到action方法参数的映射。例如，http参数user.address.city将映射到方法参数user的Address属性的City属性。<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultmodelbinder.aspx">DefaultModelBinder</a>也同样适用于数组和其他列表类型。</p>
<p>更进一步来说，例如，您可能希望从数据库中进行检索，直接根据person的id将其转换为Person对象。Timothy Khouri（网名<a href="http://www.singingeels.com/">SingingEels</a>）在他的文章<a href="http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx">Model Binders in ASP.NET MVC</a>中更好的阐述了这种方法。他的代码基于Preview 5，但其理念是一样的。</p>
<h3>9.ControllerBase</h3>
<p>所有的Controller均继承自基类<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.aspx">Controller</a>。要想在action中封装自己的逻辑和约定，创建自己的父类使所有Controller继承自该类，是一种很好的方式。</p>
<h3>10.ResultFilter</h3>
<p>与ActionFilter类似，<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.iresultfilter.aspx">ResultFilters</a>在ActionResult前后执行。OutputCache过滤器也可以作为ResultFilter的示例。另外，比较常用的诠释这种过滤器的示例是日志记录。如果您希望在页面返回给用户时记录日志，可以编写自定义的RenderFilter，在ActionResult执行之后记录日志。</p>
<h3>11.ActionResult</h3>
<p>ASP.NET MVC提供了很多result用来呈现视图、JSON、纯文本、文件并重定向到其他action。如果您需要其他类型的result，可以自定义<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.actionresult.aspx">ActionResult</a>，并实现<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.actionresult.executeresult.aspx">ExecuteResult</a>方法。例如，如果您希望将PDF文件作为结果发送，您需要使用PDF库编写能够生成PDF的ActionResult。又如RSS feed，可参见<a href="http://blogs.msdn.com/jowardel/archive/2009/03/11/asp-net-rss-actionresult.aspx">how to write a RssResult in this post</a>。</p>
<h3>12.ViewEngine</h3>
<p>您可能不需要编写自己的view engine，但您也许可以考虑使用其他引擎来替代默认的WebForm view engine。在我看来，最有趣的引擎就是<a href="http://sparkviewengine.com">Spark</a>。</p>
<p>如果您确实希望编写自己的view engine，可以看一下<a href="http://bradwilson.typepad.com/blog/">Brad Wilson</a>的文章: <a href="http://bradwilson.typepad.com/blog/2008/08/partial-renderi.html">Partial Rendering &amp; View Engines in ASP.NET MVC</a>。</p>
<h3>13.HtmlHelper</h3>
<p>视图必须十分简单整洁，它们只能包含html标记并调用HtmlHelper的辅助方法。视图中不能包含任何代码，所以辅助方法必须十分方便，使您可以将代码从视图中提取出来，放到一个可测试的环境中去。正如<a href="http://blog.wekeroad.com/blog/">Rob Conery</a>所说：如果有if，就构造辅助方法（If there's an IF, make a Helper）。</p>
<p>什么是HtmlHelper辅助方法？其实就是<a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.htmlhelper.aspx">HtmlHelper</a>类的扩展方法，这是唯一的要求。</p>
<p>你可以从Rob的文章<a href="http://blog.wekeroad.com/blog/asp-net-mvc-avoiding-tag-soup/">Avoiding Tag Soup</a>中了解到为什么说HtmlHelper是封装视图中代码的好方法。</p>
<h3>在您的应用中该使用哪个呢？</h3>
<p>正如您所猜测的那样，并不是所有的应用都需要扩展以上的13个扩展点。最可能在所有应用中进行扩展的是ActionFilter和HtmlHelper。另外，您很可能会使用其他人编写的扩展，如使用了IoC容器的ControllerFactory或用来摆脱WebForm的ViewEngine。</p>
<p>但是，学习这些扩展点并进行尝试是十分重要的，这样您才会做出选择，并随时准备在必要的时候使用这些强大的扩展点。下周<a href="http://feeds2.feedburner.com/Codeclimber">我将发表</a>一些文章来阐述如何使用这些扩展点。</p>
<p>如果您想详细了解更多关于该话题的内容，可以考虑购买即将出版的<a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.com%2FBeginning-ASP-NET-MVC-Simone-Chiaretta%2Fdp%2F047043399X%2F&amp;tag=codec04-20&amp;linkCode=ur2&amp;camp=1789&amp;creative=9325">Beginning ASP.NET MVC</a>（我是作者之一）或<a href="http://www.amazon.com/gp/product/0470384611?ie=UTF8&amp;tag=codec04-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0470384611">Professional ASP.NET MVC</a>（ASP.NET MVC开发团队编写）或<a href="http://www.amazon.com/gp/product/1933988622?ie=UTF8&amp;tag=codec04-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1933988622">ASP.NET MVC in Action</a> （<a href="http://jeffreypalermo.com/blog/">Jeffrey Palermo</a>和<a href="http://www.flux88.com/">Ben Scheirman</a>著）。</p>
<p>我是否遗漏了某些您认为重要的扩展点呢？您是否使用过我上面提到的扩展点呢？<a href="http://codeclimber.net.nz/archive/2009/04/08/13-asp.net-mvc-extensibility-points-you-have-to-know.aspx#feedback">我很想听听</a>您所遇到的场景。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1493489.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47998/" target="_blank">传诺基亚正在开发Android手机</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>深入浅出Eclipse RCP（1）：Hello RCP</title><link>http://www.cnblogs.com/kirinboy/archive/2009/05/25/HeadFirstEclipseRcp1.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 25 May 2009 08:40:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/05/25/HeadFirstEclipseRcp1.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1488967.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/05/25/HeadFirstEclipseRcp1.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1488967.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1488967.html</trackback:ping><description><![CDATA[<p>Eclipse RCP（Rich Client Platform）允许开发者使用Eclipse架构设计灵活的、可扩展的、美观的应用程序。本系列希望通过层层深入的介绍，一步一步带您进入Eclipse RCP的玄妙世界。</p>
<h1>第一个RCP程序</h1>
<p>打开Eclipse，新建一个Plug-in Project，点击Next。在Project name文本框中输入项目名称net.kirin.rcp.hello，如下图所示。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_2.png"><img border="0" width="528" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb.png" alt="image" height="502" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>点击Next，如下图所示，在&ldquo;Would you like to create a rich client application？&rdquo;后面选中Yes。其余均可保留默认值。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_4.png"><img border="0" width="530" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb_1.png" alt="image" height="504" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>点击Next，选择Hello RCP模板。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_6.png"><img border="0" width="530" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb_2.png" alt="image" height="502" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>点击Next，打开如下对话框，各项均可保留默认值。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_8.png"><img border="0" width="529" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb_3.png" alt="image" height="502" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>点击Finish，将创建结构如下的项目。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_10.png"><img border="0" width="335" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb_4.png" alt="image" height="397" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>双击MANIFEST.MF文件，将打开一个名为Overview的界面，点击Launch an Eclipse application，</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_16.png"><img border="0" width="900" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb_7.png" alt="image" height="555" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>结果如下图所示。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_18.png"><img border="0" width="402" src="http://images.cnblogs.com/cnblogs_com/kirinboy/WindowsLiveWriter/EclipseRCP_C520/image_thumb_8.png" alt="image" height="300" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" /></a> </p>
<p>这就是我们的第一个RCP程序。当然，我们没有写一行代码，完全是Hello RCP模板自动为我们创建的。</p>
<h1>代码解析</h1>
<p>可以看到，RCP模板自动为我们创建了Application.java、ApplicationActionBarAdvisor.java、ApplicationWorkbenchAdvisor.java、ApplicationWorkbenchWindowAdvisor.java、Perspective.java这5个类。下面我们对这5个类进行逐一分析。</p>
<h2>Application类</h2>
<p>在普通的Java程序中，总有一个main()方法作为应用程序的入口点。而RCP程序的入口点则是Application类。打开Application.java文件，可以看到该类实现了IPlatformRunnable接口，入口方法如下所示：</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: #7f0055">public </span>Object run(Object args) <span style="color: #7f0055">throws </span>Exception {
    Display display = PlatformUI.createDisplay();
    <span style="color: #7f0055">try </span>{
        <span style="color: #7f0055">int </span>returnCode = PlatformUI.createAndRunWorkbench(display, <span style="color: #7f0055">new </span>ApplicationWorkbenchAdvisor());
        <span style="color: #7f0055">if </span>(returnCode == PlatformUI.<span style="color: #0000c0">RETURN_RESTART</span>) {
            <span style="color: #7f0055">return </span>IPlatformRunnable.<span style="color: #0000c0">EXIT_RESTART</span>;
        }
        <span style="color: #7f0055">return </span>IPlatformRunnable.<span style="color: #0000c0">EXIT_OK</span>;
    } <span style="color: #7f0055">finally </span>{
        display.dispose();
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>run()方法为IPlatformRunnable接口所定义的方法，在RCP程序启动时，会首先执行该方法。Application首先创建一个Display对象， 然后调用PlatformUI.createAndRunWorkbench()方法创建并启动工作台，这将打开应用程序主窗口，并使该窗口处于持续打开状态。应用程序开始处理用户的鼠标单击、鼠标移动、按键等各种事件，直到用户关闭程序退出，这就是所谓的事件循环。当然，在关闭之前，必须用display.dispose()销毁Display对象以释放资源。</p>
<h2>ApplicationWorkbenchAdvisor类</h2>
<p>在PlatformUI.createAndRunWorkbench()方法中，还传入了一个新建的ApplicationWorkbenchAdvisor对象。它类负责应用程序生命周期管理，它继承自WorkbenchAdvisor类。开发人员可以在该类中实现程序启动或者关闭时的某种处理。该类的具体实现只是给用户一个初始的视图界面而已，还需要配合WorkbenchWindowAdvisor、ActionBarAdvisor才能构成一个较为完整的用户界面。</p>
<p>打开ApplicationWorkbenchAdvisor.java文件，代码如下：</p>
<pre class="code"><span style="color: #7f0055">public class </span>ApplicationWorkbenchAdvisor <span style="color: #7f0055">extends </span>WorkbenchAdvisor {

    <span style="color: #7f0055">private static final </span>String <span style="color: #0000c0">PERSPECTIVE_ID </span>= <span style="color: #2a00ff">"net.kirin.rcp.hello.perspective"</span>;

    <span style="color: #7f0055">public </span>WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
        <span style="color: #7f0055">return new </span>ApplicationWorkbenchWindowAdvisor(configurer);
    }

    <span style="color: #7f0055">public </span>String getInitialWindowPerspectiveId() {
        <span style="color: #7f0055">return </span><span style="color: #0000c0">PERSPECTIVE_ID</span>;
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p>
<p>createWorkbenchWindowAdvisor()方法初始化在主界面显示的透视图，并返回ApplicationWorkbenchWindowAdvisor对象。</p>
<p>ApplicationWorkbenchAdvisor还包含一些比较重要的方法：</p>
<ul>
<li>initialize：最先调用。在窗口打开之前调用，可以用来处理初始化配置工作。</li>
<li>preStartup：initialize之后、第一个窗口打开之前调用，可以用来处理临时或者可选处理工作。 </li>
<li>postStartup：第一个窗口打开之后但启动事件循环之前调用，可以用来进行那些需要自动处理的工作，例如弹出一个提示窗口。</li>
<li>preShutdown：事件循环结束之后但窗口关闭之前调用，可以用来进行保存数据、关闭数据库服务器等处理工作。 </li>
<li>postShutdown：窗口关闭之后调用，可以用来进行保存应用程序状态、清除initialize创建的对象等处理工作。 </li>
</ul>
<h2>ApplicationWorkbenchWindowAdvisor类</h2>
<p>在ApplicationWorkbenchAdvisor类中创建了一个ApplicationWorkbenchWindowAdvisor对象。它主要负责窗口生命周期的管理，例如状态栏、工具栏、菜单、窗口标题、窗口大小和各种控件等等，也可以处理窗口的各种事件例程。其主要代码如下：</p>
<pre class="code"><span style="color: #7f0055">public class </span>ApplicationWorkbenchWindowAdvisor <span style="color: #7f0055">extends </span>WorkbenchWindowAdvisor {

    <span style="color: #7f0055">public </span>ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
        <span style="color: #7f0055">super</span>(configurer);
    }

    <span style="color: #7f0055">public </span>ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) {
        <span style="color: #7f0055">return new </span>ApplicationActionBarAdvisor(configurer);
    }
    
    <span style="color: #7f0055">public void </span>preWindowOpen() {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
        configurer.setInitialSize(<span style="color: #7f0055">new </span>Point(400, 300));
        configurer.setShowCoolBar(<span style="color: #7f0055">false</span>);
        configurer.setShowStatusLine(<span style="color: #7f0055">false</span>);
        configurer.setTitle(<span style="color: #2a00ff">"Hello RCP"</span>);
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p><a href="http://11011.net/software/vspaste"></a>preWindowOpen()方法在窗口控件创建之前调用，用来配置窗口，如设置窗口大小、工具栏、状态栏和标题等。createActionBarAdvisor创建了一个ApplicationActionBarAdvisor对象。</p>
<p>其它比较重要的方法还包括：</p>
<ul>
<li>postWindowRestore：当窗口根据上一次的保存状态恢复创建之后调用，可以用来调整调整窗口恢复状态。</li>
<li>postWindowCreate：窗口创建之后调用，可以用于调整窗口。 </li>
<li>postWindowOpen：窗口已经打开之后调用，可以用来注册窗口监听，例如在此方法中实现系统托盘。 </li>
</ul>
<h2>ApplicationActionBarAdvisor类</h2>
<p>ApplicationActionBarAdvisor主要负责管理窗口的菜单栏、状态栏、工具栏的外观和行为。其主要代码如下：</p>
<pre class="code"><span style="color: #7f0055">public class </span>ApplicationActionBarAdvisor <span style="color: #7f0055">extends </span>ActionBarAdvisor {

    <span style="color: #7f0055">public </span>ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {
        <span style="color: #7f0055">super</span>(configurer);
    }

    <span style="color: #7f0055">protected void </span>makeActions(IWorkbenchWindow window) {
    }

    <span style="color: #7f0055">protected void </span>fillMenuBar(IMenuManager menuBar) {
    }
    
}</pre>
<p><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p>
<p>makeActions()方法用来注册菜单或工具栏的动作。fillMenuBar()方法用来添加菜单栏。</p>
<p>其它比较重要的方法还包括：</p>
<ul>
<li>fillCoolBar：添加工具栏 </li>
<li>fillStatusLine：添加状态栏 </li>
</ul>
<h2>Perspective类</h2>
<p>Perspective类主要负责界面布局的安排，其代码如下：</p>
<pre class="code"><span style="color: #7f0055">public class </span>Perspective <span style="color: #7f0055">implements </span>IPerspectiveFactory {

    <span style="color: #7f0055">public void </span>createInitialLayout(IPageLayout layout) {
    }
}
</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>createInitialLayout()方法通常用于创建初始的界面布局。</p>
<h1>参考资料</h1>
<p>1. Eclipse从入门到精通 </p>
<p>2. Eclipse RCP应用系统开发方法与实战 </p>
<p>3. <a target="_blank" href="http://www.vogella.de/articles/RichClientPlatform/article.html">Eclipse RCP - Tutorial with Eclipse 3.4</a> </p>
<p>4. <a href="http://www.blogjava.net/youxia/archive/2006/11/17/81852.html">使用Eclipse RCP进行桌面程序开发（一）：快速起步</a> </p>
<h1>小结</h1>
<p>本篇作为《深入浅出Eclipse RCP》系列的开篇，介绍了如何使用Hello RCP模板创建最简单的RCP程序，并对自动生成的5个类进行了解析。如果您是RCP的初学者，也许会对本文中出现的一些概念改到困惑。没有关系，我将在接下来的随笔中介绍RPC的基本架构以及Workbench、Action、Perspective等概念。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1488967.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47996/" target="_blank">7月编程语言排行榜</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>拿到了《ASP.NET 3.5 揭秘（卷1）》的样书</title><link>http://www.cnblogs.com/kirinboy/archive/2009/03/19/ASP-NET-35-Unleashed.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Thu, 19 Mar 2009 14:28:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/03/19/ASP-NET-35-Unleashed.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1417180.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/03/19/ASP-NET-35-Unleashed.html#Feedback</comments><slash:comments>47</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1417180.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1417180.html</trackback:ping><description><![CDATA[<p>今天收到了两个快件，一个是淘宝上刚买的E63，还有一个就是六本厚厚的<a href="http://www.china-pub.com/195145" target="_blank">《ASP.NET 3.5 揭秘（卷1）》</a>的样书。看着自己辛苦了几个月的成果，心里那叫一个激动啊（此处省略1w字&#8230;&#8230;）
<p>于是用心爱的63拍了几张pp，上图！
<p><a href="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(001)_2.jpg"><img title="20090319(001)" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="484" alt="20090319(001)" src="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(001)_thumb.jpg" width="644" border="0" /></a>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(002)_2.jpg"><img title="20090319(002)" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="484" alt="20090319(002)" src="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(002)_thumb.jpg" width="644" border="0" /></a> </p>
<p>书皮秉承了图灵一贯的风格。</p>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(003)_2.jpg"><img title="20090319(003)" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="484" alt="20090319(003)" src="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(003)_thumb.jpg" width="644" border="0" /></a> </p>
<p>扉页有&#8220;样书&#8221;的章。</p>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(004)_2.jpg"><img title="20090319(004)" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="484" alt="20090319(004)" src="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/ASP.NET3.51_13425/20090319(004)_thumb.jpg" width="644" border="0" /></a> </p>
<p>译者介绍，最后一个译者就是在下啦。什么？看不清？赶快去买一本吧，呵呵。</p>
<p>对于Stephen Walther的ASP.NET揭秘系列，我早在一年前就进行过<a href="http://www.cnblogs.com/kirinboy/archive/2008/03/25/dotNet_Books_Recommend.html" target="_blank">介绍和推荐</a>，没想到这次居然有机会翻译将自己带入ASP.NET殿堂的名著的后续版本，实在感到万分荣幸，以及万分担忧（怕自己翻译不好啊&#8230;&#8230;）。</p>
<p>总的来说，《ASP.NET 3.5 揭秘》上下两卷延续着前两版的风格和质量，循序渐进、深入潜出、示例丰富。作为译者，不敢亵渎经典，唯有小心翼翼，字句斟酌，力求做到信、达、雅。好在有上一版四位MVP的卓越工作，他们流畅的翻译使我少走了很多弯路。图灵公司的陈兴璐和王军花两位编辑认真负责，保障了两卷书的顺利出版。</p>
<p>这一版《揭秘》新增了300也的内容，包括：</p>
<p>1. ListView和DataPager</p>
<p>2. LINQ to SQL</p>
<p>3. ASP.NET AJAX</p>
<p>4. 全新的示例程序</p>
<p>同时删除了Web部件的相关内容。</p>
<p>本书是一部全面的ASP.NET工具书，尤其适合广大初学者。如果大家对于本书有什么意见或建议，请在留言中列出。</p>
<img src ="http://www.cnblogs.com/kirinboy/aggbug/1417180.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47995/" target="_blank">Google Voice 上手</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>乱弹抽象类与接口</title><link>http://www.cnblogs.com/kirinboy/archive/2009/03/02/AbstractClass_and_Interface.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 02 Mar 2009 06:20:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/03/02/AbstractClass_and_Interface.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1401501.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/03/02/AbstractClass_and_Interface.html#Feedback</comments><slash:comments>31</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1401501.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1401501.html</trackback:ping><description><![CDATA[<p>这几天园子里有几篇随笔对抽象类与接口进行了比较和分析，具体的概念Anytao在<a href="http://www.cnblogs.com/anytao/archive/2007/04/12/must_net_02.html" target="_blank">[你必须知道的.NET] 第二回：对抽象编程：接口和抽象类</a>中已经总结的很好了，大家可以参考。每个人对面向对象都有自己的理解，我也来谈谈我的认识。</p>
<p><font color="#0080ff"><strong>为什么CLR不允许多重继承？</strong></font></p>
<p>这个问题乍看起来也许很小白，不允许就是不允许，哪来那么多为什么呢？你以为你是小沈阳啊？</p>
<p>呵呵，不好意思，我还没完，接下来的问题是，为什么所有的类都继承自System.Object？为什么不能实例化抽象（包括抽象类和接口）？为什么接口不能有实现？&#8230;&#8230;对于这些问题，我们真的认真思考过吗？</p>
<p>其实，面向对象思想，是对现实世界最好的诠释。一切皆对象，在OO中如此，在现实生活中亦是如此。在虚拟的程序中，我们经常会对现实中的各种事物进行建模，于是&#8220;人&#8221;、&#8220;汽车&#8221;等事物就成了代码中的Person和Car。我们为什么会自然而然地进行这样的转换呢？这正是面向对象编程思想的魅力所在。&#8220;人&#8221;和&#8220;汽车&#8221;在现实中都代表一类事物，他们有相同的行为和属性，这就是面向对象中类的概念。</p>
<p>许多年前，当我开始接触面向对象的时候，我并不能理解书本上晦涩的概念。然而随着CS水平的日益提高，我也逐渐对面向对象有了自己的认识。AK47就是一个类，当你按下B+4+1的时候，就初始化了这样一个实例。它有自己的属性：大小、颜色、子弹数量、换弹时间；也有自己的行为：发射、换子弹。此后，再将面向对象与现实世界相联系，我逐渐理解了前面提到的那几个问题。他们的答案都是一个：因为现实世界就是这个样子的。</p>
<p>为什么所有的类都继承自System.Object？因为现实世界所有的事物都可以归为一个绝对抽象的基类。生物、非生物都是这个基类的派生。外形、运动等等就是这个基类的属性或行为。</p>
<p>为什么不能实例化抽象？因为现实世界中不存在抽象的具体事物。抽象是从众多的事物中抽取出共同的、本质性的特征，它并不是某一具体的事物。换句话说，如果存在可以具体化的抽象，那么这个抽象就不是真正意义上的抽象了，它对于事物共性的提取，是错误的。</p>
<p>为什么不允许多重继承？因为现实世界种不存在多重继承。派生类与基类的关系是IS-A，即派生类是一个基类。人就是动物，动物就是生物，不可能同时还是非生物。任何一个事物都有所属，我们把动物植物按照一定的纲、门（汗&#8230;&#8230;）分类，就是这个原因。如果存在A既是B又是C，只能说明B是C或者C是B，如果B和C没有继承关系，那么你说A到底是B还是C呢？当然，随着科技的发展，新的事物层出不穷，有些无法无法按照单继承分类的事物，其实就是一个新的事物。比如，水路两用车，到底是属于车呢，还是属于船呢？其实都不是，它就是水路两用的交通工具，自成一派。</p>
<p><font color="#0080ff"><strong>抽象类，还是接口？</strong></font></p>
<p>抽象类和接口都属于以上提到的抽象，两者的在语法上的区别Anytao和其他园友已经总结的差不多了，不再赘述。我主要想和大家讨论一下在设计时如何选择抽象类和接口。</p>
<p>我的原则是，尽量按照现实世界的语义来判断。</p>
<p>众所周知，派生类与抽象类的关系是IS-A，实现类与接口的关系是CAN-DO。这就说明抽象类与子类之间的所属关系较接口来说更加明确。接口只是行为方式上的一种契约，一个类实现了一个接口，只能说明该类具有该接口所约定的行为，但它并不属于该接口（事实上，接口也无法成为其他类的所属者），因此我们把对抽象类的派生叫做继承，而把对接口的派生叫做实现。例如，IComparable接口为所有可进行比较的类提供一个抽象的约定，但也仅仅局限于该约定。在语义上，&#8220;可以比较的&#8221;并不是某一类事物全部行为和属性的抽象，因此并不能够成为其派生类的父类。</p>
<p>人和汽车肯定不属于一类事物，但是他们却可以有相同的行为，即Move。那么Person类和Car类虽然继承自不同的基类，但它们却可以实现同一个接口IMoveable（该接口仅包含一个Move方法）。</p>
<p>因此，在设计时，我们需要思考这个抽象是代表的一类事物，还是代表一类行为或属性。如果是一类事物，就设计为抽象类；如果是一类行为或属性，就设计为接口。如果发现某个子类不得不继承两个基类，那么就必须审视一下这两个基类的设计是否合理，是否应该提取一些接口出来，或者是否应该重新设计一个基类。</p>
<p>现在来回答为什么接口不能有实现的问题，因为现实世界&#8230;&#8230;哎呀，哪里的砖头？&#8230;&#8230;</p>
<p>呵呵，其实正如前面所说，接口只是一种规范的约定，它并不了解各个实现类的具体细节。抽象类Person可以对Move方法提供默认实现（走或跑），Car也同样可以（前进或倒退），但是IMoveable接口并不知道，能实现这个接口的类成千上万并且实现方式千奇百怪，要它提供一个默认的移动方式，实在强接口所难。</p>
<p><font color="#0080ff"><strong>对于命名的建议</strong></font></p>
<p>对于抽象类和接口的命名规范，我的建议是：将抽象类（类也是如此）命名为名词，将接口命名为I为前缀的形容词，最好以-able为后缀。</p>
<p>在.NET Framework里随处可见以-able为后缀的接口，如IComparable、IDisposable&#8230;&#8230;意为&#8220;可&#8230;&#8230;的&#8221;，这完全复合CAN-DO的语义定义。前面的IMoveable接口如果命名为IMove，在语义上或多或少地缺少了CAN-DO的意境。</p>
<p>当然，这只是一种建议。只要牢记接口是&#8220;可&#8230;&#8230;的&#8221;这个原则，在选择抽象类和接口时就不会感到茫然了。</p>
<p>总之一句话，请参考现实世界。</p>
 <img src ="http://www.cnblogs.com/kirinboy/aggbug/1401501.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47994/" target="_blank">Google号召社区力量为互联网加速</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>【翻译】Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 4</title><link>http://www.cnblogs.com/kirinboy/archive/2009/03/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_4.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Sun, 01 Mar 2009 09:03:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/03/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_4.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1400916.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/03/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_4.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1400916.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1400916.html</trackback:ping><description><![CDATA[<p>原文地址：<a target="_blank" href="http://aspalliance.com/1630_Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_4.all">Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 4</a></p>
<p><strong><span style="color: #0080ff;">摘要</span></strong></p>
<p>在本系列的第四篇中，Keyvan讨论了与MVC模式相关的单元测试的概念，以及微软是如何将这些概念应用于其ASP.NET MVC框架中的。他提出了对ASP.NET MVC应用进行单元测试必须遵循的原则和过程。</p>
<p><strong><span style="color: #0080ff;">内容</span></strong></p>
<ul>
<li>简介 </li>
<li>单元测试概述 </li>
<li>为什么使用MVC？为什么使用ASP.NET MVC？ </li>
<li>ASP.NET MVC中的抽象与隔离 </li>
<li>MVC模式中的测试过程 </li>
<li>ASP.NET MVC Preview 2中改进的测试特性 </li>
<li>其他部分 </li>
<li>小结</li>
</ul>
<p><strong><span style="color: #0080ff;">简介</span></strong></p>
<p>在本系列的前三篇中，我讨论了MVC模式中的主要概念、ASP.NET MVC框架以及KBlog（本系列中创建的简单Blog引擎）。目前为止，我讨论了MVC模式中三个核心组件中的两个：控制器和数据模型。</p>
<p>Web开发者（不止是ASP.NET开发者）对MVC模式&ldquo;趋之若鹜&rdquo;的主要原因是，它赋予了开发者测试Web应用的能力，使得测试变得更加简单。因此，Web应用也可以进行单元测试了。</p>
<p>当然，对于传统的Web应用也不是不能单元测试，只是MVC模式下要简单得多。</p>
<p>本节和下节的话题都是单元测试。在MVC模式中，你可以仅测试控制器类。在演示如何对MVC应用进行单元测试之前，我们先从基本概念入手。</p>
<p>在开始正题之前我需要指出的是，本系列前三篇是基于ASP.NET MVC框架的第一个发布版本CTP的，而本文则基于第二个CTP版本。</p>
<p><strong><span style="color: #0080ff;">单元测试概述</span></strong></p>
<p>测试驱动开发（TDD）是当今最普遍的软件开发方式。敏捷开发方法的兴起以及小型软件开发团队如雨后春笋般的增多，是TDD作为软件开发首选方法的主要原因（当然还有其他的原因）。</p>
<p>因为显然我不可能在这里讲述太多TDD和单元测试的东西，因此我假设读者已经有了这方面的背景。但是，一个简短的介绍还是有必要的。</p>
<p>TDD最核心的部分就是单元测试。单元测试是将应用划分为小的不相关的可以独立工作的单元，并对这些单元进行测试的一组技术和过程。</p>
<p>单元测试基于以下一些原则。首先，将应用划分为更小的更便于测试的部分。其次，这些单元彼此之间相互独立互不影响。这种独立性使得对一个单元的更改不会波及到其他单元。</p>
<p>单元测试对这些小的单元进行测试，看它们对于不同的环境和输入数据是否都会按预期执行。这样，全部单元测试通过之后，就可以开始下一阶段的开发了。如果你修改了其他元素或单元并破坏了现有代码（这并不稀奇），那么现有的测试将不会通过，同时发出警报。</p>
<p>因此，你可以反复修改重构代码而不会破坏其连贯性。这也是众多开发人员选择单元测试的主要原因。</p>
<p>然而，我无法对测试驱动开发和单元测试进行过多的描述，当然我也不会那么做。但我只是想说，有很多方法可以对数据层、抽象代码和Web服务进行单元测试。同时，也有很多优秀的模式和实践方面的文档，描述如何编写使单元测试过程更加简单的代码。</p>
<p><strong><span style="color: #0080ff;">为什么使用MVC？为什么使用ASP.NET MVC？</span></strong></p>
<p>Web开发是一个需要自身技术和模式的普通开发场景。由于Web开发与多项服务器技术如Web服务器属性或客户端请求细节等息息相关，软件开发者需要寻求一条途径以降低这些依赖。基于以上介绍，你知道为什么了吗？</p>
<p>原因就是&ldquo;隔离&rdquo;。我们需要降低依赖来改善隔离等级。解决方案就是引进新的开发模式（如MVC）。最著名的为MVC量身打造的技术就是Ruby on Rails，这也是Ruby之所以流行的原因之一。</p>
<p>Web开发技术的发展使得微软开始考虑改变ASP.NET技术以适应该模式，ASP.NET MVC Framework应运而生。</p>
<p>总而言之：</p>
<ul>
<li>单元测试很棒（在我看来，它应该成为软件开发的一部分）。</li>
<li>单元测试需要对应用中不同的但愿进行高等级的抽象和隔离。</li>
<li>传统的ASP.NET Web Form应用对HttpContext、HttpRequest和HttpResponse之类的对象有很强的依赖。</li>
<li>微软需要在其Web开发技术中适应MVC。</li>
<li>微软需要降低所有依赖并提升隔离等级。</li>
<li>因此，考虑到隔离和抽象，微软需要构建ASP.NET MVC模式。</li>
</ul>
<p>因此，ASP.NET MVC Framework伴随着一些主要的ASP.NET类（如HttpContext、HttpResponse或HttoRequest）的重新设计，而悄然诞生。</p>
<p><span style="color: #0080ff;"><strong>ASP.NET MVC中的抽象和隔离</strong></span></p>
<p>我们需要良好的抽象和隔离，但在设计和编码时应该如何做到这一点呢？抽象类和接口为我们提供了很好的方案。</p>
<p>单元测试与抽象基类和接口是很紧密的，并且有很多的模式和实践。我不想在这个话题上深入下去。</p>
<p>当使用抽象类和接口来进行抽象时，你可以很简单地测试这些类的实例，因为他们拥有共同的主要方法。在单元测试时这是十分有用的，因为它使你能更好地控制在单元测试中扮演某些角色的类。</p>
<p>但是如何选择使用抽象基类还是接口呢？尽管它们都可以为我们提供抽象，但也都存在一些局限性。</p>
<p>我的朋友，微软ASP.NET MVC团队成员，Phil Haack，发表了一些博客解释这些局限性。并且，他们在第一个CTP版本的ASP.NET MVC Framework中所面对的接口的局限，使得他们在第二版甚至以后所有的版本中都选择了抽象基类。</p>
<ul>
<li><a href="http://haacked.com/archive/2008/02/21/versioning-issues-with-abstract-base-classes-and-interfaces.aspx">Versioning Issues With Abstract Base Classes and Interfaces</a></li>
<li><a href="http://haacked.com/archive/2008/02/21/abstract-base-classes-have-versioning-problems-too.aspx">Abstract Base Classes Have Versioning Problems Too</a></li>
<li><a href="http://haacked.com/archive/2008/03/04/the-cost-of-breaking-changes.aspx">The Cost Of Breaking Changes</a></li>
</ul>
<p>然而从最终用户的角度来看，你不必郭宇关心这些问题。</p>
<p>但最终，微软在第二个发布版本的ASP.NET MVC中使用了抽象基类。</p>
<p>你将看到这些改变有助于你在下面的章节中对ASP.NET MVC应用进行单元测试。</p>
<p>微软将所有的抽象类移动到一个新的程序集<em>System.Web.Abstractions</em>中，你可以反编译该程序集中的类。</p>
<p>图1按层次和结构展示了这些类，你可以对其抽象级别有些了解。</p>
<p>图1</p>
<p><img border="0" width="662" src="http://aspalliance.com/ArticleFiles/1630/image001.gif" height="538" /></p>
<p>如图所示，有7组基类及其派生类。每个抽象基类都代表了一个包含很多依赖的传统的ASP.NET类。因此这种抽象有助于单元测试过程，你将在下一节中有所了解。</p>
<p>简而言之，我们可以将这些类归为7种类型：</p>
<ul>
<li>HttpSessionState</li>
<li>HttpServerUtility</li>
<li>HttpContext</li>
<li>HtpResponse</li>
<li>HttpRequest</li>
<li>HttpBrowserCapabilities</li>
<li>HttpCachePolicy</li>
</ul>
<p>如你所见，所有的类型都与服务器、客户端属性和特性联系紧密。但这种抽象有助于降低依赖性并改进易测性。</p>
<p><span style="color: #0080ff;"><strong>MVC模式中的测试过程</strong></span></p>
<p>ASP.NET MVC中的测试过程是什么样的呢？MVC模式的设计将应用的架构划分为相互独立的组件（模型、视图和控制器），这简化了测试的过程。其理念就是，将应用划分为这些组件以限定对控制器组件的单元测试。</p>
<p>换句话说，只需要测试MVC中的控制器类，这就足够了。</p>
<p>控制器是纯粹的程序类，与数据模型和视图（数据层和用户接口层）完全独立。这种独立性可以使你通过测试控制器来测试应用程序。</p>
<p>下一篇将讨论如何测试控制器。</p>
<p><span style="color: #0080ff;"><strong>ASP.NET MVC Preview 2中改进的测试特性</strong></span></p>
<p>ASP.NET MVC的新版本（Preview 2）（目前的最新版本为ASP.NET MVC 1.0 RC）提供了一些新的特性帮助开发者改善测试能力。一个主要的改变就是可以在创建ASP.NET MVC项目的同时创建集成的单元测试项目。在此之前，你只能先创建ASP.NET MVC项目，然后再手工创建单元测试项目。而现在Visual Studio会询问你是否新建Visual Studio单元测试项目。</p>
<p>如图2所示。在创建ASP.NET MVC项目之后，将会显示该对话框询问你是否创建测试项目。</p>
<p>图2</p>
<p><img border="0" width="590" src="http://aspalliance.com/ArticleFiles/1630/image002.gif" height="405" /></p>
<p>当然，你可以忽略它，不创建单元测试项目。</p>
<p>另一方面，Visual Studio测试框架（MsTest）并不是唯一的选择。许多职业程序员选择其他测试框架如NUnit、MbUnit，他们都是.NET开发者所熟知的。</p>
<p><span style="color: #0080ff;"><strong>其它部分</strong></span></p>
<ul>
<li><a href="/kirinboy/archive/2008/01/30/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_1.html">Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 1</a> </li>
<li><a href="/kirinboy/archive/2008/02/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_2.html">Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 2</a> </li>
<li><a href="/kirinboy/archive/2008/03/24/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_3.html">Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 3</a></li>
</ul>
<p><span style="color: #0080ff;"><strong>小结</strong></span></p>
<p>本系列的第四部分全部是关于单元测试的概念，它使得在ASP.NET MVC应用中进行测试驱动开发成为可能。这也是MVC模式的主要优点之一。</p>
<p>我先介绍了在MVC模式中进行单元测试的重要性，然后简要介绍了单元测试以及主要目标，然后深入创建ASP.NET MVC Framework的过程。这之后，讨论了抽象和隔离的必要性，以及在ASP.NET MVC Framework中是如何做到这一点的。</p>
<p>在这篇文章中，我没有涉及到任何关于KBlog，有关该理论概念的所有应用程序也留在了下一篇中。在使用MVC模式以及ASP.NET MVC Framework时，对单元测试概念的理解是很重要的部分。</p>
<p>在本系列的下一篇中，我将对KBlog中的控制器进行单元测试，对单元测试的理论进行实践。</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1400916.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47989/" target="_blank">Twitter无处不在 魔兽世界Twitter发送器插件发布</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>用DataBindings属性绑定控件的值</title><link>http://www.cnblogs.com/kirinboy/archive/2009/02/05/1384684.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Thu, 05 Feb 2009 08:04:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2009/02/05/1384684.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1384684.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2009/02/05/1384684.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1384684.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1384684.html</trackback:ping><description><![CDATA[摘要: &nbsp;&nbsp;<a href='http://www.cnblogs.com/kirinboy/archive/2009/02/05/1384684.html'>阅读全文</a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1384684.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47988/" target="_blank">Firefox 3.5匆忙推出漏洞多 Mozilla本月将更新</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>OpenFileDialog会改变FileInfo的默认路径吗？</title><link>http://www.cnblogs.com/kirinboy/archive/2008/11/26/1341210.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Wed, 26 Nov 2008 02:46:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/11/26/1341210.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1341210.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/11/26/1341210.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1341210.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1341210.html</trackback:ping><description><![CDATA[<p>今天在博客园的QQ群上有人发现了这样一个问题，即用FileInfo fi = new FileInfo(path)默认的fi.DirectoryName为当前应用程序所在目录，但如果用OpenFileDialog打开某文件的话，再次fi = new FileInfo(path)，其fi.DirectoryName变为OpenFileDialog所打开的文件所在的路径。</p>
<p>例如：</p>
<pre class="csharpcode">FileInfo fi = <span class="kwrd">new</span> FileInfo(<span class="str">"test.txt"</span>);
MessageBox.Show(fi.DirectoryName);
openFileDialog1.ShowDialog();
fi = <span class="kwrd">new</span> FileInfo(<span class="str">"test.txt"</span>);
MessageBox.Show(fi.DirectoryName);</pre>
<p>第一次弹出的路径为</p>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/OpenFileDialogFileInfo_916B/1_4.jpg"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="80" alt="1" src="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/OpenFileDialogFileInfo_916B/1_thumb_1.jpg" width="244" border="0" /></a> </p>
<p>然后弹出OpenFileDialog窗口，打开其他路径中的一个文件，弹出的路径为</p>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/OpenFileDialogFileInfo_916B/2_2.jpg"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="86" alt="2" src="http://www.cnblogs.com/images/cnblogs_com/kirinboy/WindowsLiveWriter/OpenFileDialogFileInfo_916B/2_thumb.jpg" width="244" border="0" /></a> </p>
<p>如果在声明FileInfo时使用绝对路径，则不会出现这样的问题。</p>
<p>是不是用相对路径声明FileInfo时，会产生一个默认路径，而OpenFileDialog改变了这个默认路径呢？</p>
<p>答案是肯定的。FileDialog控件提供了一个RestoreDirectory属性，用来指示对话框在关闭前是否还原当前目录。代码如下：</p>
<p>FileInfo fi = <span class="kwrd">new</span> FileInfo(<span class="str">"test.txt"</span>);<br />
MessageBox.Show(fi.DirectoryName);<br />
openFileDialog1.RestoreDirectory = true;<br />
openFileDialog1.ShowDialog();<br />
fi = <span class="kwrd">new</span> FileInfo(<span class="str">"test.txt"</span>);<br />
MessageBox.Show(fi.DirectoryName);</p>
<p>这样，第二次弹出的对话框就与第一次相同了。</p>
<p>此外，即使设置了FileDialog控件的InitialDerectory，只要RestoreDirectory设置为true，用相对路径初始化FileInfo时，默认的工作路径均为当前程序所在的路径。</p>
<img src ="http://www.cnblogs.com/kirinboy/aggbug/1341210.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47987/" target="_blank">预测：Twitter最可能收购的十家公司</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>【翻译】Data Access with LINQ to SQL (1) -- New C# and VB.NET Language Features</title><link>http://www.cnblogs.com/kirinboy/archive/2008/08/13/Data_Access_with_LINQ_to_SQL_1.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Wed, 13 Aug 2008 07:32:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/08/13/Data_Access_with_LINQ_to_SQL_1.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1266986.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/08/13/Data_Access_with_LINQ_to_SQL_1.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1266986.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1266986.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p><em></em>&nbsp; </p>
<p><em>该系列翻译自《ASP.NET Unleashed 3.5》第18章内容</em> </p>
<p>&nbsp; </p>
<p>在.NET Framework 3.5的众多新特性之中，LINQ to SQL是最重要的一个。实际上，它也许意味着自SQL诞生以来，应用程序与数据库的结合方式上最重大的一次变革。 </p>
<p>长期以来，程序员处理未持久化数据（应用程序）和持久化数据（数据库）的方式有着天壤之别：在应用程序中，我们使用对象和属性（用C#或VB.NET创建）；而在大多数数据库中，我们使用表和字段。 </p>
<p>不管我们的应用程序和数据库是否描述非常类似的数据，这都是事实。例如，你可能会拥有一个类和一个表，其名称均为Product，代表你的Web站点所销售的产品列表。尽管如此，在这些实体之间进行交互的语言（C#、VB.NET与SQL语言）却不尽相同。很多大公司都会拥有不同的开发人员，有的擅长C#或VB.NET，有的则专攻SQL。 </p>
<p>程序员要花费惊人的时间来转换对象和关系型世界，而这项工作是机械和乏味的。每当我想起花费在声明这些类（包含数据库字段到属性的映射）上的时间时，我都会不寒而栗。而这些时间我本可以用来陪孩子们逛公园、看电影或遛狗，等等。 </p>
<p>LINQ to SQL的诞生使得我们可以对SQL宣判死刑。或者更准确地说，它使得SQL语言走向幕后，我们再也不必使用SQL了。这是件好事。SQL已死！ </p>
<p>本章是较难的一章。LINQ to SQL并非很容易理解的概念，它依赖于C#、VB.NET以及.NET Framework中引入的一些新特性，而这些特性是不易掌握的。所以，请保持耐心，做个深呼吸。我保证最后一切都会清晰起来。 </p>
<p>本章分为4部分。在第一部分中，我们讨论C#、VB.NET和.NET Framework 3.5引入的支持LINQ的新特性。然后，你将学习如何使用LINQ to SQL实体描述数据库表。接下来，我解释如何使用LINQ to SQL执行标准SQL命令，如SELECT、INSERT、UPDATE和DELETE命令。在本章的最后部分，我将示范如何创建自定义实体类（包含数据有效性验证）。 </p>
<h3><strong>C#和VB.NET的新特性</strong></h3>
<p>微软公司为C#和VB.NET引入了诸多新的语言特性，以支持LINQ to SQL工作。很多特性都使得C#和VB.NET的行为更像动态语言（如JavaScript）。尽管引入的主要动机是支持LINQ，这些新特性本身还是非常有趣的。 </p>
<p><em>注意：要使用这些新特性，你需要使Web站点面向.NET Framework 3.5，确保项目中包含web.config文件。然后选择菜单选项Website&agrave;Start Options&agrave;Build，在Target Framework中选择.NET Framework 3.5。执行这些步骤将会修改你的web.config文件，使其引用必要的程序集并使用正确的C#或VB.NET版本。</em> </p>
<h4><strong>理解自动属性</strong></h4>
<p>我们将探索的第一个新语言特性叫做自动属性（Automatic Properties）。不幸的是，只有C#支持该特性，VB.NET并不支持。 </p>
<p>自动属性提供了定义新属性的快速方法。如代码清单18-1所示，类Product包含了Id、Description和Price属性。 </p>
<p>代码清单 18-1 LanguageChanges\App_Code\AutomaticProperties.cs </p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> AutomaticProperties
{
<span class="rem">// Automatic Properties</span>
<span class="kwrd">public</span> <span class="kwrd">int</span> Id { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> Description { get; set; }
<span class="rem">// Normal Property</span>
<span class="kwrd">private</span> <span class="kwrd">decimal</span> _Price;
<span class="kwrd">public</span> <span class="kwrd">decimal</span> Price
{
get { <span class="kwrd">return</span> _Price; }
set { _Price = <span class="kwrd">value</span>; }
}
}</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>注意前两个属性Id和Description，没有使用Getter和Setter，这与最后一个属性Price不同。C#编译器将为你自动创建Getter和Setter，以及与属性对应的私有字段。 </p>
<p>你不能向自动属性中的Getter和Setter添加任何逻辑，也不能创建只读的自动属性。 </p>
<p>自动属性怎么会和LINQ to SQL有关呢？在使用LINQ to SQL时，你为了得到数据的结构（类似SQL查询时使用select语句得到的列表），经常将类设计为仅包含数据库表的各个字段。因此，你肯定希望使用最小的工作量来创建属性列表，自动属性就是为此量身定做的。 </p>
<p><em>注意：使用Visual Web Developer或Visual Studio可以向类或页面中快速添加自动属性，你只需输入&ldquo;prop&rdquo;并按Tab键两次。</em> </p>
<h3><strong>理解初始化器</strong></h3>
<p>使用初始化器（Initializers）可以减少实例化类的工作量。例如，假设你拥有如代码清单18-2（C#）或代码清单 18-3中的类（VB.NET）。 </p>
<p>代码清单 18-2 LanguageChanges\App_Code\Product.cs </p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">class</span> Product
{
<span class="kwrd">public</span> <span class="kwrd">int</span> Id { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> Name { get; set; }
<span class="kwrd">public</span> <span class="kwrd">decimal</span> Price { get; set; }
}</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>代码清单 18-3 LanguageChanges\App_Code\Product.vb </p>
<pre class="csharpcode"><span class="kwrd">Public</span> <span class="kwrd">Class</span> Product
<span class="kwrd">Private</span> _Id <span class="kwrd">As</span> <span class="kwrd">Integer</span>
<span class="kwrd">Public</span> <span class="kwrd">Property</span> Id() <span class="kwrd">As</span> <span class="kwrd">Integer</span>
<span class="kwrd">Get</span>
<span class="kwrd">Return</span> _Id
<span class="kwrd">End</span> <span class="kwrd">Get</span>
<span class="kwrd">Set</span>(<span class="kwrd">ByVal</span> value <span class="kwrd">As</span> <span class="kwrd">Integer</span>)
_Id = value
<span class="kwrd">End</span> <span class="kwrd">Set</span>
<span class="kwrd">End</span> <span class="kwrd">Property</span>
<span class="kwrd">Private</span> _Name <span class="kwrd">As</span> <span class="kwrd">String</span>
<span class="kwrd">Public</span> <span class="kwrd">Property</span> Name() <span class="kwrd">As</span> <span class="kwrd">String</span>
<span class="kwrd">Get</span>
<span class="kwrd">Return</span> _Name
<span class="kwrd">End</span> <span class="kwrd">Get</span>
<span class="kwrd">Set</span>(<span class="kwrd">ByVal</span> value <span class="kwrd">As</span> <span class="kwrd">String</span>)
_Name = value
<span class="kwrd">End</span> <span class="kwrd">Set</span>
<span class="kwrd">End</span> <span class="kwrd">Property</span>
<span class="kwrd">Private</span> _Price <span class="kwrd">As</span> <span class="kwrd">Decimal</span>
<span class="kwrd">Public</span> <span class="kwrd">Property</span> Price() <span class="kwrd">As</span> <span class="kwrd">Decimal</span>
<span class="kwrd">Get</span>
<span class="kwrd">Return</span> _Price
<span class="kwrd">End</span> <span class="kwrd">Get</span>
<span class="kwrd">Set</span>(<span class="kwrd">ByVal</span> value <span class="kwrd">As</span> <span class="kwrd">Decimal</span>)
_Price = value
<span class="kwrd">End</span> <span class="kwrd">Set</span>
<span class="kwrd">End</span> <span class="kwrd">Property</span>
<span class="kwrd">End</span> <span class="kwrd">Class</span>
</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>Product类包含3个公共属性（由于C#有自动属性的优势，因此C#示例采用自动属性进行定义，而VB.NET示例采用普通方式进行定义）。 </p>
<p>现在，你需要创建一个Product类的实例，在.NET Framework 2.0中，采用以下方式（C#）： </p>
<pre class="csharpcode">Product product1 = <span class="kwrd">new</span> Product();
product1.Id = 1;
product1.Name = &ldquo;Laptop Computer&rdquo;;
product1.Price = 800.00m;</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>VB.NET采用以下方式： </p>
<pre class="csharpcode"><span class="kwrd">Dim</span> product1 <span class="kwrd">As</span> <span class="kwrd">New</span> Product()
product1.Id = 1
product1.Name = &ldquo;Laptop Computer&rdquo;
product1.Price = 800.0</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>注意，初始化一个简单的Product类使用了4行代码，这太浪费了。利用初始化器，这些工作可以用一行代码完成。用C#使用初始化器的代码如下： </p>
<p>Product product2 = new Product {Id=1, Name=&rdquo;Laptop Computer&rdquo;, Price=800.00m}; </p>
<p>用VB.NET使用初始化器的代码如下： </p>
<p>Dim product2 As New Product() With {.Id = 1, .Name = &ldquo;Laptop Computer&rdquo;,.Price = 800.0} </p>
<p>在.NET Framework 2.0中，你可以声明Product类的构造函数，包含Id、Name和Price作为参数。但由于还需要在构造函数中将参数值赋给属性，因此导致了代码的膨胀。使用初始化器，你显然可以做类似的工作。并且，你还因此得到了好处：更加小巧的类，以及声明该类时使用的最少的代码。 </p>
<h4><strong>理解类型推断</strong></h4>
<p>一个新特性使得C#和VB.NET看上去非常像JavaScript之类的动态语言，这个特性就是局部变量类型推断（Type Inference）。当使用类型推断时，你允许C#或VB.NET编译器在编译时确定变量类型。 </p>
<p>下面的代码介绍了在C#中如何使用类型推断： </p>
<p>var message = &ldquo;Hello World!&rdquo;; </p>
<p>在VB.NET中，使用如下代码： </p>
<p>Dim message = &ldquo;Hello World!&rdquo; </p>
<p>注意，变量message在声明时并没有为其指定任何类型。C#和VB.NET编译器会根据你初始化变量时的值来推断变量的类型（这里为String类型）。 </p>
<p>使用类型推断并没有性能上的损失（变量并非后期绑定）。编译器在编译时就判断出了变量的数据类型。 </p>
<p>为了支持类型推断，C#引入了一个新的关键字：var。当你希望编译器自行判断变量的数据类型时，就可以使用var类型声明变量。 </p>
<p>当你为局部变量提供初始值时才能使用类型推断。例如，下面的代码不会执行（C#）： </p>
<p>var message; </p>
<p>message = &ldquo;Hello World!&rdquo;; </p>
<p>由于message变量在声明时没有初始化，这段代码将不能通过编译（C#编译器）。 </p>
<p>下面的代码在VB.NET中可以执行（但它做的并不是你想要的）： </p>
<p>Dim message </p>
<p>message = &ldquo;Hello World!&rdquo; </p>
<p>在这种情况下，VB.NET认为message变量为一个Object类型。由于将字符串赋给了变量，在运行时将会把变量的值转换为String类型。从性能角度看，这并不是一个好的做法。 </p>
<p><em>注意：VB.NET 9.0中有一个新的Option叫做Option Infer。要想使隐含类型（implicit typing）正确工作，必须激活Option Infer。你可以在代码文件的最顶端加入&ldquo;Option Infer On&rdquo;来激活该类的Option Infer。</em> </p>
<p>下一节将介绍类型推断与LINQ to SQL的关系。在使用LINQ to SQL时，很多情况下都无法确定变量的类型，因此你需要编译器能够进行推断。 </p>
<h4><strong>理解匿名类型</strong></h4>
<p>另一个类似于动态语言中你可能比较熟悉的概念是匿名类型（Anonymous Types）。当你需要创建一个临时类型（type），却并不想创建一个类（class）时，可以使用匿名类型。 </p>
<p>下面的代码介绍了如何在C#中创建匿名类型： </p>
<p>var customer = new {FirstName = &ldquo;Stephen&rdquo;, LastName = &ldquo;Walther&rdquo;}; </p>
<p>在VB.NET中创建同样的匿名类型，可以使用下面的代码： </p>
<p>Dim customer = New With {.FirstName = &ldquo;Stephen&rdquo;, .LastName = &ldquo;Walther&rdquo;} </p>
<p>注意，customer变量并没有指定类型（这与JavaScript或VBScript非常类似）。尽管如此，customer仍然具有它的类型，你只是不知道它的名字而已：它是匿名的，理解这一点是十分重要的。 </p>
<p>仅仅一行代码，我们既创建了一个新的类，又实例化了它的属性。其简洁性让我感动到内伤。 </p>
<p>在使用LINQ to SQL时，匿名类型十分有用。因为你会发现你经常需要实时地（on the fly）创建一些新类型。例如，当执行一个查询时，你也许希望返回一个类，来代表一些数据库字段的集合。你将需要创建一个包含这些字段的临时类。 </p>
<h4><strong>理解泛型</strong></h4>
<p>是的，我知道泛型（Generics）并不是.NET 3.5的新特性。但是，它对LINQ to SQL来说是相当重要的部分，值得我们花点篇幅来回顾。 </p>
<p>注意： </p>
<p>要使用泛型，你需要引入System.Collections.Generic命名空间。 </p>
<p>我使用泛型的绝大多数情况，是由于泛型集合。例如，如果想描述一个字符串列表，你可以这样声明（C#）： </p>
<p>List&lt;string&gt; stuffToBuy = new List&lt;string&gt;(); </p>
<p>stuffToBuy.Add(&ldquo;socks&rdquo;); </p>
<p>stuffToBuy.Add(&ldquo;beer&rdquo;); </p>
<p>stuffToBuy.Add(&ldquo;cigars&rdquo;); </p>
<p>使用VB.NET，则这样声明： </p>
<p>Dim stuffToBuy As New List(Of String) </p>
<p>stuffToBuy.Add(&ldquo;socks&rdquo;) </p>
<p>stuffToBuy.Add(&ldquo;beer&rdquo;) </p>
<p>stuffToBuy.Add(&ldquo;cigars&rdquo;) </p>
<p>现在，利用集合初始化器，你可以仅用一行代码就声明一个强类型的字符串列表（C#）： </p>
<p>List&lt;string&gt; stuffToBuy2 = new List&lt;string&gt; {&ldquo;socks&rdquo;, &ldquo;beer&rdquo;, &ldquo;cigars&rdquo;}; </p>
<p><em>注意：VB.NET并不支持集合或数组初始化器。</em> </p>
<p>List类是泛型类，因为在声明的时候指定了它要包含的对象的类型。C#中，要在尖括号之间（&lt; &gt;）指定类型，而在VB.NET中要使用Of关键字。在上例中，我们创建了一个包含字符串的List类。同样地 ，我们也可以创建包含整型或其他自定义类型（如Product和Customer类，分别代表产品和顾客）的List类。 </p>
<p>因为泛型是强类型的，因此泛型集合（如List）优于非泛型集合（如ArrayList）。ArrayList将所有对象都保存为object，而泛型将所有对象保存为它们特定的类型。当从ArrayList中取出一个对象时，在使用该对象前你必须将其转换为特定类型。而从泛型中取出对象则不需要这种转换。 </p>
<p>泛型并不仅仅局限于集合。你可以创建泛型方法、泛型类以及泛型接口。 </p>
<p>例如，当使用ADO.NET时，我喜欢将data reader转换为强类型的List集合。如Listing 18.4所示，GetListFromCommand()方法包含一个command对象，执行该对象，然后自动生成一个强类型的List。 </p>
<p>代码清单 18-4 LanguageChanges\App_Code\GenericMethods.cs </p>
<pre class="csharpcode"><span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Data.SqlClient;
<span class="kwrd">public</span> <span class="kwrd">class</span> GenericMethods
{
<span class="kwrd">public</span> <span class="kwrd">static</span> List&lt;T&gt; GetListFromCommand&lt;T&gt;(SqlCommand command)
<span class="kwrd">where</span> T: ICreatable, <span class="kwrd">new</span>()
{
List&lt;T&gt; results = <span class="kwrd">new</span> List&lt;T&gt;();
<span class="kwrd">using</span> (command.Connection)
{
command.Connection.Open();
SqlDataReader reader = command.ExecuteReader();
<span class="kwrd">while</span> (reader.Read())
{
T newThing = <span class="kwrd">new</span> T();
newThing.Create(reader);
results.Add(newThing);
}
}
<span class="kwrd">return</span> results;
}
}
<span class="kwrd">public</span> <span class="kwrd">interface</span> ICreatable
{
<span class="kwrd">void</span> Create(SqlDataReader reader);
}</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>代码清单18-4中的GetListFromCommand()方法接收一个SqlCommand对象并且返回一个泛型List&lt;T&gt;。where子句用来约束泛型类型。泛型约束使得类型T必须实现ICreatable接口并且可以通过new实例化（准确的意思应为，必须包含无参的构造函数。&mdash;&mdash;译者注）。 </p>
<p>代码清单18-4中同样定义了ICreatable接口，它要求类实现Create()方法。 </p>
<p>既然我们创建了可以将data reader 转换为强类型的list的泛型方法，那么我们就可以用它处理任何实现了ICreatable接口的类，如代码清单18-5中的Movie类。 </p>
<p>代码清单 18-5 Movie.cs </p>
<pre class="csharpcode"><span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Data.SqlClient;
<span class="kwrd">public</span> <span class="kwrd">class</span> Movie : ICreatable
{
<span class="kwrd">public</span> <span class="kwrd">int</span> Id { get; set; }
<span class="kwrd">public</span> <span class="kwrd">string</span> Title { get; set; }
<span class="kwrd">public</span> <span class="kwrd">void</span> Create(SqlDataReader reader)
{
Id = (<span class="kwrd">int</span>)reader[&ldquo;Id&rdquo;];
Title = (<span class="kwrd">string</span>)reader[&ldquo;Title&rdquo;];
}
}</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>你可以通过下面的方法调用GetListFromCommand()方法（随书CD中的ShowGenericMethods.aspx页面使用了该代码）： </p>
<pre class="csharpcode"><span class="kwrd">string</span> conString = WebConfigurationManager.ConnectionStrings[&ldquo;con&rdquo;].ConnectionString;
SqlConnection con = <span class="kwrd">new</span> SqlConnection(conString);
SqlCommand cmd = <span class="kwrd">new</span> SqlCommand(&ldquo;SELECT Id, Title FROM Movie&rdquo;, con);
List&lt;Movie&gt; movies = GenericMethods.GetListFromCommand&lt;Movie&gt;(cmd);</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>在这里，泛型的出色之处在于，你不必为每个类型编写相同的代码将data reader 转换为泛型List。你只编写了一个泛型方法GetListFromCommand()，却可以用该方法转换任何复合泛型约束的类型。 </p>
<p>理解泛型的正确方法是理解代码模板。你可以使用泛型来定义某一代码模式，而将一个特定的类应用与该模式中。 </p>
<h4><strong>理解Lambda表达式</strong></h4>
<p>Lambda表达式是.NET Framework 3.5引入的另一个新特性，它提供了一种极其简洁的定义方法的方式。 </p>
<p>假设你希望将Click事件处理器正确地绑定到button控件，代码清单18-6所示为其中一种方法。 </p>
<p>代码清单 18-6 LanguageChanges\NormalMethod.aspx </p>
<pre class="csharpcode"><span class="asp">&lt;%@ Page Language=&rdquo;C#&rdquo; %&gt;</span>
<span class="kwrd">&lt;!</span><span class="html">DOCTYPE</span> <span class="attr">html</span> <span class="attr">PUBLIC</span> &ldquo;<span class="attr">-</span>//<span class="attr">W3C</span>//<span class="attr">DTD</span> <span class="attr">XHTML</span> <span class="attr">1</span>.<span class="attr">0</span> <span class="attr">Transitional</span>//<span class="attr">EN</span>&rdquo;
&ldquo;<span class="attr">http:</span>//<span class="attr">www</span>.<span class="attr">w3</span>.<span class="attr">org</span>/<span class="attr">TR</span>/<span class="attr">xhtml1</span>/<span class="attr">DTD</span>/<span class="attr">xhtml1-transitional</span>.<span class="attr">dtd</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">script</span> <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">void</span> Page_Init()
{
btn.Click += <span class="kwrd">new</span> EventHandler(btn_Click);
}
<span class="kwrd">void</span> btn_Click(<span class="kwrd">object</span> sender, EventArgs e)
{
lblResult.Text = DateTime.Now.ToString();
}
<span class="kwrd">&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span>=&rdquo;<span class="attr">http:</span>//<span class="attr">www</span>.<span class="attr">w3</span>.<span class="attr">org</span>/<span class="attr">1999</span>/<span class="attr">xhtml</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">head</span> <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Normal Method<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">head</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">form</span> <span class="attr">id</span>=&rdquo;<span class="attr">form1</span>&rdquo; <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">asp:Button</span> <span class="attr">id</span>=&rdquo;<span class="attr">btn</span>&rdquo; <span class="attr">Text</span>=&rdquo;<span class="attr">Go</span>!&rdquo; <span class="attr">Runat</span>=&rdquo;<span class="attr">server</span>&rdquo; <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;</span><span class="html">asp:Label</span> <span class="attr">id</span>=&rdquo;<span class="attr">lblResult</span>&rdquo;  <span class="attr">Runat</span>=&rdquo;<span class="attr">server</span>&rdquo; <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">form</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span></pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>在代码清单18-6中，Page_Init()方法将btn_Click()方法绑定到Button的Click事件。当点击按钮时，执行btn_Click()方法，显示当前日期和时间。这没什么特别之处。 </p>
<p>.NET Framework 2.0引入了匿名方法的概念，其好处是可以声明内联方法。例如，代码清单18-7的功能与上例相同，所不同的是使用了匿名方法来处理Button的Click事件。 </p>
<p>代码清单 18-7 LanguageChanges\AnonymousMethod.aspx </p>
<pre class="csharpcode"><span class="asp">&lt;%@ Page Language=&rdquo;C#&rdquo; %&gt;</span>
<span class="kwrd">&lt;!</span><span class="html">DOCTYPE</span> <span class="attr">html</span> <span class="attr">PUBLIC</span> &ldquo;<span class="attr">-</span>//<span class="attr">W3C</span>//<span class="attr">DTD</span> <span class="attr">XHTML</span> <span class="attr">1</span>.<span class="attr">0</span> <span class="attr">Transitional</span>//<span class="attr">EN</span>&rdquo;
&ldquo;<span class="attr">http:</span>//<span class="attr">www</span>.<span class="attr">w3</span>.<span class="attr">org</span>/<span class="attr">TR</span>/<span class="attr">xhtml1</span>/<span class="attr">DTD</span>/<span class="attr">xhtml1-transitional</span>.<span class="attr">dtd</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">script</span> <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">void</span> Page_Init()
{
btn.Click += <span class="kwrd">delegate</span>(<span class="kwrd">object</span> sender, EventArgs e)
{
lblResult.Text = DateTime.Now.ToString();
};
}
<span class="kwrd">&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span>=&rdquo;<span class="attr">http:</span>//<span class="attr">www</span>.<span class="attr">w3</span>.<span class="attr">org</span>/<span class="attr">1999</span>/<span class="attr">xhtml</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">head</span> <span class="attr">id</span>=&rdquo;<span class="attr">Head1</span>&rdquo; <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Anonymous Method<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">head</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">form</span> <span class="attr">id</span>=&rdquo;<span class="attr">form1</span>&rdquo; <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">asp:Button</span> <span class="attr">id</span>=&rdquo;<span class="attr">btn</span>&rdquo;<span class="attr">Text</span>=&rdquo;<span class="attr">Go</span>!&rdquo;<span class="attr">Runat</span>=&rdquo;<span class="attr">server</span>&rdquo; <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;</span><span class="html">asp:Label</span> <span class="attr">id</span>=&rdquo;<span class="attr">lblResult</span>&rdquo;<span class="attr">Runat</span>=&rdquo;<span class="attr">server</span>&rdquo; <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">form</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span></pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>在代码清单18-7中，处理Click事件的方法是在Page_Init()方法之中声明的。 </p>
<p><em>注意：VB.NET并不支持匿名方法，但它支持Lambda表达式，所以VB.NET使用者请不要跳过。</em> </p>
<p>Lambda表达式将匿名方法的概念更进一步，它将声明一个方法所必需的语法数量降到了最低。代码清单18-8使用了Lambda表达式，它功能与以上两个例子相同。 </p>
<p>代码清单 18-8 LanguageChanges\LambdaExpression.aspx </p>
<pre class="csharpcode"><span class="asp">&lt;%@ Page Language=&rdquo;C#&rdquo; %&gt;</span>
<span class="kwrd">&lt;!</span><span class="html">DOCTYPE</span> <span class="attr">html</span> <span class="attr">PUBLIC</span> &ldquo;<span class="attr">-</span>//<span class="attr">W3C</span>//<span class="attr">DTD</span> <span class="attr">XHTML</span> <span class="attr">1</span>.<span class="attr">0</span> <span class="attr">Transitional</span>//<span class="attr">EN</span>&rdquo;
&ldquo;<span class="attr">http:</span>//<span class="attr">www</span>.<span class="attr">w3</span>.<span class="attr">org</span>/<span class="attr">TR</span>/<span class="attr">xhtml1</span>/<span class="attr">DTD</span>/<span class="attr">xhtml1-transitional</span>.<span class="attr">dtd</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">script</span> <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">void</span> Page_Init()
{
btn.Click += (sender, e) =&gt; lblResult.Text = DateTime.Now.ToString();
}
<span class="kwrd">&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span>=&rdquo;<span class="attr">http:</span>//<span class="attr">www</span>.<span class="attr">w3</span>.<span class="attr">org</span>/<span class="attr">1999</span>/<span class="attr">xhtml</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">head</span> <span class="attr">id</span>=&rdquo;<span class="attr">Head1</span>&rdquo; <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Lambda Expressions<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">head</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">form</span> <span class="attr">id</span>=&rdquo;<span class="attr">form1</span>&rdquo; <span class="attr">runat</span>=&rdquo;<span class="attr">server</span>&rdquo;<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">asp:Button</span> <span class="attr">id</span>=&rdquo;<span class="attr">btn</span>&rdquo;<span class="attr">Text</span>=&rdquo;<span class="attr">Go</span>!&rdquo;<span class="attr">Runat</span>=&rdquo;<span class="attr">server</span>&rdquo; <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;</span><span class="html">asp:Label</span> <span class="attr">id</span>=&rdquo;<span class="attr">lblResult</span>&rdquo;<span class="attr">Runat</span>=&rdquo;<span class="attr">server</span>&rdquo; <span class="kwrd">/&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">form</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span></pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>代码清单18-8中的Lambda表达式如下， </p>
<p>(sender, e) =&gt; lblResult.Text = DateTime.Now.ToString(); </p>
<p>这仅仅是编写方法的简洁方式。Lambda表达式使用=&gt;操作符（goes into操作符）来分隔方法的参数列表和方法体。编译器（通常）可以推断出参数的数据类型。尽管如此，如果你愿意，还是可以像下面的代码那样指明参数类型， </p>
<p>(object sender, EventArgs e) =&gt; lblResult.Text = DateTime.Now.ToString(); </p>
<p>有必要提一下，当方法只有一个参数时，圆括号是可选的。因此，Lambda表达式可以非常简洁。 </p>
<p>Visual Basic也支持Lambda表达式，但多了一些限制。Visual Basic中的Lambda表达式中只能包含表达式，不能包含语句。 </p>
<p>VB中创建Lambda表达式的语法如下， </p>
<p>Dim AddNumbers = Function(x, y) x + y </p>
<p>Response.Write(AddNumbers(5, 6)) </p>
<p>第一条语句创建了一个名为AddNumbers的变量，这即为一个Lambda表达式。VB语法Function(x, y) x + y相当于C#语法(x, y) =&gt; x + y。第二条语句使用两个参数调用Lambda表达式。 </p>
<h4><strong>理解扩展方法</strong></h4>
<p>扩展方法的概念对于使用过JavaScript（考虑prototype）的人来说，也应该是非常熟悉的。 </p>
<p>使用扩展方法，你可以向一个已有类中添加新的方法。例如，你可以创建任意一个方法，并将它添加到String类中。 </p>
<p>由于害怕JavaScript注入攻击，我一直以来都对字符串进行HTML编码。在.NET Framework 2.0中，可以调用静态方法Server.HtmlEncode()来对字符串进行HTML编码，如下， </p>
<p>string evilString = &ldquo;&lt;script&gt;alert(&lsquo;boom!&rsquo;)&lt;&rdquo; + &ldquo;/script&gt;&rdquo;; </p>
<p>ltlMessage.Text = Server.HtmlEncode(evilString); </p>
<p>在这段代码中，调用了Server类中的静态方法HtmlEncode()。如果我们可以向下面这样，直接调用字符串的HtmlEncode()方法，岂不妙哉 </p>
<p>string evilString = &ldquo;&lt;script&gt;alert(&lsquo;boom!&rsquo;)&lt;&rdquo; + &ldquo;/script&gt;&rdquo;; </p>
<p>ltlMessage.Text = evilString.HtmlEncode(); </p>
<p>使用扩展方法，就可以这么做。我们可以向喜欢的类中添加任何方法。创建扩展方法，首先要创建一个静态类，并创建一个第一个参数为特殊参数的静态方法。代码清单18-9向String类中添加HtmlEncode()方法，以此描述了如何创建扩展方法。 </p>
<p>代码清单 18-9 LanguageChanges\MyExtensions.vb<a name="_ftnref2_7527" href="#_ftn2_7527">[2]</a> </p>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> MyExtensions
{
<span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">string</span> HtmlEncode(<span class="kwrd">this</span> <span class="kwrd">string</span> str)
{
<span class="kwrd">return</span> System.Web.HttpUtility.HtmlEncode(str);
}
}</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>注意，HtmlEncode()方法中仅有的参数前面多了关键字this。这样的参数指明了扩展方法所应用的类型。 </p>
<p>在VB.NET中创建扩展方法与在C#中极为类似。代码清单18-10中的HtmlEncode()方法与上面的功能相同。 </p>
<p>代码清单 18-10 LanguageChanges\MyExtensions.cs<a name="_ftnref3_7527" href="#_ftn3_7527">[3]</a> </p>
<pre class="csharpcode"><span class="kwrd">Imports</span> System.Runtime.CompilerServices
<span class="kwrd">Public</span> <span class="kwrd">Module</span> MyExtensions
&lt;Extension()&gt; _
<span class="kwrd">Public</span> <span class="kwrd">Function</span> HtmlEncode(<span class="kwrd">ByVal</span> str <span class="kwrd">As</span> <span class="kwrd">String</span>) <span class="kwrd">As</span> <span class="kwrd">String</span>
<span class="kwrd">Return</span> System.Web.HttpUtility.HtmlEncode(str)
<span class="kwrd">End</span> <span class="kwrd">Function</span>
<span class="kwrd">End</span> Module</pre>
<p>
<style type="text/css"><!--
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
--></style>
</p>
<p>当使用VB.NET时，必须将扩展方法声明在一个module中。另外，还必须标记为System.Runtime.CompilerServices.Extension属性。 </p>
<h4><strong>理解LINQ</strong></h4>
<p>终于，我们要讨论最后一个话题LINQ了，在这之后我们就可以开始研究本章的真正内容&mdash;&mdash;LINQ to SQL了。 </p>
<p>LINQ是Language Integrated Query的简称，它由C#和VB.NET的一系列新特性组成，这些特性允许我们执行查询。LINQ使得SQL查询就像C#或VB.NET的语法一样简单。 </p>
<p>一个简单的LINQ查询示例如下， </p>
<p>var words = new List&lt;string&gt; {&ldquo;zephyr&rdquo;, &ldquo;apple&rdquo;, &ldquo;azure&rdquo;}; </p>
<p>var results = from w in words </p>
<p>where w.Contains(&ldquo;z&rdquo;) </p>
<p>select w; </p>
<p>第一条语句创建了一个泛型列表words，它包含三个字符串。第二条语句就是LINQ查询。 </p>
<p>LINQ查询及其像反向的SQL语句。它从列表中获得所有的包含字母z的单词。执行该查询，results变量将包含一下两个单词： </p>
<p>zephyr </p>
<p>azure </p>
<p>你可以对所有实现了IEnumerable&lt;T&gt;接口的对象执行标准的LINQ查询。这些实现了该接口的对象称为sequence。常用的sequence均为泛型List类或标准Array类（因此任何可以存入数组中的类，都可以使用LINQ进行查询）。 </p>
<p>C#语言提供了以下子句，供我们在查询中使用： </p>
<ul>
<li>from&mdash;&mdash;指定数据源以及用来迭代数据源的变量（范围变量）。</li>
<li>where&mdash;&mdash;过滤查询的结果。</li>
<li>select&mdash;&mdash;指定查询结果中的项。</li>
<li>group&mdash;&mdash;通过某一关键字，对相关的值进行聚合。</li>
<li>into&mdash;&mdash;存储聚合中的结果，或连接到一个临时变量。</li>
<li>orderby&mdash;&mdash;将查询结果按升序或降序进行排序。</li>
<li>join&mdash;&mdash;通过一个关键字，对两个数据源进行连接。</li>
<li>let&mdash;&mdash;创建一个临时变量，来存储子查询的结果。</li>
</ul>
<p>创建一个LINQ查询，类似于创建一个反向的SQL查询。LINQ查询以一个from子句开始，它指定了数据的位置。然后，指定where子句来过滤数据。最后，指定用来表示数据的select子句（决定你要返回的对象和属性）。 </p>
<p>在内部，标准LINQ查询被翻译成调用System.Linq.Enumerable类的方法。Enumerable类包含了一些扩展方法，这些扩展方法可以应用到任何实现了IEnumerable&lt;T&gt;接口的类中。 </p>
<p>因此，查询 </p>
<p>var results = from w in words </p>
<p>where w.Contains(&ldquo;z&rdquo;) </p>
<p>select w; </p>
<p>将被C#编译器翻译成下面的查询 </p>
<p>var results = words.Where( w =&gt; w.Contains(&ldquo;z&rdquo;) ).Select( w =&gt; w ); </p>
<p>第一个查询使用了查询语法（query syntax），第二个查询使用了方法语法（method syntax）。这两种查询是相同的。 </p>
<p>注意，使用方法语法的查询在Where()和Select()方法中允许使用Lambda表达式。Where()方法中的Lambda表达式用来过滤数据，只返回包含字母z的单词。Select()方法指明要返回的对象和属性。如果我们将Lambda表达式w=&gt;w.Length传递给Select()方法，该查询将返回每个单词的长度，而不是单词本身。 </p>
<p>在创建LINQ查询时，使用查询语法还是方法语法纯粹属于个人偏好。查询语法属于语言的特性（C#或VB.NET），方法语法和语言无关。 </p>
<p>我发现我使用方法语法的时候更多一些，因为查询语法不过是方法语法的子集。也就是说，使用方法语法可以做更多的事情。然而在某些情况下，使用方法语法编写查询会显得有些冗长。例如，使用查询语法编写LINQ to SQL左外连接，要比使用方法语法简单得多。 </p>
<p>最后，选择方法语法还是查询语法其实并不重要，因为所有的查询语法语句都将被编译器翻译成方法语法。在使用标准LINQ时，这些调用的方法都存在与Enumerable类中。 </p>
<p>在SDK文档中查找System.Linq.Enumerable类可以浏览Enumerable支持的全部方法。这里列举了一些有趣且实用的方法， </p>
<ul>
<li>Aggregate()&mdash;&mdash;对序列中的每一项执行同一个函数。</li>
<li>Average()&mdash;&mdash;返回序列中每一项的平均值。</li>
<li>Count()&mdash;&mdash;返回序列的总项数。</li>
<li>Distinct()&mdash;&mdash;返回序列中不同的项。</li>
<li>Max()&mdash;&mdash;返回序列中的最大值。</li>
<li>Min()&mdash;&mdash;返回序列中的最小值。</li>
<li>Select()&mdash;&mdash;返回序列中的某些项或属性。</li>
<li>Single()&mdash;&mdash;返回序列中的某个单一值。</li>
<li>Skip()&mdash;&mdash;跳过序列中指定数目的项并返回剩下的元素。</li>
<li>Take()&mdash;&mdash;返回序列中指定数目的元素</li>
<li>Where()&mdash;&mdash;过滤序列中的元素。</li>
</ul>
<p>本节我们讨论了标准LINQ（也叫LINQ to Objects）。LINQ使用了Provider Model，它有很多不同的实现，包括LINQ to SQL、LINQ to XML、LINQ over DataSets以及LINQ to Entities。LINQ也有很多第三方实现，包括LINQ to NHibernate和LINQ to SharePoint。这些不同的实现可以用来查询不同的数据源，如XML文件、SharePoint列表等等。 </p>
<p>在本章，我们仅讨论LINQ to SQL，它是微软专门为操作数据库数据而设计的官方版本。下面就让我们开始吧。</p>
<p>&nbsp;</p><img src ="http://www.cnblogs.com/kirinboy/aggbug/1266986.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47986/" target="_blank">网易澄清:与暴雪合资公司仅提供技术支持</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>ORA-01861: 文字与格式字符串不匹配</title><link>http://www.cnblogs.com/kirinboy/archive/2008/05/05/1183004.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 05 May 2008 03:16:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/05/05/1183004.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1183004.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/05/05/1183004.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1183004.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1183004.html</trackback:ping><description><![CDATA[摘要: &nbsp;&nbsp;<a href='http://www.cnblogs.com/kirinboy/archive/2008/05/05/1183004.html'>阅读全文</a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1183004.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47985/" target="_blank">杰克逊悼念仪式或成史上最大规模Web活动</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>.NET技术书籍推荐</title><link>http://www.cnblogs.com/kirinboy/archive/2008/03/25/dotNet_Books_Recommend.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Tue, 25 Mar 2008 08:48:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/03/25/dotNet_Books_Recommend.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1121536.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/03/25/dotNet_Books_Recommend.html#Feedback</comments><slash:comments>45</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1121536.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1121536.html</trackback:ping><description><![CDATA[摘要: 今天看到dudu号召大家推荐对自己最有帮助的技术图书，在下不才，几年间看过的书籍屈指可数，但有幸的是，所看的书都是经典中的经典。借此post向大家推荐一下。因为都是经典书，所以没什么新意，其他人推荐的也不外乎这几本。&nbsp;&nbsp;<a href='http://www.cnblogs.com/kirinboy/archive/2008/03/25/dotNet_Books_Recommend.html'>阅读全文</a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1121536.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47984/" target="_blank">《商业周刊》:Mozilla的志愿者开发模式被复制</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>【翻译】Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 3</title><link>http://www.cnblogs.com/kirinboy/archive/2008/03/24/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_3.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Mon, 24 Mar 2008 08:21:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/03/24/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_3.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1119913.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/03/24/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_3.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1119913.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1119913.html</trackback:ping><description><![CDATA[摘要: 在本系列的第三篇，Keyvan讨论了blog引擎中的数据模型，这些模型从Controller中获得数据并传递到View。他借助截图和代码，向我们展示了数据模型中LINQ方面的相关概念。&nbsp;&nbsp;<a href='http://www.cnblogs.com/kirinboy/archive/2008/03/24/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_3.html'>阅读全文</a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1119913.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47977/" target="_blank">Mono 的Virtual PC 虚拟机</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>【翻译】Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 2</title><link>http://www.cnblogs.com/kirinboy/archive/2008/02/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_2.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Fri, 01 Feb 2008 08:12:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/02/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_2.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1061507.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/02/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_2.html#Feedback</comments><slash:comments>17</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1061507.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1061507.html</trackback:ping><description><![CDATA[摘要: 在该ASP.NET MVC Framework系列文章的第二篇里，Keyvan介绍了如何在ASP.NET MVC中使用Controller。他在Blog系统添加了Controller并讨论了一些细节。首先介绍了URL Routing的概念，然后对Controller类进行深入剖析，最后研究了如何在简单Blog系统中实现Controller。&nbsp;&nbsp;<a href='http://www.cnblogs.com/kirinboy/archive/2008/02/01/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_2.html'>阅读全文</a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1061507.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47970/" target="_blank">19岁天才黑客发布首个iPhone 3GS破解软件</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item><item><title>【翻译】Building a Simple Blog Engine with ASP.NET MVC and LINQ - Part 1</title><link>http://www.cnblogs.com/kirinboy/archive/2008/01/30/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_1.html</link><dc:creator>麒麟.NET</dc:creator><author>麒麟.NET</author><pubDate>Wed, 30 Jan 2008 09:12:00 GMT</pubDate><guid>http://www.cnblogs.com/kirinboy/archive/2008/01/30/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_1.html</guid><wfw:comment>http://www.cnblogs.com/kirinboy/comments/1058990.html</wfw:comment><comments>http://www.cnblogs.com/kirinboy/archive/2008/01/30/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_1.html#Feedback</comments><slash:comments>18</slash:comments><wfw:commentRss>http://www.cnblogs.com/kirinboy/comments/commentRss/1058990.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/kirinboy/services/trackbacks/1058990.html</trackback:ping><description><![CDATA[摘要: 微软发布了ASP.NET 3.5 Extensions的第一个CTP版本，它包含ASP.NET 3.5的一个重要的extensions：ASP.NET MVC Framework。本系列文章拟使用ASP.NET MVC和LINQ建立一个简单的Blog系统，在第一部分，Keyvan介绍了MVC模式、ASP.NET MVC Framework以及简单Blog系统的基本原理。&nbsp;&nbsp;<a href='http://www.cnblogs.com/kirinboy/archive/2008/01/30/Building_a_Simple_Blog_Engine_with_ASPNET_MVC_and_LINQ__Part_1.html'>阅读全文</a><img src ="http://www.cnblogs.com/kirinboy/aggbug/1058990.html?type=1" width = "1" height = "1" /><br/><br/>--------------------------<br/>新闻：<a href="http://news.cnblogs.com/n/47969/" target="_blank">新浪邮箱大本营粉墨登场！Sina.cn开放注册</a><br/>网站导航: <a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://dotnet.cnblogs.com" target="_blank">.NET频道</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://zzk.cnblogs.com" target="_blank">找找看</a>]]></description></item></channel></rss>