﻿<?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>IT博客-醉意的未来-文章分类-.NET技术</title><link>http://www.cnitblog.com/kenlen/category/1181.html</link><description>路漫漫其修远兮，吾将上下而求索！&lt;br&gt;
&lt;b&gt;For things to change, first I must change!&lt;/b&gt;</description><language>zh-cn</language><lastBuildDate>Tue, 27 Sep 2011 10:32:18 GMT</lastBuildDate><pubDate>Tue, 27 Sep 2011 10:32:18 GMT</pubDate><ttl>60</ttl><item><title>Asp.Net 学习资源列表 </title><link>http://www.cnitblog.com/kenlen/articles/4820.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Tue, 22 Nov 2005 10:35:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4820.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4820.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4820.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4820.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4820.html</trackback:ping><description><![CDATA[<SPAN lang=EN-US style="FONT-SIZE: 9pt; COLOR: black"><BR>
<P>名称：快速入门<BR>地址：<A href="http://chs.gotdotnet.com/quickstart/" target=_blank><FONT color=#000080>http://chs.gotdotnet.com/quickstart/</FONT></A><BR>描述：本站点是微软.NET技术的快速入门网站，我们不必再安装.NET Framework中的快速入门示例程序，直接在网上查看此示例即看。<BR></P>
<P>名称：微软官方.NET指导站点<BR>地址：<A href="http://www.gotdotnet.com/" target=_blank><FONT color=#000080>http://www.gotdotnet.com/</FONT></A><BR>描述：上面的站点是本站的一个子站点，本站点提供微软.NET官方信息，并且有大量的用户源代码、控件下载，微软.NET开发组的人员也经常在此站点发表一些指导性文章。<BR></P>
<P>名称：SourceForge<BR>地址：<A href="http://www.sourceforge.net/" target=_blank><FONT color=#000080>http://www.sourceforge.net</FONT></A><BR>描述：世界上最大的Open Source项目在线网站，上面已经有.NET的各种大型Open Source项目上千件，包括SharpDevelop、NDoc、Mono等都是在此站点发布最新源代码信息。<BR></P>
<P>名称：CodeProject<BR>地址：<A href="http://www.codeproject.com/" target=_blank><FONT color=#000080>http://www.codeproject.com</FONT></A><BR>描述：很多非官方的中小型示例源代及文章，相当全面，基本上我们想要的各种方面的资料都可以在此处查找。<BR></P>
<P>名称：Fabrice's weblog <BR>地址：<A href="http://dotnetweblogs.com/FMARGUERIE/Story/4139.aspx" target=_blank><FONT color=#000080>http://dotnetweblogs.com/FMARGUERIE/Story/4139.aspx</FONT></A><BR>描述：这是一个WebLog形式的在线日志网站，定期更新，包括.NET相关的工具、混淆器、反编译器等各种信息，十分值得收藏。<BR></P>
<P>名称：<BR>地址：<A href="http://www.aspalliance.com/aldotnet/examples/translate.aspx" target=_blank><FONT color=#000080>http://www.aspalliance.com/aldotnet/examples/translate.aspx</FONT></A> <BR>描述：c#翻译为vb.net，提供一个文本框，将你的C#源代码贴进去，就可以帮你翻译成VB.NET语法。<BR></P>
<P>名称：CSharpHelp<BR>地址：<A href="http://www.csharphelp.com/" target=_blank><FONT color=#000080>http://www.csharphelp.com</FONT></A><BR>描述： 专业的C#语言在线帮助网站，主要提供C#语言方面的技术文章。专业性很强。<BR></P>
<P>名称：DotNet247<BR>地址：<A href="http://www.dotnet247.com/" target=_blank><FONT color=#000080>http://www.dotnet247.com</FONT></A><BR>描述：最好的索引网站，分别按照门类及命名空间的索引，也提供了Microsoft KB知识库。 <BR></P>
<P>名称：ASP.NET<BR>地址：<A href="http://www.asp.net/" target=_blank><FONT color=#000080>http://www.asp.net</FONT></A><BR>描述：微软.NET webform的老巢，资料和实例代码都非常难得。<BR></P>
<P>名称：微软.NET Winform<BR>地址：<A href="http://www.windowsforms.net/" target=_blank><FONT color=#000080>http://www.windowsforms.net/</FONT></A><BR>描述：微软.NET Winform的老巢。<BR></P>
<P>名称：微软 KnowledgeBase <BR>地址：<A href="http://support.microsoft.com/" target=_blank><FONT color=#000080>http://support.microsoft.com/</FONT></A><BR>描述：微软知识库，开发的时候遇到的怪问题，可能会在这里找到答案。<BR></P>
<P>名称：MSDN <BR>地址：<A href="http://msdn.microsoft.com/" target=_blank><FONT color=#000080>http://msdn.microsoft.com/</FONT></A><BR>描述：这个就不用多说了吧，虽然出了中文MSDN，但是资料还是不够全，英文的就什么都有了。<BR></P>
<P>名称：HotScripts <BR>地址：<A href="http://www.hotscripts.com/" target=_blank><FONT color=#000080>http://www.hotscripts.com/</FONT></A><BR>描述：Welcome to HotScripts.com, the net’s largest PHP, CGI, Perl, javascript and ASP script collection and resource web portal. We currently have 24,004 scripts across 11 different programming languages and 1,240 categories, as well as links to books, articles, as well as programming tips and tutorials. <BR></P>
<P>名称：ASPAlliance <BR>地址：<A href="http://www.aspalliance.com/" target=_blank><FONT color=#000080>http://www.aspalliance.com/</FONT></A><BR>描述：提供相当丰富的文章和示例代码，思路匮乏的时候可以找找思路<BR></P>
<P>名称：CSDN文档中心 <BR>地址：<A href="http://dev.csdn.net/" target=_blank><FONT color=#000080>http://dev.csdn.net/</FONT></A><BR>描述：中文的，资料还算丰富，可以作为国内首选。<BR></P>
<P>名称：DOTNET中华网 <BR>地址：<A href="http://www.aspxcn.com/" target=_blank><FONT color=#000080>http://www.aspxcn.com/</FONT></A><BR>描述：2002-2003年的时候这个站点很不错的，不过现在好像管理不得力，有点疲软，资料更新也不过及时，论坛里人也不够热心了，因为希望它好起来，所以列出来。资料都比较老，不过有些D版的东西还可以。提供很多学习代码。<BR></P>
<P>名称：中国DotNet俱乐部 <BR>地址：<A href="http://www.chinaspx.com/" target=_blank><FONT color=#000080>http://www.chinaspx.com/</FONT></A><BR>描述：有点公司背景的网站，很健壮，资料更新及时，比较丰富。论坛解答也不错。<BR></P>
<P>名称：【孟宪会之精彩世界】 <BR>地址：<A href="http://dotnet.aspx.cc/" target=_blank><FONT color=#000080>http://dotnet.aspx.cc/</FONT></A><BR>描述：MS-MVP的个人站点，包括了他所有的经验文章，还是很值得一看的。<BR></P>
<P>名称：dotNET Tools.org <BR>地址：<A href="http://www.dotnettools.org/" target=_blank><FONT color=#000080>http://www.dotnettools.org</FONT></A><BR>描述：ccboy，也就是CSDN的小气的神的站点，里面有很多关于.NET等的好东东。<BR></P>
<P>名称：博客堂 <BR>地址：<A href="http://blog.joycode.com/" target=_blank><FONT color=#000080>http://blog.joycode.com/</FONT></A><BR>描述：半官方性质的MS-MVP汇集blog，大家可以在这里接触到最新的技术，了解发展趋势，对技术的探索等等，优秀的文章。<BR></P>
<P>名称：DotNetBips.com - Applying .NET <BR>地址：<A href="http://www.dotnetbips.com/" target=_blank><FONT color=#000080>http://www.dotnetbips.com/</FONT></A><BR>描述：该站点的文章，涉及到了整个.NET，从底层的IL到语言到架构，文章很多，质量还不错。<BR></P>
<P>名称：C# Frequently Asked Questions<BR>地址：<A href="http://blogs.msdn.com/csharpfaq/" target=_blank><FONT color=#000080>http://blogs.msdn.com/csharpfaq/</FONT></A><BR>描述：The C# team posts answers to common questions<BR></P>
<P>名称：正则表达式<BR>地址：<A href="http://www.regexplib.com/" target=_blank><FONT color=#000080>http://www.regexplib.com/</FONT></A><BR>描述:&nbsp; 正则表达式学习站点 <BR></P>
<P>名称：WINDOW formS FAQ <BR>地址：<A href="http://www.syncfusion.com/FAQ/Winforms/" target=_blank><FONT color=#000080>http://www.syncfusion.com/FAQ/Winforms/</FONT></A><BR>描述：常见的forms faq问题，很多问题都可以在这里找到答案。 <BR></P>
<P>名称：ASP.NET 常用类库说明<BR>地址：<A href="http://www.123aspx.com/rotor/default.aspx" target=_blank><FONT color=#000080>http://www.123aspx.com/rotor/default.aspx</FONT></A><BR>描述：不用多说，看标题就知道是关于asp.net的名称空间的<BR></P>
<P>名称：ASP.NET System.Web.Mail <BR>地址：<A href="http://www.systemwebmail.com/faq/3.8.aspx" target=_blank><FONT color=#000080>http://www.systemwebmail.com/faq/3.8.aspx</FONT></A><BR>描述：邮件发送常见问题解决方法<BR></P>
<P>名称：VB.NET &amp; C# 比较<BR>地址：<A href="http://www.harding.edu/USER/fmccown/WWW/vbnet_csharp_comparison.html" target=_blank><FONT color=#000080>http://www.harding.edu/USER/fmccown/WWW/vbnet_csharp_comparison.html</FONT></A><BR>描述：VB.NET跟C#语法区别<BR></P>
<P>名称：VB.NET架构师　BLOG<BR>地址：<A href="http://panopticoncentral.net/" target=_blank><FONT color=#000080>http://panopticoncentral.net/</FONT></A><BR>描述：不用多说，想了解VB.NET的朋友不可不去的站点(PS，不知道我有没有记错是不是这个地址）<BR></P>
<P>名称：索克论坛<BR>地址：<A href="http://www.sorke.com/bbs/Boards.asp" target=_blank><FONT color=#000080>http://www.sorke.com/bbs/Boards.asp</FONT></A><BR>描述：我想应该是国内最好的第三方.NET控件的下载基地</P></SPAN><img src ="http://www.cnitblog.com/kenlen/aggbug/4820.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-22 18:35 <a href="http://www.cnitblog.com/kenlen/articles/4820.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET2.0实现无刷新客户端回调 </title><link>http://www.cnitblog.com/kenlen/articles/4688.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Sat, 19 Nov 2005 05:11:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4688.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4688.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4688.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4688.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4688.html</trackback:ping><description><![CDATA[<A class=bluekey href="http://www.yesky.com/key/1360/1360.html" target=_blank>Asp</A>.Net2.0的客户端回调是一种很让人激动的方法，他能够让我们控制要提交什么数据给服务器而不用提交整个页面，同时服务器也只返回你所需要的数据而不要发回整个页面。<BR><BR>　　首先我们要说一个很重要的方法：GetCallbackEventRefernce.我把我的理解写出来，可能是错误的，恳请指出，非常感谢！ <BR><BR>　　GetCallbackEventReference首先实现让客户端<A class=bluekey href="http://www.yesky.com/key/1399/6399.html" target=_blank>脚本</A>有能力传递参数给服务器端的RaiseCallbackEvent方法,然后返回RaiseCallBackEvent方法的值给你在GetCallbackEventRefernce方法中注册的一个参数（其实也是一个你要在客户端写的脚本）。调用GetCallbackEventRefernce你必须从客户端脚本中传递给他两个参数，一个是要传递给RaiseCallbackEvent事件的值，一个是context. <BR><BR>　　他的参数意义如下： <BR><BR>　　第一个：实现了ICallbackEventHandler借口的页面或者服务器控件，写this代表但前页面。 <BR><BR>　　第二个：代表你从要从客户端传递给服务器RaiseCallbackEvent方法的值 <BR><BR>　　第三个：你要在客户端写的一个js函数，同时，服务器也会把计算得到的数据传递给这个函数做为这个函数的参数。 <BR><BR>　　第四个：context具体什么意思我也不太清楚GetCallbackEventRefernce发送到了客户、端的代码是这样的： <BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false) </TD></TR></TBODY></TABLE><BR>　　那么我们要怎么样做才能够从客户端调用他呢？看到了三中方法： <BR><BR>　　第一种：在后台写个public string，在Page_Load中给他赋值为：=Page.ClientScript.GetCallbackEventReference(this, "message", "ShowServerTime", "context");注意在这里是Page.ClientScrip,因为他会返回个ClientScriptManager,ClientScriptManager管理所有的客户端脚本。然后在前台某个按钮的onclick事件里＜%=那个public后台字符串%＞.做个小实验代码如下： <BR><BR>　　前台ServerTime.aspx:为了方便去掉好多没用的html <BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜%@ page language="<A class=bluekey href="http://www.yesky.com/key/1664/1664.html" target=_blank>C#</A>" CodeFile="ServerTime.aspx.cs" Inherits="ServerTime_aspx" %＞ <BR>＜html＞ <BR>＜head＞ <BR>＜title＞Server Time＜/title＞ <BR>＜script language="<A class=bluekey href="http://www.yesky.com/key/1754/1754.html" target=_blank>javascript</A>"＞ <BR><BR>function GetServerTime() <BR>{ <BR>　var message = ''; <BR>　var context = ''; <BR>　＜%=sCallBackFunctionInvocation%＞ <BR>} <BR><BR>function ShowServerTime(timeMessage, context) { <BR>　alert('现在服务器上的时间是:\n' + timeMessage); <BR>} <BR>＜/script＞ <BR>＜/head＞ <BR>＜body＞ <BR>＜form id="MainForm" runat="server"＞ <BR>＜input type="button" value="得到服务器端时间" onclick="GetServerTime();" /＞ <BR>＜/form＞ <BR>＜/body＞ <BR>＜/html＞ </TD></TR></TBODY></TABLE><BR>　　后台：<BR><BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><IMG src="http://www.cnitblog.com/images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #0000ff">using</SPAN><SPAN style="COLOR: #000000">&nbsp;System;<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">using</SPAN><SPAN style="COLOR: #000000">&nbsp;System.Web.UI;<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/None.gif" align=top><BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/None.gif" align=top></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;partial&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;ServerTime_aspx&nbsp;:&nbsp;Page,ICallbackEventHandler<BR><IMG id=Codehighlighter1_102_458_Open_Image onclick="this.style.display='none'; Codehighlighter1_102_458_Open_Text.style.display='none'; Codehighlighter1_102_458_Closed_Image.style.display='inline'; Codehighlighter1_102_458_Closed_Text.style.display='inline';" src="http://www.cnitblog.com/images/OutliningIndicators/ExpandedBlockStart.gif" align=top><IMG id=Codehighlighter1_102_458_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_102_458_Closed_Text.style.display='none'; Codehighlighter1_102_458_Open_Image.style.display='inline'; Codehighlighter1_102_458_Open_Text.style.display='inline';" src="http://www.cnitblog.com/images/OutliningIndicators/ContractedBlock.gif" align=top></SPAN><SPAN id=Codehighlighter1_102_458_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnitblog.com/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_102_458_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top>　</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">一定要实现ICallbackEventHandler借口</SPAN><SPAN style="COLOR: #008000"><BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top></SPAN><SPAN style="COLOR: #000000">　</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">string</SPAN><SPAN style="COLOR: #000000">&nbsp;sCallBackFunctionInvocation;<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top>　</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;Page_Load(</SPAN><SPAN style="COLOR: #0000ff">object</SPAN><SPAN style="COLOR: #000000">&nbsp;sender,&nbsp;System.EventArgs&nbsp;e)<BR><IMG id=Codehighlighter1_233_359_Open_Image onclick="this.style.display='none'; Codehighlighter1_233_359_Open_Text.style.display='none'; Codehighlighter1_233_359_Closed_Image.style.display='inline'; Codehighlighter1_233_359_Closed_Text.style.display='inline';" src="http://www.cnitblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_233_359_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_233_359_Closed_Text.style.display='none'; Codehighlighter1_233_359_Open_Image.style.display='inline'; Codehighlighter1_233_359_Open_Text.style.display='inline';" src="http://www.cnitblog.com/images/OutliningIndicators/ContractedSubBlock.gif" align=top>　</SPAN><SPAN id=Codehighlighter1_233_359_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnitblog.com/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_233_359_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top>　　sCallBackFunctionInvocation&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;Page.ClientScript.GetCallbackEventReference(</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">message</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ShowServerTime</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">context</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>　}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top><BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top>　</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">string</SPAN><SPAN style="COLOR: #000000">&nbsp;RaiseCallbackEvent(</SPAN><SPAN style="COLOR: #0000ff">string</SPAN><SPAN style="COLOR: #000000">&nbsp;eventArgument)<BR><IMG id=Codehighlighter1_419_456_Open_Image onclick="this.style.display='none'; Codehighlighter1_419_456_Open_Text.style.display='none'; Codehighlighter1_419_456_Closed_Image.style.display='inline'; Codehighlighter1_419_456_Closed_Text.style.display='inline';" src="http://www.cnitblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><IMG id=Codehighlighter1_419_456_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_419_456_Closed_Text.style.display='none'; Codehighlighter1_419_456_Open_Image.style.display='inline'; Codehighlighter1_419_456_Open_Text.style.display='inline';" src="http://www.cnitblog.com/images/OutliningIndicators/ContractedSubBlock.gif" align=top>　</SPAN><SPAN id=Codehighlighter1_419_456_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><IMG src="http://www.cnitblog.com/images/dot.gif"></SPAN><SPAN id=Codehighlighter1_419_456_Open_Text><SPAN style="COLOR: #000000">{<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/InBlock.gif" align=top>　　</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;DateTime.Now.ToString();<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>　}</SPAN></SPAN><SPAN style="COLOR: #000000"><BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</SPAN></SPAN><SPAN style="COLOR: #000000">&nbsp;<BR><IMG src="http://www.cnitblog.com/images/OutliningIndicators/None.gif" align=top></SPAN></DIV>第二种方法：在上面的方法中我们必须要在前台绑定后台，那么如果不绑定呢？我们这样做：<BR><BR>　　直接把GetCallbackEventReference当做js函数中的一个实现内容，然后把这个js函数注册到客户端。<BR><BR>　　前台TestPage代码：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜%@ Page Language="C#" AutoEventWireup="true" CodeFile="TestPage.aspx.cs" Inherits="TestPage" %＞<BR>＜html＞<BR>＜head＞<BR>＜title＞Untitled Page＜/title＞<BR>＜script type="text/javascript"＞<BR>function test()<BR>{<BR>　var lb = document.getElementById("Select1");<BR>　//取的那个下拉框<BR>　var con = lb.options[lb.selectedIndex].text;<BR>　//得到你选择的下拉框的文本再调用呢个CallTheServer,是一个由服务器端输出的js函数<BR>　CallTheServer(con,'');<BR>}<BR>function ReceiveServerData(rValue)<BR>{<BR>　Results.innerHTML = rValue;<BR>}<BR>＜/script＞<BR>＜/head＞<BR>＜body＞<BR>＜form id="form1" runat="server"＞<BR>＜div＞<BR>＜select id="Select1"＞<BR>＜option value=1 selected="selected"＞老鼠徒弟＜/option＞<BR>＜option value=2＞吴旗娃师傅＜/option＞<BR>＜/select＞<BR>＜br /＞<BR>＜br /＞<BR>＜input onclick="test()" value="从服务器返回下拉框文本" type=button＞<BR>＜br /＞<BR>＜br /＞<BR>＜span ID="Results"＞＜/span＞<BR>＜br /＞<BR>＜/div＞<BR>＜/form＞<BR>＜/body＞<BR>＜/html＞</TD></TR></TBODY></TABLE><BR>　　后台代码：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>using System;<BR>using System.Data;<BR>using System.Configuration;<BR>using System.Collections;<BR>using System.Web;<BR>using System.Web.Security;<BR>using System.Web.UI;<BR>using System.Web.UI.WebControls;<BR>using System.Web.UI.WebControls.WebParts;<BR>using System.Web.UI.HtmlControls;<BR><BR>public partial class TestPage : System.Web.UI.Page,System.Web.UI.ICallbackEventHandler<BR>{<BR>　protected void Page_Load(object sender, EventArgs e)<BR>　{<BR>　　String cbReference = Page.ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context");<BR>　　String callbackScript;<BR>　　callbackScript = "function CallTheServer(arg,context)" +"{ " + cbReference + "} ;";<BR>　　Page.ClientScript.RegisterStartupScript(this.GetType(),"abcdefg",callbackScript, true);<BR>　　//第四个参数代表是不是要自动给着脚本加上＜script type="text/javascript"＞＜/script＞标记，当然要加啊<BR>　}<BR>　public String RaiseCallbackEvent(String eventArgument)<BR>　{<BR>　　return "你选择的是" + eventArgument;<BR>　}<BR>}</TD></TR></TBODY></TABLE>第三种：前面两种都是＜input type="button"的html控件，那么如果是服务器按钮呢？当然也可以，在后台添加服务器按钮的onclick 属性。<BR><BR>　　前台third.aspx代码：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜%@ Page Language="C#" AutoEventWireup="true" CodeFile="third.aspx.cs" Inherits="third" %＞<BR>＜html＞<BR>＜head＞<BR>＜title＞Untitled Page＜/title＞<BR>＜/head＞<BR>＜body＞<BR>＜form id="form1" runat="server"＞<BR>＜div＞<BR>＜select id="Select1"＞<BR>＜option selected="selected" value=1＞老鼠徒弟＜/option＞<BR>＜option value=2＞吴旗娃师傅＜/option＞<BR>＜/select＞<BR>＜asp:Button ID="Button1" runat="server" Text="这是个服务器按钮" /＞＜/div＞<BR>＜div id="div1" /＞<BR>＜script type="text/javascript"＞<BR>function Re(ret)<BR>{<BR>　document.getElementById("div1").innerHTML = ret;<BR>　alert(ret);<BR>}<BR>＜/script＞<BR>＜/form＞<BR>＜/body＞<BR>＜/html＞<BR>后台代码：<BR>using System;<BR>using System.Data;<BR>using System.Configuration;<BR>using System.Collections;<BR>using System.Web;<BR>using System.Web.Security;<BR>using System.Web.UI;<BR>using System.Web.UI.WebControls;<BR>using System.Web.UI.WebControls.WebParts;<BR>using System.Web.UI.HtmlControls;<BR><BR>public partial class third : System.Web.UI.Page,System.Web.UI.ICallbackEventHandler<BR>{<BR>　protected void Page_Load(object sender, EventArgs e)<BR>　{<BR>　　//第四个参数为null，因为你不可能再在js中给他传参数了<BR>　　string str = Page.ClientScript.GetCallbackEventReference(this,"document.getElementById('Select1')._<BR>　　　　　　　options[document.getElementById('Select1').selectedIndex].text","Re",null);<BR>　　//return false是为了防止提交窗体<BR>　　Button1.Attributes.Add("onclick",str+";return false;");<BR>　}<BR><BR>　#region ICallbackEventHandler Members<BR>　<BR>　public string RaiseCallbackEvent(string eventArgument)<BR>　{<BR>　　if (eventArgument == "老鼠徒弟")<BR>　　{<BR>　　　return "老鼠徒弟：人生如鼠，不在仓就在厕！";<BR>　　}<BR>　　else<BR>　　{<BR>　　　return "吴旗娃师傅：自信自强，乐观向上";<BR>　　}<BR>　}<BR>　#endregion<BR>}<BR></TD></TR></TBODY></TABLE><BR>　　小技巧，当你写完System.Web.UI.ICallbackEventHandler后，把鼠标移上去，那么System前面会有个小图表，点他会自动写好那个RaiseCallbackEvent代码，效果如下；<BR><img src ="http://www.cnitblog.com/kenlen/aggbug/4688.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-19 13:11 <a href="http://www.cnitblog.com/kenlen/articles/4688.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>An Overview of Visual Basic 2005</title><link>http://www.cnitblog.com/kenlen/articles/4604.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Thu, 17 Nov 2005 05:50:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4604.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4604.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4604.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4604.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4604.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Ken GetzMCW Technologies, LLC Updated November 2005Summary: Get on overview of the new features in Visual Basic 2005 including, My Visual Basic, IntelliSense, Edit and Continue, AutoCorrect, Just ...&nbsp;&nbsp;<a href='http://www.cnitblog.com/kenlen/articles/4604.html'>阅读全文</a><img src ="http://www.cnitblog.com/kenlen/aggbug/4604.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-17 13:50 <a href="http://www.cnitblog.com/kenlen/articles/4604.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个功能齐全的DataGrid分页例子</title><link>http://www.cnitblog.com/kenlen/articles/4388.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Sat, 12 Nov 2005 02:45:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4388.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4388.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4388.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4388.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 方法一：DataGridPaging.aspx&lt;%@&nbsp;Page&nbsp;language="c#"&nbsp;EnableViewState&nbsp;=&nbsp;"true"&nbsp;Codebehind="DataGridPaging.aspx.cs"&nbsp;AutoEventWireup="false"&nbsp;Inherits="eMeng.Exam.DataG...&nbsp;&nbsp;<a href='http://www.cnitblog.com/kenlen/articles/4388.html'>阅读全文</a><img src ="http://www.cnitblog.com/kenlen/aggbug/4388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-12 10:45 <a href="http://www.cnitblog.com/kenlen/articles/4388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Visual Basic 9.0 </title><link>http://www.cnitblog.com/kenlen/articles/4365.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Fri, 11 Nov 2005 13:48:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4365.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4365.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4365.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4365.html</trackback:ping><description><![CDATA[<DIV style="PADDING-RIGHT: 15px; PADDING-LEFT: 20px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px">
<H1>Visual Basic 9.0 概述</H1>
<H2 class=subtitle></H2>
<DIV class=date>发布日期： 11/10/2005<SPAN class=datePipe> | </SPAN>更新日期： 11/10/2005</DIV>
<DIV class=overview>
<P>Erik Meijer、Amanda Silver 和 Paul Vick<BR>Microsoft、Corporation</P>
<P><B>摘要：</B>本文概述支持数据密集型编程的新的 Visual Basic 语言特性和新的语言扩展。</P></DIV>
<CENTER><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.gif" width=30 border=0></CENTER>
<DIV style="HEIGHT: 18px"></DIV>
<H5 style="PADDING-TOP: 2px">本页内容</H5>
<TABLE style="MARGIN-TOP: 7px; MARGIN-BOTTOM: 12px" cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0JB0AA"><IMG height=9 alt=简介 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0JB0AA">简介</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0GB0AA"><IMG height=9 alt="开始使用 Visual Basic 9.0" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0GB0AA">开始使用 Visual Basic 9.0</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0DB0AA"><IMG height=9 alt=隐式类型本地变量 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0DB0AA">隐式类型本地变量</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0AB0AA"><IMG height=9 alt=对象和集合初始值设置项 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E0AB0AA">对象和集合初始值设置项</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E4AA"><IMG height=9 alt=匿名类型 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E4AA">匿名类型</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E1AA"><IMG height=9 alt="深层次的 XML 支持" hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#E1AA">深层次的 XML 支持</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EXAA"><IMG height=9 alt=查询综合 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EXAA">查询综合</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EUAA"><IMG height=9 alt="扩展方法 " hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EUAA">扩展方法 </A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#ERAA"><IMG height=9 alt=嵌套函数 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#ERAA">嵌套函数</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EOAA"><IMG height=9 alt=空类型 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EOAA">空类型</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#ELAA"><IMG height=9 alt=宽松的委托 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#ELAA">宽松的委托</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EIAA"><IMG height=9 alt=动态接口（或强“鸭子类型”） hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EIAA">动态接口（或强“鸭子类型”）</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EFAA"><IMG height=9 alt=动态标识符 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#EFAA">动态标识符</A></TD></TR>
<TR vAlign=top>
<TD><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#ECAA"><IMG height=9 alt=小结 hspace=4 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_down.gif" width=7 vspace=2 border=0></A></TD>
<TD class=onThisPage><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#ECAA">小结</A></TD></TR></TBODY></TABLE><A name=E0JB0AA></A>
<H2>简介</H2>
<P>“代号为 Orcas 的 Visual Basic”(Visual Basic 9.0) 引入了一些语言扩展功能，以统一的方式支持数据密集型编程 — 创建、更新以及查询关系型数据库、XML 文档和对象图形，这些语言扩展均基于“代号为 Whidbey 的Visual Basic”而构建。除此之外，Visual Basic 9.0 还引入一些新的语言特性，用于支持可能的静态输入和必要的动态输入，以增强 Visual Basic 的特有功能。这些新特性是： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>隐式类型本地变量 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>查询综合 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>对象初识值设定项 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>匿名类型 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>与 <I>Linq</I> 框架的完全集成 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>深层次的 XML 支持 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>宽松的委托 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>空类型 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>动态接口 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>动态标识符 </P></TD></TR></TBODY></TABLE>
<P>本文对这些新特性进行简要概述。要获得包括 Visual Basic 语言定义和编译器预览在内的更多信息，请访问 Visual Basic 开发人员中心 (<A href="http://msdn.microsoft.com/vbasic/default.aspx" target=_blank>http://msdn.microsoft.com/vbasic/default.aspx</A>)。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=E0GB0AA></A>
<H2>开始使用 Visual Basic 9.0</H2>
<P>为了领会这些语言特性在实际应用中的作用，我们举一个真实示例 — <A href="http://www.odci.gov/cia/publications/factbook/" target=_blank>CIA World Factbook 数据库</A>。该数据库包罗万象，涵盖了世界各国的地理、经济、社会和政治信息。就本文示例而言，我们采用一个包含各国国名、首都、总面积和人口的架构。在 Visual Basic 9.0 中，我们以下面这个类来表示该架构：</P><PRE class=codeSample>Class Country
  Public Property Name As String
  Public Property Area As Float 
  Public Property Population As Integer
End Class
</PRE>
<P>以下是国家数据库的一个小子集，我们将以其作为运行示例：</P><PRE class=codeSample>Dim Countries = _
  { new Country{ _
      .Name = "Palau", .Area = 458, .Population = 16952 }, _
    new Country{ _
      .Name = "Monaco", .Area = 1.9, .Population = 31719 }, _
    new Country{ _
      .Name = "Belize", .Area = 22960, .Population = 219296 }, _
    new Country{ _
      .Name = "Madagascar", .Area = 587040, .Population = 13670507 } _
  }
</PRE>
<P>给定该列表后，通过以下查询综合，我们就能够查询所有人口小于一百万的国家：</P><PRE class=codeSample>Dim SmallCountries = Select Country _
                     From Country In Countries _
                     Where Country.Population &lt; 1000000
For Each Country As Country In SmallCountries
  Console.WriteLine(Country.Name)
Next
</PRE>
<P>因为只有马达加斯加岛 (Madagascar) 拥有超过一百万的居民，所以编译并运行以上程序时，将打印以下国家名称列表：</P><PRE class=codeSample>Palau
Monaco
Belize 
</PRE>
<P>Visual Basic 9.0 提供的特性使编程完成这项查询相当简单，让我们通过研究该程序来理解这些特性。首先，Countries 变量的声明</P><PRE class=codeSample>Dim Countries = _
  { new County { .Name = "Palau", .Area = 458, .Population = 16952 }, _
  ... _
  }
</PRE>
<P>使用了新的<EM>对象初始值设定项</EM><EM></EM>语法，即 new Country {..., .Area = 458, ...}，该语法类似于现有的 With 语句，通过这种简洁的、基于表达式的语法可以创建复杂的对象实例。 </P>
<P>它还说明了<EM>隐式类型的本地变量</EM> 的声明方法，即编译器从声明右侧的初始化表达式推断本地变量 Countries 的类型。以上声明完全等效于显式类型的 Country() 类型的本地变量声明。</P><PRE class=codeSample>Dim Countries As Country() = {...}
</PRE>
<P>需要强调的是，这仍然是强类型声明；编译器已经自动推断出本地声明右侧的类型，因此程序员无需手动将其输入到程序中。</P>
<P>SmallCountries 本地变量的声明通过类似于 SQL 风格的查询综合进行初始化，以筛选出居民总数不足一百万的所有国家。与 SQL 的异曲同工并非有意的，其目的是使已经了解 SQL 的程序员能够更迅速地了解 Visual Basic 的查询语法。</P><PRE class=codeSample>Dim SmallCountries = Select Country _
                     From Country In Countries _
                     Where Country.Population &lt; 1000000
</PRE>
<P>请注意，我们还有另一个隐式输入的应用程序：编译器将 SmallCountries 的类型推断为 IEnumerable(Of Country)。编译器本身将查询综合翻译成标准的查询操作符。在这种情况下，翻译如以下语句一样简单：</P><PRE class=codeSample>Function F(Country As Country) As Boolean
  Return Country.Population &lt; 1000000
End Function

Dim SmallCountries As IEnumerable(Of Country) = _
   Countries.Where(AddressOf F)
</PRE>
<P>扩展语法将编译器生成的本地函数作为委托 AddressOf F 传递到扩展函数 Where 中，在标准的查询操作符库中，该函数定义为 IEnumerable(Of T) 接口的扩展。</P>
<P>既然我们已经了解了 Visual Basic 9 中的一些新特性，那么就让我们进一步研究其中的细节吧。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=E0DB0AA></A>
<H2>隐式类型本地变量</H2>
<P>在隐式类型的本地变量声明中，本地变量的类型从本地声明语句右侧的初识化表达式中推断。例如，编译器将推断以下所有变量声明的类型：</P><PRE class=codeSample>Dim Population = 31719
Dim Name = "Belize"
Dim Area = 1.9
Dim Country = New Country{ .Name = "Palau", ...}
</PRE>
<P>因此，这些声明完全等效于以下显式类型声明：</P><PRE class=codeSample>Dim Population As Integer = 31719
Dim Name As String = "Belize"
Dim Area As Float = 1.9
Dim Country As Country = New Country{ .Name = "Palau", ...}
</PRE>
<P>因为在默认情况下推断本地变量声明的类型，所以无论 Option Strict 的设置为何，总是早期绑定对于这些变量的访问。在 Visual Basic 9.0 中，程序员必须按以下方式将变量显式声明为类型 Object，从而显式指定晚期绑定：</P><PRE class=codeSample>Dim Country As Object = new Country{ .Name = "Palau", ... }
</PRE>
<P>要求使用显式晚期绑定是为了防止意外使用晚期绑定，但更重要的是，这样做可以有力地扩展它对诸如 XML 等新数据类型的晚期绑定，这一点我们稍后讨论。有一个可选的项目级开关可以切换现有的行为。</P>
<P>For...Next 或 For Each...Next 语句中的循环控制变量也可以是一个隐式类型变量。在 For Dim I = 0 To Count 或 For Each Dim C In SmallCountries 中指定循环控制变量时，标识符将定义一个新的隐式类型本地变量，其类型从初始值设定项或集合表达式中推断，并作用于整个循环。在 For 右面使用 Dim 与隐式类型的循环控制变量一样，都是 Visual Basic 9.0 引入的新特性。</P>
<P>通过该类型引用应用程序，我们能够按以下方式重写打印所有小国的循环：</P><PRE class=codeSample>For Each Dim Country In SmallCountries
  Console.WriteLine(Country.Name)
Next
</PRE>
<P>Country 的类型推断为 SmallCountries 的元素类型 Country。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=E0AB0AA></A>
<H2>对象和集合初始值设置项</H2>
<P>在 Visual Basic 中，With 语句无需多次指定目标表达式，即可简化对一个聚合值的多个成员的访问。在 With 语句块内，对以句点 (“.”) 开头的成员访问表达式进行求值，就好像句点前面带有 With 语句的目标表达式一样。例如，以下语句初始化一个新的 Country 实例，随后又将该实例的字段初始化为所需的值：</P><PRE class=codeSample>Dim Palau = New Country()
With Palau
  .Name = "Palau"  
  .Area = 458
  .Population = 16952
End With
</PRE>
<P>新的 Visual Basic 9.0 <EM>对象初始值设定项</EM><EM></EM>采用的是一种基于表达式的 With 格式，用于以简明的方式创建复杂的对象实例。通过对象初识值设定项，我们能够将以上两条语句捕获为一条（隐式类型的）本地声明，如下所示：</P><PRE class=codeSample>Dim Palau = New Country { _
  .Name = "Palau", _
  .Area = 458, _
  .Population = 16952 
}
</PRE>
<P>这种在表达式中进行的对象初始化对于查询而言非常重要。一条查询语句通常就像由等号右侧的 Select 子句初始化的对象声明。由于 Select 子句返回一个表达式，因此我们必须能够以一条表达式初始化整个对象。 </P>
<P>我们已经看到，对象初始值设定项对于创建复杂对象的集合也是很方便的。任何支持 Add 方法的集合都能够通过一个<EM>集合初始值设定项</EM> 表达式进行初始化。例如，给定城市声明作为部分类，</P><PRE class=codeSample>Partial Class City
  Public Property Name As String
  Public Property Country As String
  Public Property Longitude As Float 
  Public Property Latitude As Float
End Class
</PRE>
<P>我们能够按以下方式创建一个示例国家的首都城市列表：</P><PRE class=codeSample>Dim Capitals = New List(Of City){ _
  { .Name = "Antanarivo", _
    .Country = "Madagascar", _
    .Longitude = 47.4, _
    .Lattitude = -18.6 }, _
  { .Name = "Belmopan", _
    .Country = "Belize", _
    .Longitude = -88.5, _
    .Latitude = 17.1 }, _
  { .Name = "Monaco", _
    .Country = "Monaco", _
    .Longtitude = 7.2, _
    .Latitude = 43.7 }, _
  { .Country = "Palau",
    .Name = "Koror", _
    .Longitude = 135, _
    .Latitude = 8 } _
}
</PRE>
<P>该示例还使用嵌套对象初始值设定项，嵌套初始值设定项的构造函数从上下文中推断。在这种情况下，每个嵌套初始值设定项完全等效于完整形式的 New City{...}。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=E4AA></A>
<H2>匿名类型</H2>
<P>通常，我们希望在查询结果中移除或<EM>抛出</EM> 一类特定成员。例如，我们也许只想知道所有热带首都城市的 Name 和 Country，虽然也使用源数据中的 Latitude 和 Longitude 列标识热带，但是在结果中抛出了这两列。在 Visual Basic 9.0 中要实现这个功能，我们可以为每个纬度位于巨蟹座热带和摩羯座热带之间的城市 C 创建一个新的对象实例，而不用命名类型：</P><PRE class=codeSample>Const TropicOfCancer = 23.5
Const TropicOfCapricorn = -23.5

Dim Tropical = Select New{ .Name = City.Name, .Country = City.Country } _
               From City In Capitals _
               Where TropicOfCancer =&lt; City.Latitude _
               AndAlso City.Latitude &gt;= TropicOfCapricorn
</PRE>
<P>本地变量 Tropical 的推断类型是一种匿名类型实例的集合，即 IEnumerable(Of { Name As String, Country As String })。Visual Basic 编译器将创建一个系统生成的新类，例如 _Name_As_String_Country_As_String_，该类的成员名和成员类型按以下方式从对象初始值设定项中推断：</P><PRE class=codeSample>Class _Name_As_String_Country_As_String_ 
    Public Property Name As String
    Public Property Country As String
    Public Default Property Item(Index As Integer) As Object
    ...
End Class
</PRE>
<P>编译器会合并同一个程序内的相同匿名类型。如果两个匿名对象初始值设定项以相同的顺序指定相同名字和类型的属性序列，那么它们将产生同一种匿名类型的实例。表面上，Visual Basic 生成的匿名类型会删除到 Object 中，这样编译器就可以统一将匿名类型作为函数的参数和结果传递。在 Visual Basic 代码内部使用时，编译器将用特殊的自定义属性修饰生成的类，从而记住类型 _Name_As_String_Country_As_String_ 实际上代表匿名类型 { Name As String, Country As String }。</P>
<P>因为匿名类型通常用于抛出一种现有类型的成员，所以 Visual Basic 9.0 允许使用简写投影标记 New { City.Name, City.Country } 作为长格式 New { .Name = City.Name, .Country = City.Country } 的缩写。在查询综合的结果表达式中使用这种简写投影标记时，我们甚至能够按以下方式进一步简化投影初始值设定项：</P><PRE class=codeSample>Dim Tropical = Select City.Name, City.Country _
               From City In Capitals _
               Where TropicOfCancer =&lt; City.Latitude _
               AndAlso City.Latitude &gt;= TropicOfCapricorn
</PRE>
<P>注意，对于前面的长格式而言，这两种缩写形式意义相同。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=E1AA></A>
<H2>深层次的 XML 支持</H2>
<P><B><I>XLinq</I></B> 是一个内存中的新 XML 编程 API，它是为利用最新的 .NET Framework 功能（例如语言集成查询框架）而专门设计的。Visual Basic 9.0 通过 <EM>XML </EM><EM>文字</EM><EM></EM>和 <EM>XML </EM><EM>之上的晚期绑定</EM> 提供针对 XLinq 的深层次支持，就像查询综合在底层标准 .NET Framework 查询操作符上添加熟悉且方便的语法一样。 </P>
<P>为了解释 XML 文字，我们将查询实质上为水平关系的数据源 Countries 和 Capitals，以构造一个具有层次结构的 XML 模型，该模型将每个国家的首都作为一个子元素嵌套，并计算人口密度作为一个属性。 </P>
<P>为了查找某一给定国家的首都，我们将每个国家的国名成员和每个城市的国家成员<I>联接</I> 起来。给定了一个国家及其首都，我们就能够将计算出的值填入嵌入表达式<I>空白值</I> 处，从而很容易地构造出 XML 片断。我们可以通过括号将表示名称属性的空白值括起来，例如 Name=(Country.Name)，还可以通过从 ASP.NET 借用而来的尖括号将子元素括起来，例如 &lt;Name&gt;&lt;%= City.Name %&gt;&lt;/Name&gt;。以下即为将 XML 文字和查询综合结合在一起的查询代码：</P><PRE class=codeSample>Dim CountriesWithCapital As XElement = _
  &lt;Countries&gt;
    &lt;%= Select &lt;Country Name=(Country.Name)
                        Density=(Country.Population/Country.Area)&gt;
                 &lt;Capital&gt;
                   &lt;Name&gt;&lt;%= City.Name %&gt;&lt;/Name&gt;
                   &lt;Longitude&gt;&lt;%= City.Longitude %&gt;&lt;/Longtitude&gt;
                   &lt;Latitude&gt;&lt;%= City.Latitude %&gt;&lt;/Latitude&gt;
                 &lt;/Capital&gt;
               &lt;/Country&gt; _
        From Country In Countries, City In Capitals _
        Where Country.Name = City.Country %&gt;
  &lt;/Countries&gt;                     
</PRE>
<P>注意，类型 XElement 在声明中可以省略；如果省略，它将像任何其他本地声明一样进行推断。为了后文立论方便，我们在本示例中保留显式类型。</P>
<P>我们希望，在此声明中，Select 查询的结果在 &lt;Countries&gt; 元素内部进行替换。因此，Select 查询将成为 &lt;Countries&gt; 内第一处空白值的内容，该空白值内容的范围由我们熟悉的 ASP.NET 格式标记 &lt;%= 和 %&gt; 界定。由于 Select 查询的结果和 XML 文字都是表达式，那么很自然地可以在 Select 内嵌套另一个 XML 文字。该嵌套文字本身包含了嵌套属性空白值（Country.Name 和经过计算的人口密度比 Country.Population/Country.Area）和嵌套元素空白值（首都城市名和坐标）。 </P>
<P>编译并运行以上查询时，将返回以下 XML 文档（格式稍作调整以减少空行）：</P><PRE class=codeSample>&lt;Countries&gt;
 &lt;Country Name="Palau" Density="0.037117903930131008"&gt;
   &lt;Capital&gt;
     &lt;Name&gt;Koror&lt;/Name&gt;&lt;Longitude&gt;135&lt;/Longitude&gt;&lt;Latitude&gt;8&lt;/Latitude&gt;
   &lt;/Capital&gt;
 &lt;/Country&gt;
 &lt;Country Name="Monaco" Density="16694.21052631579"&gt;
   &lt;Capital&gt;
     &lt;Name&gt;Monaco&lt;/Name&gt;&lt;Longitude&gt;7.2&lt;/Longitude&gt;&lt;Latitude&gt;3.7&lt;/Latitude&gt;
   &lt;/Capital&gt;
 &lt;/Country&gt;
 &lt;Country Name="Belize" Density="9.5512195121951216"&gt;
   &lt;Capital&gt;
     &lt;Name&gt;Belmopan&lt;/Name&gt;&lt;Longitude&gt;-88.5&lt;/Longitude&gt;&lt;Latitude&gt;17.1&lt;/Latitude&gt;
   &lt;/Capital&gt;
 &lt;/Country&gt;
 &lt;Country Name="Madagascar" Density="23.287181452711909"&gt;
   &lt;Capital&gt;
     &lt;Name&gt;Antananarivo&lt;/Name&gt;
     &lt;Longitude&gt;47.4&lt;/Longitude&gt;&lt;Latitude&gt;-18.6&lt;/Latitude&gt;
   &lt;/Capital&gt;
  &lt;/Country&gt;
&lt;/Countries&gt; 
</PRE>
<P>Visual Basic 9.0 将 XML 文字编译为正常的 System.Xml.XLinq 对象，以确保 Visual Basic 和其他使用 XLinq 的语言之间的完全互操作。就本示例查询而言，由编译器生成的代码为（假设我们可以看到）：</P><PRE class=codeSample>Dim CountriesWithCapital As XElement = _ 
  New XElement("Countries", _
    Select New XElement("Country", _
             New XAttribute("Name", Country.Name), _
             New XAttribute("Density", Country.Population/Country.Area), _
             New XElement("Capital", _
               New XElement("Name", City.Name), _
               New XElement("Longitude", City.Longitude), _
               New XElement("Latitude", City.Latitude)))
    From Country In Countries, City In Capitals _
    Where Country.Name = City.Country)
</PRE>
<P>Visual Basic 9.0 除了构造 XML 之外，还通过 XML 上的晚期绑定简化了<I>访问</I> XML 结构的步骤，换言之，Visual Basic 代码中的标识符在运行时就被绑定到相应的 XML 属性和元素。例如，我们能够按以下方式打印出所有示例国家的人口密度： </P>
<TABLE class=numberedList cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>1.</P></TD>
<TD>
<P>通过<I>子轴</I> CountriesWithCapital.Country 从 CountriesWithCapital XML 结构中获得所有“Country”元素； </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>2.</P></TD>
<TD>
<P>通过<EM>属性轴</EM> Country.@Density 获得 Country 元素的“Density”属性； </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>3.</P></TD>
<TD>
<P>通过<EM>后代轴</EM> Country...Latitude（在源代码中以三个点顺序书写）获得所有 Country 元素的“Latitude”子元素，无论这些子元素在层次结构中出现的深度如何；以及 </P></TD></TR>
<TR vAlign=top>
<TD class=listNumber noWrap align=right>
<P>4.</P></TD>
<TD>
<P>通过 IEnumerable(Of T) 上的<I>扩展索引器</I> 选择结果排序中的第一个元素。 </P></TD></TR></TBODY></TABLE>
<P>如果将这些步骤综合到一起，代码如下所示：</P><PRE class=codeSample>For Each Dim Country In CountriesWithCapital.Country
  Console.WriteLine("Density = "+ Country.@Density)
  Console.WriteLine("Latitude = "+ Country...Latitude(0))
Next
</PRE>
<P>当声明、赋值或初始化的目标表达式是 Object 类型而非某一种更具体的类型时，编译器“懂得”在正常对象上使用晚期绑定。同样，在目标表达式为 XElement、XDocument、XAttribute 类型或集合时，编译器也“懂得”使用 XML 之上的晚期绑定。 </P>
<P>编译器按以下方式翻译 XML 之上的晚期绑定结果： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>子轴表达式 CountriesWithCapital.Country 翻译为原始 XLinq 调用 CountriesWithCapital.Elements("Country")，该调用返回 Country 元素的所有名为“Country”的子元素集合； </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>属性轴表达式 Country.@Density 转换为 Country.Attribute("Density")，它返回 Country 的名为“Density”的一个子属性；而且 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P>后代轴表达式 Country...Latitude(0) 转换为组合形式 ElementAt(Country.Descendants(Latitude),0)，它返回 Country 下任意深度的所有命名元素集合。 </P></TD></TR></TBODY></TABLE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=EXAA></A>
<H2>查询综合</H2>
<P><EM>查询综合</EM> 提供一种类似于 SQL 的查询语言集成语法，一方面它与 Visual Basic 的外在风格相吻合，另一方面它与新的 .NET 语言集成查询框架无缝集成。 </P>
<P>熟悉 <A href="http://pi3.informatik.uni-mannheim.de/~moer/querycompiler.pdf" target=_blank>SQL 实现</A>的人都可以认出底层 .NET Framework 顺序操作符中许多关系代数的组合操作符，例如代表查询处理器内查询计划的投影、选择、矢量积、组合和排序。 </P>
<P>查询综合的语义通过将其转换为顺序操作符进行定义，所以基本操作符将绑定到任何属于顺序操作符范围内的操作符。这意味着如果导入某一个特定的实现，用户将能够有效地重新绑定查询综合的语法。特别是，查询综合能重新绑定到一个使用 DLinq 基础结构或本地查询优化器的顺序操作符实现，该本地查询优化器将尝试在几个本地或远程数据源之间分发查询执行。这种底层顺序操作符的重新绑定与典型的 COM 提供程序模型的实质相似，通过此功能，同一接口的不同实现能够支持千变万化的操作和部署选择而不用覆盖应用程序代码。</P>
<P>基本的 Select...From...Where... 综合筛选出满足 Where 子句中谓词条件的所有值。前面的一个示例说明如何查找所有居民总数少于一百万的国家：</P><PRE class=codeSample>Dim SmallCountries = Select Country _
                     From Country In Countries _
                     Where Country.Population &lt; 1000000
</PRE>
<P>在顺序操作符内，标识符 It 绑定到当前“行”。像 Me 一样，It 的成员自动归属到作用域内。It 的概念对应于 XQuery 的上下文标识符“.”，它的使用类似于 SQL 中的“*”。例如，我们能够通过以下查询返回所有国家及其首都的集合：</P><PRE class=codeSample>Dim CountriesWithCapital = _
  Select It _
  From Country In Countries, City In Capitals _
  Where Country.Name = City.Country
</PRE>
<P>这个本地声明的推断类型是 IEnumerable(Of { Country As Country, City As City })。 </P>
<P>通过 Order By 子句，我们能够根据任意多个排序关键字对查询结果进行排序。例如，以下查询返回一个所有国家国名的列表，该列表按经度升序、人口降序的顺序排列：</P><PRE class=codeSample>Dim Sorted = Select Country.Name _
             From Country In Countries, City In Capitals _
             Where Country.Name = City.Country
             Order By City.Longtitude Asc, Country.Population Desc
</PRE>
<P><EM>聚合操作符</EM> 作用于集合之上，它将集合“聚合”成一个值，例如 Min、Max、Count、Avg、Sum……我们能够使用以下查询来计算小国的数量：</P><PRE class=codeSample>Dim N As Integer = _
  Select Count(Country) _
  From Country In Countries _
  Where Country.Population &lt; 1000000
</PRE>
<P>与 SQL 相似，我们为聚合提供了特殊的语法，用它来简化多个聚合操作非常方便。例如，要用一条语句统计小国数量并计算各国的平均密度，我们可以编写以下代码</P><PRE class=codeSample>Dim R As { Total As Integer, Density As Double } = _
  Select New { .Total = Count(Country), _
               .Density = Avg(Country.Population/Country.Area) } _
  From Country In Countries _
  Where Country.Population &lt; 1000000
</PRE>
<P>由于应用了编译器生成的聚合函数，这种聚合形式比没有任何聚合的正常结果集的结果更简短。</P>
<P>聚合函数最常出现在与划分源集合相结合的情况下。例如，我们可以将所有国家按其是否位于热带进行分组，<EM>然后</EM> 合计每组的数量。为了实现这个功能，我们引入 Group By 子句。辅助函数 IsTropical 封装了对一个 Country 是否具有热带气候的测试：</P><PRE class=codeSample>Partial Class Country
  Function IsTropical() As Boolean
    Return TropicOfCancer =&lt; Me.Latitude _
           AndAlso Me.Latitude &gt;= TropicOfCapricorn
  End Function
End Class
</PRE>
<P>给定该辅助函数后，我们就可以使用与前面完全相同的聚合函数，但首先，要将 Country 和 Capital 对的输入集合按 Country.IsTropical 是否相同划分成组。本示例可以划分为两组：一组包含热带国家 Palau、Belize 和 Madagascar；另一组包含非热带国家 Monaco。 </P>
<TABLE class=dataTable id=EIXAA cellSpacing=0 cellPadding=0>
<THEAD>
<TR class=stdHeader vAlign=top>
<TD id=colECBIXAA>关键字</TD>
<TD id=colEBBIXAA>国家</TD>
<TD id=colEABIXAA style="BORDER-RIGHT: #cccccc 1px solid">城市</TD></TR></THEAD>
<TBODY>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>Country.IsTropical() = True</P></TD>
<TD>
<P class=lastInCell>Palau</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>Koror</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>Country.IsTropical() = True</P></TD>
<TD>
<P class=lastInCell>Belize</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>Belmopan</P></TD></TR>
<TR class=record vAlign=top>
<TD>
<P class=lastInCell>Country.IsTropical() = True</P></TD>
<TD>
<P class=lastInCell>Madagascar</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>Antanarivo</P></TD></TR>
<TR class=evenRecord vAlign=top>
<TD>
<P class=lastInCell>Country.IsTropical() = False</P></TD>
<TD>
<P class=lastInCell>Monaco</P></TD>
<TD style="BORDER-RIGHT: #cccccc 1px solid">
<P class=lastInCell>Monaco</P></TD></TR></TBODY></TABLE>
<DIV class=dataTableBottomMargin></DIV>
<P>然后，通过计算总数和平均密度合计这些组中的值。现在，结果类型是一个 Total As Integer 和 Density As Double 对的集合： </P><PRE class=codeSample>Dim CountriesByClimate _
  As IEnumerable(Of Total As Integer, Density As Double }) =
    Select New { .Total = Count(Country), _
                 .Density = Avg(Country.Population/Country.Area) } _
    From Country In Countries, City In Capitals _
    Where Country.Name = City.Country
    Group By Country.IsTropical()
</PRE>
<P>以上查询在很大程度上隐藏了复杂性，实际上 Group By 子句的结果是一个 IEnumerable(Of Grouping(Of { Boolean, { Country As Country, City As City })) 类型的分组值集合，与上面的表格很像。每个这样的 Grouping 项都包含一个 Key 成员，该成员从关键字提取表达式 Country.IsTropical() 和一个 Group 中派生，该 Group 包含具有相同关键字提取表达式值的唯一的国家和城市集合。Visual Basic 编译器合成用户定义的聚合函数，给定这样一个分组，通过聚合每个分组，可以计算出所需的结果。</P>
<P>注意，前面示例中的每个 Group 都包含 Country 和 Capital，而我们只需要其中的 Country 来计算查询的最终结果。Group By 子句允许对组进行预选。例如，我们能够通过以下的综合将所有国家的名称按其所在半球进行划分：</P><PRE class=codeSample>Dim ByHemisphere As IEnumerable(Of Grouping(Of Boolean, String)) = _
  Select It _
  From Country In Countries, City In Capitals _
  Where Country.Name = City.Country
  Group Country.Name By City.Latitude &gt;= 0
</PRE>
<P>该结果将返回集合 { New Grouping { .Key = False, .Group = { "Madagascar", "Belize" }}, New Grouping { .Key = True, .Group = { "Palau" }}。</P>
<P>Visual Basic 9.0 中的<EM>查询综合</EM> 是完全<A href="http://plato.stanford.edu/entries/compositionality/" target=_blank>复合的</A>，这意味着它可以任意嵌套，而且只受静态类型规则的限制。采用复合使理解复杂的查询更容易，因为只要理解每个单独的子表达式即可。同时能更轻松地明确地定义语言的语义和类型规则。作为一种设计原则，它与作为 SQL 设计基础的那些原则截然不同。SQL 语言不是完全复合的，它有许多针对特殊用例的专门设计，这些特殊用例是由数据库在广泛应用中日积月累的经验演变而来的。但是，正是由于缺少完全复合性，使得只了解独立的片断，不可能从整体上理解复杂的 SQL 查询。 </P>
<P>SQL 缺少复合性的一个原因是，底层的关系数据模型本身就不是复合的。例如，表不可能包含子表，换句话说，所有的表必须是平面型的。因此，为了与 SQL 的数据模型保持一致，SQL 编程人员只能编写结果为平面表的单一复杂表达式，而不能将其分解成更小的单元。引用 Jim Gray 的一句话，“在计算机科学中，只有递归才是好的。”Visual Basic 基于 CLR 类型系统，因此不限制某些类型作为其他类型的组成部分出现。除了静态类型化规则，它对作为其他表达式组成部分出现的表达式类型没有限制。所以，像行、对象、XML 还有活动目录、文件、注册表项等都是查询源和查询结果中最好的类型。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=EUAA></A>
<H2>扩展方法 </H2>
<P>大部分 .NET Framework 标准查询基础结构的基本功能源自<EM>扩展方法</EM>。事实上，编译器会将所有查询综合直接转换为由作用域内命名空间定义的标准查询操作符扩展方法。扩展方法是通过自定义属性进行标记的共享方法，这些自定义属性允许方法通过实例-方法语法进行调用。它有效地扩展了现有类型，并通过其他方法构造类型。</P>
<P>扩展方法主要是为那些库设计人员服务的，所以 Visual Basic 不提供针对声明语言语法的直接支持。相反，编程人员直接将所需的自定义属性附加到模块和成员上，从而将两者标记为扩展方法。以下示例定义一个任意集合上的扩展方法Count：</P><PRE class=codeSample>&lt;System.Runtime.CompilerServices.Extension&gt; _
Module MyExtensions
  &lt;System.Runtime.CompilerServices.Extension&gt; _
  Function Count(Of T)([Me] As IEnumerable(Of T)) As Integer
    For Each Dim It In [Me]
      Count += 1
    Next
  End Function
End Module
</PRE>
<P>前面提到，尖括号语法是关键字转义符，它允许使用 Me 作为普通变量名。扩展方法是<EM>模拟</EM><EM></EM>实例方法的共享方法，那么像真正的实例方法那样使用标识符 Me 作为输入名是很方便的，但是由于该标识符是一个关键字，必须使用括号进行转义，因此实际上在共享方法中是不允许使用它的。</P>
<P>扩展方法只不过是常规的共享方法，只要显式提供将要操作的实例集合，就可以调用 Count 函数，这同调用 Visual Basic 中任何其他共享函数一样：</P>
<P>Dim TotalSmallCountries = _ MyExtensions.Count(Select Country _ From Country In Countries _ Where Country.Population &lt; 1000000)</P>
<P>扩展方法通过普通的 Imports 语句进入作用域。然后，和其他方法一样，它将以第一个参数赋予的类型出现。 </P><PRE class=codeSample>Imports MyExtensions

Dim TotalSmallCountries = _
  (Select Country _
   From Country In Countries _
   Where Country.Population &lt; 1000000).Count()
</PRE>
<P>扩展方法的优先级低于常规实例方法；如果正常处理一个调用表达式时没有发现可用的实例方法，那么编译器尝试将该调用解释为一个扩展方法调用。</P>
<P>但是，编写此项查询的最自然的方法是使用我们前面看到的聚合语法：</P><PRE class=codeSample>Dim TotalSmallCountries = _
   Select Count(Country) _
   From Country In Countries _
   Where Country.Population &lt; 1000000
</PRE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=ERAA></A>
<H2>嵌套函数</H2>
<P>许多标准查询操作符都定义为扩展方法，以参数形式代理 Func(Of S,T) 类型，例如 Where、Select、SelectMany 等。为了便于编译器将综合转换为底层查询操作符，或便于 Visual Basic 编程人员直接调用查询操作符，需要简单地创建代理。具体来说，我们需要能够创建所谓的<EM>闭包</EM> — 捕捉其环境上下文的代理。Visual Basic 创建闭包的机制通过嵌套的本地函数和子例程声明来完成。 </P>
<P>为了说明嵌套函数的使用，我们将调入在 System.Query 命名空间中定义的原始底层查询操作符。扩展方法之一是 TakeWhile 函数，在测试为真时它发送一个序列中的元素，然后忽略该序列中的其余元素。</P><PRE class=codeSample>&lt;Extension&gt; _
Shared Function TakeWhile(Of T) _
  (source As IEnumerable(Of T), Predicate As Func(Of T, Boolean)) _
    As IEnumerable(Of T)
</PRE>
<P>OrderByDescending 操作符根据经过证实的排序关键字按降序顺序排列其参数集合：</P><PRE class=codeSample>&lt;Extension&gt; _
Shared Function OrderByDescending (T, K As IComparable(Of K)) _
  (Source As IEnumerable(Of T), KeySelector As Func(Of T, K)) _
     As OrderedSequence(Of T)
</PRE>
<P>查找所有小国的另一种方法是，首先按人口将国家排序，然后通过 TakeWhile 挑选出所有居民总数不足一百万的国家。</P><PRE class=codeSample>Function Population(Country As Country) As Integer
  Return Country.Population
End Function

Function LessThanAMillion(Country As Country) As Boolean
  Return Country.Population &lt; 1000000
End Function

Dim SmallCountries = _
  Countries.OrderBy(AddresOf Population) _
           .TakeWhile(AddresOf LessThanAMillion)
</PRE>
<P>尽管查询综合不是必要的，但是 Visual Basic 也许支持直接使用匿名函数和子例程的语法（即所谓的“lambda 表达式”），编译器会将它们转换为本地函数声明。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=EOAA></A>
<H2>空类型</H2>
<P>关系数据库存在空值语义，但是空值语义通常与普通的编程语言不一致，也不为编程人员所熟知。在数据密集型应用程序中，程序准确无误且清晰明了地处理语义很关键。正是由于认识到了这种必要性，因此在“Whidbey”中，CLR 通过一般类型 Nullable(Of T As Struct) 添加了针对为空性的运行时支持。使用这种类型，我们能够声明可为空的值类型版本，例如 Integer、Boolean、Date 等等。Visual Basic 语法中的空类型是 T?，其原因将变得显而易见。</P>
<P>例如，鉴于并非所有国家都独立，我们将一个新成员添加到类 Country 中，该成员代表各个国家的独立日期（如果可用）：</P><PRE class=codeSample>Partial Class Country
  Public Property Independence As Date?
End Class 
</PRE>
<P>与数组类型一样，我们也可以在属性名中添加表示可为空值的修饰符，如以下声明所示：</P><PRE class=codeSample>Partial Class Country
  Public Property Independence? As Date
End Class 
</PRE>
<P>Palau 的独立日期是 #10/1/1994#，但是英国 Virgin Islands 是大不列颠联合王国的附属领土，因此其独立日期是 Nothing。</P><PRE class=codeSample>Dim Palau = _
  New Country { _
    .Name = "Palau", _
    .Area = 458, _
    .Population = 16952, _
    .Independence = #10/1/1994# }

Dim VirginIslands = _
  New Country { _
    .Name = "Virgin Islands", _
    .Area = 150, _
    .Population= 13195, _
    .Independence = Nothing }
</PRE>
<P>Visual Basic 9.0 将支持有关空值的三值逻辑和空传播算术，这意味着如果运算中的一个操作数为 Nothing，那么结果将为 Nothing，这样的运算包括算术、比较、逻辑、按位、移位、字符串或类型操作。如果两个操作数都是适当的值，那么将按操作数的基本值执行操作，并将结果转换为可为空。</P>
<P>Palau.Independence 和 VirginIslands.Independence 的类型都是 Date?，所以编译器将使用空传播算术计算下列减法，这样，本地声明 PLength 和 VILength 的推断类型都是 TimeSpan?。 </P><PRE class=codeSample>Dim PLength = #8/24/2005# - Palau.Independence          REM 3980.00:00:00
</PRE>
<P>因为操作数都不为 Nothing，所以 PLength 的值为 3980.00:00:00。另一方面，虽然 VILength 的结果仍然是 TimeSpan? 类型，但是由于 VirginIslands.Independence 的值为 Nothing，根据空传播法则，VILength 的值将为 Nothing。</P><PRE class=codeSample>Dim VILength = #8/24/2005# - VirginIslands.Independence REM Nothing
</PRE>
<P>与 SQL 中的运算符一样，比较运算符将使用空传播，逻辑运算符将使用三值逻辑。在 If 和 While 语句中，Nothing 被解释为 False，因此，在以下代码片断中，将执行 Else 分支：</P><PRE class=codeSample>If VILength &lt; TimeSpan.FromDays(10000)
  ...
Else
  ...
End If
</PRE>
<P>请注意，根据三值逻辑，等式将检查 X = Nothing，而 Nothing = X 总是计算为 Nothing；为了检查 X 是否为 Nothing，我们应该使用双值逻辑比较 X Is Nothing 和 Nothing Is X。</P>
<P>运行时处理可为空的值，特别是在 Object 中双向进行装箱和取消装箱操作时。当对一个表示 Nothing 的可为空的值（即 HasValue 属性为 False）进行装箱时，该值将装箱到空引用中。当对一个适当的值（即 HasValue 属性为 True）进行装箱时，首先将取消基础值包装，然后再进行装箱。因此，堆上的对象都不是是动态类型 Nullable(Of T)；所有可见类型都是 T。而且，我们能够将 Object 中的值取消装箱，设置为 T 或 Nullable(Of T) 类型。但是，这样做将导致晚期绑定不能动态地决定应该使用双值逻辑还是三值逻辑。例如，当我们执行一个两个数的早期绑定加法时，其中一个数为 Nothing，使用空传播后，结果为 Nothing：</P><PRE class=codeSample>Dim A As Integer? = Nothing
Dim B As Integer? = 4711
Dim C As Integer? = A+B REM C = Nothing 
</PRE>
<P>但是，在这两个值上使用晚期绑定加法时，结果却是 4711，这是由于晚期绑定使用的是双值逻辑，该逻辑依据的事实是：A 和 B 的动态类型都是 Integer，而不是 Integer?。因此 Nothing 被解释为 0：</P><PRE class=codeSample>Dim X As Object = A
Dim Y As Object = B
Dim Z As Object = X+Y REM Z = 4711
</PRE>
<P>为了确保语义正确，我们需要将编译器定向为使用空传播重载。 </P><PRE class=codeSample>Operator +(x As Object?, y As Object?) As Object?
</PRE>
<P>方法是通过“?”操作符将任意一个操作数转换为可为空值的类型：</P><PRE class=codeSample>Dim X As Object = A
Dim Y As Object = B
Dim Z As Object? = X?+Y REM Z = Nothing
</PRE>
<P>请注意，这意味着我们必须能够将任何类型 T 创建为 T?。底层的 CLR Nullable(Of T As Struct) 类型只强制参数类型为非空结构。当 T 是非空值类型时，Visual Basic 编译器将 T? 清除为 Nullable(Of T)；当 T 不是非空值类型时，编译器将其清除为 T。在 Visual Basic 程序内，这两种情况下的静态类型都是 T?，编译器会保留足够的内部元数据来记住这一点。</P>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=ELAA></A>
<H2>宽松的委托</H2>
<P>在 Visual Basic 8.0 中创建一个使用 AddressOf 或 Handles 的委托时，为了绑定到委托标识符，一种方法是必须精确匹配委托类型的签名。在下列示例中，OnClick 子例程的签名必须精确匹配事件处理程序委托 Delegate Sub EventHandler(sender As Object, e As EventArgs) 的签名，该签名在 Button 类型的后台声明：</P><PRE class=codeSample>Dim WithEvents B As New Button()

Sub OnClick(sender As Object, e As EventArgs) Handles B.Click
  MessageBox.Show("Hello World from" + B.Text)
End Sub
</PRE>
<P>然而，在调用<I>非委托</I> 函数和子例程时，Visual Basic 并不要求实参精确匹配正在尝试调用的这种方法。我们真正调用的 OnClick 子例程使用的实参类型是 Button 和类型 MouseEventArgs，两者分别是形参 Object 和 EventArgs 的子类型，如以下代码片断所示：</P><PRE class=codeSample>Dim M As New MouseEventArgs(MouseButtons.Left, 2, 47, 11,0) 
OnClick(B, M)
</PRE>
<P>相反，假设我们定义一个带有两个 Object 参数的子例程 RelaxedOnClick，然后，允许通过类型为 Object 和 EventArgs 的实参调用这个子例程：</P><PRE class=codeSample>Sub RelaxedOnClick(sender As Object, e As Object) Handles B.Click
  MessageBox.Show("Hello World from" + B.Text))
End Sub
Dim E As EventArgs = M
Dim S As Object = B
RelaxedOnClick(B,E)
</PRE>
<P>在 Visual Basic 9.0 中，绑定到委托放宽为与方法调用一致即可。也就是说，如果可以<EM>调用</EM> 这样的函数或子例程（实参与形参精确匹配，且该函数或子例程返回委托类型），就可以将其绑定到委托。换言之，委托定义和绑定与方法调用遵循同一个负载解析逻辑。</P>
<P>这意味着，在 Visual Basic 9.0 中，我们现在<I>能够</I> 将带有两个 Object 参数的子例程 RelaxedOnClick 绑定到 Button 的 Click 事件：</P><PRE class=codeSample>Sub RelaxedOnClick(sender As Object, e As Object) Handles B.Click
  MessageBox.Show(("Hello World from" + B.Text)
End Sub
</PRE>
<P>事件处理程序的 sender 和 EventArgs 这两个参数几乎无关紧要。而处理程序访问控件的状态，事件在控件上直接注册，并忽略它的两个参数。为了支持这个常见情况，委托可以放宽为不带参数，只要没有二义性的结果。换言之，我们可以只编写以下这段代码：</P><PRE class=codeSample>Sub RelaxedOnClick Handles B.Click
  MessageBox.Show("Hello World from" + B.Text)
End Sub
</PRE>
<P>在使用 AddressOf 或代理创建表达式，甚至在方法组是一个晚期绑定调用时，也可以应用委托放宽，这一点不难理解：</P><PRE class=codeSample>Dim F As EventHandler = AddressOf RelaxedOnClick
Dim G As New EventHandler(AddressOf B.Click)
</PRE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=EIAA></A>
<H2>动态接口（或强“鸭子类型”）</H2>
<P>在纯静态类型化语言中（例如 C#、Java 或带有 Option Strict On 的 Visual Basic），我们只能调用在编译时存在的目标表达式的成员。例如，下面第二个赋值语句理应导致一个编译时错误，因为类 Country 没有 Inflation 成员：</P><PRE class=codeSample>Dim Palau As Country = Countries(0)
Dim Inflation = Country.Inflation
</PRE>
<P>但是，在大多数情况下，我们有必要访问这样的成员，即使目标类型的静态类型是未知的。通过 Option Strict Off，Visual Basic 允许晚期绑定成员访问类型为 Object 的目标。尽管晚期绑定功能强大而且非常灵活，但是也付出了一定的代价。特别是，我们失去了智能感知和类型引用，因此需要转换或显式类型化以返回到早期绑定环境。</P>
<P>即使在进行晚期绑定调用时，我们通常也假定值粘附到一个特定的“接口”。只要对象可以满足接口，我们就会很高兴。动态语言社区称之为“鸭子类型”：如果它走起路来像鸭子，说出话来像鸭子，那么它就是一只鸭子。为了说明鸭子类型的概念，以下示例将随机返回一个 Country 或代表一个人的新匿名类型，两者都有一个类型为 String 的 Name 属性：</P><PRE class=codeSample>Function NamedThing() As Object
  Dim Static RandomNumbers = New Random()
  Dim I = RandomNumbers.Next(9) 
  If  I &lt; 5
    NamedThing = Countries(I)
  Else
    NamedThing = New{ .Name = "John Doe", .Age = 42-I }
  End If
End Function
Dim Name = CStr(NamedThing().Name)
</PRE>
<P>在进行晚期调用 CStr(NamedThing()) 时，我们假定 NamedThing() 返回的静态值有一个类型为 String 的 Name 成员。我们能够通过新功能<B><I>动态接口</I></B> 使这个假定显而易见。静态类型为动态接口的目标通常使用晚期绑定进行访问，但是成员访问却是静态类型化的。这意味着我们可以获得完全的智能感知和类型引用，而不必进行任何转换或显式类型化：</P><PRE class=codeSample>&lt;Dynamic&gt; _
Interface IHasName 
  Property Name As String
End Interface
Dim Thing As IHasName = NamedThing()
Dim Name = Thing.Name                REM Inferred As String.
</PRE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=EFAA></A>
<H2>动态标识符</H2>
<P>动态接口借用的现实是：程序员认为自己知道预期在晚期绑定调用中出现的成员的静态名称和签名。然而，在某些真正的动态情况下，我们甚至可能不知道想要静态调用的成员名称。动态标识符考虑到了极端的晚期绑定调用，调用表达式的标识符在该处是动态计算的。</P>
<P>下一个示例以三种不同的语言（英语、荷兰语和法语）声明三个类，每个类都有一个以各自的语言编写的“名字”字段：</P><PRE class=codeSample>Class English
  Name As String
End Class

Class Nederlands
  Naam As String
End Class
Class Français
  Nom As String
End Class
</PRE>
<P>为了访问 Person 中的这个“名字”字段，我们通过映射值来获得类型名，并查找表中的成员名。然后，我们就能够使用动态标识符访问来调用 Person 中的适当成员：</P><PRE class=codeSample>Dim Dictionary = New Dictionary(Of String, String) { _
    .Add("English", "Name"), _
    .Add("Nederlands", "Naam"), _
    .Add("Français", "Nom") }
Dim Person As Object = New Français { .Nom = "Eiffel" }
Dim Name As String = Person.(Dictionary(Person.GetType().Name))
</PRE>
<DIV style="MARGIN-TOP: 3px; MARGIN-BOTTOM: 10px"><A href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top"><IMG height=9 alt=返回页首 src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/arrow_px_up.gif" width=7 border=0></A><A class=topOfPage href="http://www.microsoft.com/china/msdn/library/langtool/vbnet/vb9overview.mspx#top">返回页首</A></DIV><A name=ECAA></A>
<H2>小结</H2>
<P>Visual Basic 9.0 引入各种各样的新特性。本文，我们以一系列关联的示例阐释了这些新特性，但下面的主题同样值得关注： </P>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>关系数据、对象数据和</B><B> XML </B><B>数据</B>。Visual Basic 9.0 统一了数据访问，使其独立于关系数据库、XML 文档或任意对象图形中的数据源，然而仍然能够在内存中持久保留或存储。这种统一包括类型、技术、工具和编程模式。特别灵活的 Visual Basic 语法使得将扩展（例如 XML 文字和类似 SQL 的查询综合）深入添加到语言内部很容易。这大大地减少了新 .NET 语言集成查询 API 的“表面区域”，通过智能感知和智能标记提高了数据访问功能的可发觉性，通过将外来语法从字符串数据提升到宿主语言中极大地改进了调试。将来，我们打算提高 XML 数据的一致性，甚至进一步借用 XSD 结构。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>通过所有静态类型化的优点提高动态性</B>。众所周知，静态类型化的优点是：在编译时而不是运行时标识 bug；通过早期绑定访问获得高性能；通过源代码中的显式声明获得清晰的语义等等。但是，有时动态类型化可以使代码更简短、更清晰且更灵活。如果一种语言不直接支持动态类型化，但是程序员需要使用它，那么他们必须通过映射、词典、调度表以及其他技术来实现动态结构的一砖一瓦。这就增加了 bug 出现的可能性，而且提高了维护成本。通过支持可能的静态类型化和需要的动态类型化，Visual Basic 为程序员提供了这两方面的优点。 </P></TD></TR>
<TR>
<TD class=listBullet vAlign=top>•</TD>
<TD class=listItem>
<P><B>减少程序员的认知负担</B>。像类型推断、对象初始值设定项和宽松的委托等特性极大地减少了代码冗余和异常规则数，这些异常规则往往是程序员需要学习、记忆或查找的，但是对性能却没有影响。诸如动态接口等特性甚至在晚期绑定的情况下也支持智能感知，这就极大地改进了高级功能的可发觉性。 </P></TD></TR></TBODY></TABLE>
<P>尽管 Visual Basic 9.0 新特性列表很长，但我们希望以上主题能够让您确信：使 Visual Basic 成为世界上最杰出的编程系统是一项连续的、及时的、专注的事业。我们也希望它能够激发您的想象力，使您和我们一样认识到这只是一个开端，更伟大的事业即将到来。</P>
<P><A href="http://msdn.microsoft.com/vbasic/default.aspx?pull=/library/en-us/dnvs05/html/vb9overview.asp" target=_blank>转到原英文页面</A></P></DIV>
<SCRIPT language=javascript>
					var framesValid = false; 
					if (window.name == "MNPMainFrame") 
					{ 
						var menuFrame = parent.frames["MNPMenuFrame"]; 
						if (menuFrame) { framesValid = true; } 
					} 
					if (!framesValid) 
						document.forms["MNPFramesForm"].submit();
					
					else 
						document.write('<layer visibility="hide"><div style="display:none"><img alt="" width="0" height="0" border="0" hspace="0" vspace="0" src="http://c.microsoft.com/trans_pixel.asp?source=www&amp;TYPE=PV&amp;p=china_msdn_library_langtool_vbnet&amp;URI=%2fchina%2fmsdn%2flibrary%2flangtool%2fvbnet%2fvb9overview.mspx&amp;GUID=1F4FC18C-F71E-47FB-8FC9-612F8EE59C61&amp;r=http%3a%2f%2fwww.microsoft.com%2fCHINA%2fmnp_utility.mspx%2fframesmenu%3furl%3d%2fchina%2fmsdn%2flibrary%2flangtool%2fvs2005%2fdnvsdev05vs05b1.mspx"></div></layer>');
					
					top.document.title = self.document.title;
				</SCRIPT>
<LAYER visibility="hide">
<DIV style="DISPLAY: none"><IMG height=0 alt="" hspace=0 src="http://c.microsoft.com/trans_pixel.asp?source=www&amp;TYPE=PV&amp;p=china_msdn_library_langtool_vbnet&amp;URI=%2fchina%2fmsdn%2flibrary%2flangtool%2fvbnet%2fvb9overview.mspx&amp;GUID=1F4FC18C-F71E-47FB-8FC9-612F8EE59C61&amp;r=http%3a%2f%2fwww.microsoft.com%2fCHINA%2fmnp_utility.mspx%2fframesmenu%3furl%3d%2fchina%2fmsdn%2flibrary%2flangtool%2fvs2005%2fdnvsdev05vs05b1.mspx" width=0 border=0></DIV></LAYER><img src ="http://www.cnitblog.com/kenlen/aggbug/4365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-11 21:48 <a href="http://www.cnitblog.com/kenlen/articles/4365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>搜索引擎研究---网络蜘蛛程序算法相关资料</title><link>http://www.cnitblog.com/kenlen/articles/4228.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Wed, 09 Nov 2005 14:43:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4228.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4228.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4228.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4228.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4228.html</trackback:ping><description><![CDATA[如何用C#语言构造蜘蛛程序 <BR>　"蜘蛛"（Spider）是Internet上一种很有用的程序，搜索引擎利用蜘蛛程序将Web页面收集到数据库，企业利用蜘蛛程序监视竞争对手的网站并跟踪变动，个人用户用蜘蛛程序下载Web页面以便脱机使用，开发者利用蜘蛛程序扫描自己的Web检查无效的链接……对于不同的用户，蜘蛛程序有不同的用途。那么，蜘蛛程序到底是怎样工作的呢？ <BR><BR>　　蜘蛛是一种半自动的程序，就象现实当中的蜘蛛在它的Web（蜘蛛网）上旅行一样，蜘蛛程序也按照类似的方式在Web链接织成的网上旅行。蜘蛛程序之所以是半自动的，是因为它总是需要一个初始链接（出发点），但此后的运行情况就要由它自己决定了，蜘蛛程序会扫描起始页面包含的链接，然后访问这些链接指向的页面，再分析和追踪那些页面包含的链接。从理论上看，最终蜘蛛程序会访问到Internet上的每一个页面，因为Internet上几乎每一个页面总是被其他或多或少的页面引用。 <BR><BR>　　本文介绍如何用C#语言构造一个蜘蛛程序，它能够把整个网站的内容下载到某个指定的目录，程序的运行界面如图一。你可以方便地利用本文提供的几个核心类构造出自己的蜘蛛程序。 <BR>　　C#特别适合于构造蜘蛛程序，这是因为它已经内置了HTTP访问和多线程的能力，而这两种能力对于蜘蛛程序来说都是非常关键的。下面是构造一个蜘蛛程序要解决的关键问题： <BR><BR>　　⑴ HTML分析：需要某种HTML解析器来分析蜘蛛程序遇到的每一个页面。 <BR><BR>　　⑵ 页面处理：需要处理每一个下载得到的页面。下载得到的内容可能要保存到磁盘，或者进一步分析处理。 <BR><BR>　　⑶ 多线程：只有拥有多线程能力，蜘蛛程序才能真正做到高效。 <BR><BR>　　⑷ 确定何时完成：不要小看这个问题，确定任务是否已经完成并不简单，尤其是在多线程环境下。 <BR><BR>　　一、HTML解析 <BR><BR>　　C#语言本身不包含解析HTML的能力，但支持XML解析；不过，XML有着严格的语法，为XML设计的解析器对HTML来说根本没用，因为HTML的语法要宽松得多。为此，我们需要自己设计一个HTML解析器。本文提供的解析器是高度独立的，你可以方便地将它用于其它用C#处理HTML的场合。 <BR><BR>　　本文提供的HTML解析器由ParseHTML类实现，使用非常方便：首先创建该类的一个实例，然后将它的Source属性设置为要解析的HTML文档： <BR><BR>ParseHTML parse = new ParseHTML();parse.Source = " 
<P>Hello World</P>";<BR><BR><BR><BR>　　接下来就可以利用循环来检查HTML文档包含的所有文本和标记。通常，检查过程可以从一个测试Eof方法的while循环开始： <BR><BR>while(!parse.Eof()){char ch = parse.Parse();<BR><BR><BR><BR>　　Parse方法将返回HTML文档包含的字符--它返回的内容只包含那些非HTML标记的字符，如果遇到了HTML标记，Parse方法将返回0值，表示现在遇到了一个HTML标记。遇到一个标记之后，我们可以用GetTag()方法来处理它。 <BR><BR>if(ch==0){HTMLTag tag = parse.GetTag();}<BR><BR><BR><BR>　　一般地，蜘蛛程序最重要的任务之一就是找出各个HREF属性，这可以借助C#的索引功能完成。例如，下面的代码将提取出HREF属性的值（如果存在的话）。 <BR><BR>Attribute href = tag["HREF"];string link = href.Value;<BR><BR><BR><BR>　　获得Attribute对象之后，通过Attribute.Value可以得到该属性的值。 <BR><BR>二、处理HTML页面 <BR><BR>　　下面来看看如何处理HTML页面。首先要做的当然是下载HTML页面，这可以通过C#提供的HttpWebRequest类实现： <BR><BR>HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_uri);response = request.GetResponse();stream = response.GetResponseStream();<BR><BR><BR><BR>　　接下来我们就从request创建一个stream流。在执行其他处理之前，我们要先确定该文件是二进制文件还是文本文件，不同的文件类型处理方式也不同。下面的代码确定该文件是否为二进制文件。 <BR><BR>if( !response.ContentType.ToLower().StartsWith("text/") ){SaveBinaryFile(response);return null;}string buffer = "",line;<BR><BR><BR><BR>　　如果该文件不是文本文件，我们将它作为二进制文件读入。如果是文本文件，首先从stream创建一个StreamReader，然后将文本文件的内容一行一行加入缓冲区。 <BR><BR>reader = new StreamReader(stream);while( (line = reader.ReadLine())!=null ){buffer+=line+"\r\n";}<BR><BR><BR><BR>　　装入整个文件之后，接着就要把它保存为文本文件。 <BR><BR>SaveTextFile(buffer);<BR><BR><BR><BR>　　下面来看看这两类不同文件的存储方式。 <BR><BR>　　二进制文件的内容类型声明不以"text/"开头，蜘蛛程序直接把二进制文件保存到磁盘，不必进行额外的处理，这是因为二进制文件不包含HTML，因此也不会再有需要蜘蛛程序处理的HTML链接。下面是写入二进制文件的步骤。 <BR><BR>　　首先准备一个缓冲区临时地保存二进制文件的内容。 byte []buffer = new byte[1024];<BR><BR><BR><BR>　　接下来要确定文件保存到本地的路径和名称。如果要把一个myhost.com网站的内容下载到本地的c:\test文件夹，二进制文件的网上路径和名称是http://myhost.com/images/logo.gif，则本地路径和名称应当是c:\test\images\logo.gif。与此同时，我们还要确保c:\test目录下已经创建了images子目录。这部分任务由convertFilename方法完成。 <BR><BR>string filename = convertFilename( response.ResponseUri );<BR><BR><BR><BR>　　convertFilename方法分离HTTP地址，创建相应的目录结构。确定了输出文件的名字和路径之后就可以打开读取Web页面的输入流、写入本地文件的输出流。 <BR><BR>Stream outStream = File.Create( filename );Stream inStream = response.GetResponseStream();<BR><BR><BR><BR>　　接下来就可以读取Web文件的内容并写入到本地文件，这可以通过一个循环方便地完成。 <BR><BR>int l;do{l = inStream.Read(buffer,0,buffer.Length);if(l&gt;0)outStream.Write(buffer,0,l);} while(l&gt;0);<BR><BR><BR><BR>　　写入整个文件之后，关闭输入流、输出流。 <BR><BR>outStream.Close();inStream.Close();<BR><BR><BR><BR>　　比较而言，下载文本文件更容易一些。文本文件的内容类型总是以"text/"开头。假设文件已被下载并保存到了一个字符串，这个字符串可以用来分析网页包含的链接，当然也可以保存为磁盘上的文件。下面代码的任务就是保存文本文件。 <BR><BR>string filename = convertFilename( m_uri );StreamWriter outStream = new StreamWriter( filename );outStream.Write(buffer);outStream.Close();<BR><BR><BR><BR>　　在这里，我们首先打开一个文件输出流，然后将缓冲区的内容写入流，最后关闭文件。 <BR><BR>　　三、多线程 <BR><BR>　　多线程使得计算机看起来就象能够同时执行一个以上的操作，不过，除非计算机包含多个处理器，否则，所谓的同时执行多个操作仅仅是一种模拟出来的效果--靠计算机在多个线程之间快速切换达到"同时"执行多个操作的效果。一般而言，只有在两种情况下多线程才能事实上提高程序运行的速度。第一种情况是计算机拥有多个处理器，第二种情况是程序经常要等待某个外部事件。 <BR><BR>　　对于蜘蛛程序来说，第二种情况正是它的典型特征之一，它每发出一个URL请求，总是要等待文件下载完毕，然后再请求下一个URL。如果蜘蛛程序能够同时请求多个URL，显然能够有效地减少总下载时间。 <BR><BR>　　为此，我们用DocumentWorker类封装所有下载一个URL的操作。每当一个DocumentWorker的实例被创建，它就进入循环，等待下一个要处理的URL。下面是DocumentWorker的主循环： <BR><BR>while(!m_spider.Quit ){m_uri = m_spider.ObtainWork();m_spider.SpiderDone.WorkerBegin();string page = GetPage();if(page!=null)ProcessPage(page);m_spider.SpiderDone.WorkerEnd();}<BR><BR><BR><BR>　　这个循环将一直运行，直至Quit标记被设置成了true（当用户点击"Cancel"按钮时，Quit标记就被设置成true）。在循环之内，我们调用ObtainWork获取一个URL。ObtainWork将一直等待，直到有一个URL可用--这要由其他线程解析文档并寻找链接才能获得。Done类利用WorkerBegin和WorkerEnd方法来确定何时整个下载操作已经完成。 <BR><BR>　　从图一可以看出，蜘蛛程序允许用户自己确定要使用的线程数量。在实践中，线程的最佳数量受许多因素影响。如果你的机器性能较高，或者有两个处理器，可以设置较多的线程数量；反之，如果网络带宽、机器性能有限，设置太多的线程数量其实不一定能够提高性能。 <BR><BR>　　四、任务完成了吗？ <BR><BR>　　利用多个线程同时下载文件有效地提高了性能，但也带来了线程管理方面的问题。其中最复杂的一个问题是：蜘蛛程序何时才算完成了工作？在这里我们要借助一个专用的类Done来判断。 <BR><BR>　　首先有必要说明一下"完成工作"的具体含义。只有当系统中不存在等待下载的URL，而且所有工作线程都已经结束其处理工作时，蜘蛛程序的工作才算完成。也就是说，完成工作意味着已经没有等待下载和正在下载的URL。 <BR><BR>　　Done类提供了一个WaitDone方法，它的功能是一直等待，直到Done对象检测到蜘蛛程序已完成工作。下面是WaitDone方法的代码。 <BR><BR>public void WaitDone(){Monitor.Enter(this);while ( m_activeThreads&gt;0 ){Monitor.Wait(this);}Monitor.Exit(this);}<BR><BR><BR><BR>　　WaitDone方法将一直等待，直到不再有活动的线程。但必须注意的是，下载开始的最初阶段也没有任何活动的线程，所以很容易造成蜘蛛程序一开始就立即停止的现象。为解决这个问题，我们还需要另一个方法WaitBegin来等待蜘蛛程序进入"正式的"工作阶段。一般的调用次序是：先调用WaitBegin，再接着调用WaitDone，WaitDone将等待蜘蛛程序完成工作。下面是WaitBegin的代码： <BR><BR>public void WaitBegin(){Monitor.Enter(this);while ( !m_started ){Monitor.Wait(this);}Monitor.Exit(this);}<BR><BR><BR><BR>　　WaitBegin方法将一直等待，直到m_started标记被设置。m_started标记是由WorkerBegin方法设置的。工作线程在开始处理各个URL之时，会调用WorkerBegin；处理结束时调用WorkerEnd。WorkerBegin和WorkerEnd这两个方法帮助Done对象确定当前的工作状态。下面是WorkerBegin方法的代码： <BR><BR>public void WorkerBegin(){Monitor.Enter(this);m_activeThreads++;m_started = true;Monitor.Pulse(this);Monitor.Exit(this);}<BR><BR><BR><BR>　　WorkerBegin方法首先增加当前活动线程的数量，接着设置m_started标记，最后调用Pulse方法以通知（可能存在的）等待工作线程启动的线程。如前所述，可能等待Done对象的方法是WaitBegin方法。每处理完一个URL，WorkerEnd方法会被调用： <BR><BR>public void WorkerEnd(){Monitor.Enter(this);m_activeThreads--;Monitor.Pulse(this);Monitor.Exit(this);}<BR><BR><BR><BR><BR><BR>　　WorkerEnd方法减小m_activeThreads活动线程计数器，调用Pulse释放可能在等待Done对象的线程--如前所述，可能在等待Done对象的方法是WaitDone方法。 <BR><BR>　　结束语：本文介绍了开发Internet蜘蛛程序的基础知识，下面提供的源代码将帮助你进一步深入理解本文的主题。这里提供的代码非常灵活，你可以方便地将它用于自己的程序。<BR><BR>创建智能网络蜘蛛——如何使用Java网络对象和HTML对象(翻译)<BR>作者：Mark O. Pendergast<BR><BR>原文：http://www.javaworld.com/javaworld/jw-11-2004/jw-1101-spider.html<BR><BR>摘要<BR>你是否想过创建自己的符合特定标准的网站数据库呢？网络蜘蛛，有时也称为网络爬虫，是一些根据网络链接从一个网站到另外一个网站，检查内容和记录位置的程序。商业搜索站点使用网络蜘蛛丰富它们的数据库，研究人员可以使用蜘蛛获得相关的信息。创建自己的蜘蛛搜索的内容、主机和网页特征，比如文字密度和内置的多媒体内容。这篇文章将告诉你如何使用Java的HTML和网络类来创建你自己的功能强大的网络蜘蛛。<BR><BR>这篇文章将介绍如何在标准Java网络对象的基础上创建一个智能的网络蜘蛛。蜘蛛的核心是一个基于关键字/短语标准和网页特征进行深入网络搜索的递归程序。搜索过程在图形上类似于JTree结构。我主要介绍的问题，例如处理相关的URL，防止循环引用和监视内存/堆栈使用。另外，我将介绍再访问和分解远程网页中如何正确是用Java网络对象。<BR><BR>● 蜘蛛示例程序<BR><BR>示例程序包括用户界面类SpiderControl、网络搜索类Spider，两个用作创建JTree显示结果的类UrlTreeNode和UrlNodeRenderer，和两个帮助验证用户界面中数字输入的类IntegerVerifier和VerifierListener。文章末尾的资源中有完整代码和文档的琏接。<BR><BR>SpiderControl界面由三个属性页组成，一个用来设置搜索参数，另一个显示结果搜索树(JTree)，第三个显示错误和状态信息，如图1<BR><BR><BR><BR><BR>图1 搜索参数属性页<BR><BR><BR>搜索参数包括访问网站的最大数量，搜索的最大深度(链接到链接到链接)，关键字/短语列表，搜索的顶级主机，起始网站或者门户。一旦用户输入了搜索参数，并按下开始按钮，网络搜索将开始，第二个属性页将显示搜索的进度。<BR><BR><BR>图2 搜索树<BR><BR>一个Spider类的实例以独立进程的方式执行网络搜索。独立进程的使用是为了SpiderControl模块可以不断更新搜索树显示和处理停止搜索按钮。当Spider运行时，它不断在第二个属性页中为JTree增加节点(UrlTreeNode)。包含关键字和短语的搜索树节点以蓝色显示(UrlNodeRenderer)。<BR><BR>当搜索完成以后，用户可以查看站点的统计，还可以用外部浏览器(默认是位于Program Files目录的Internet Explorer)查看站点。统计包括关键字出现次数，总字符数，总图片数和总链接数。<BR><BR>● Spider类<BR><BR>Spider类负责搜索给出起点(入口)的网络，一系列的关键字和主机，和搜索深度和大小的限制。Spider继承了Thread，所以可以以独立线程运行。这允许SpiderControl模块不断更新搜索树显示和处理停止搜索按钮。<BR><BR>构造方法接受包含对一个空的JTree和一个空的JtextArea引用的搜索参数。JTree被用作创建一个搜索过程中的分类站点记录。这样为用户提供了可见的反馈，帮助跟踪Spdier循环搜索的位置。JtextArea显示错误和过程信息。<BR><BR>构造器将参数存放在类变量中，使用UrlNodeRenderer类初始化显示节点的JTree。直到SpiderControl调用run()方法搜索才开始。<BR><BR>run()方法以独立的线程开始执行。它首先判断入口站点是否是一个Web引用(以http，ftp或者www开始)或是一个本地文件引用。它接着确认入口站点是否具有正确的符号，重置运行统计，接着调用searchWeb()开始搜索：<BR><BR>public void run()<BR>{<BR>DefaultTreeModel treeModel = (DefaultTreeModel)searchTree.getModel(); // get our model<BR>DefaultMutableTreeNode root = (DefaultMutableTreeNode)treeModel.getRoot();<BR>String urllc = startSite.toLowerCase();<BR>if(!urllc.startsWith("http://") &amp;&amp; !urllc.startsWith("ftp://") &amp;&amp;<BR>!urllc.startsWith("www."))<BR><BR>{<BR>startSite = "file:///"+startSite; // Note you must have 3 slashes !<BR><BR>}<BR>else // Http missing ?<BR>if(urllc.startsWith("www."))<BR>{<BR>startSite = "http://"+startSite; // Tack on http:// <BR>}<BR><BR>startSite = startSite.replace('\\', '/'); // Fix bad slashes<BR>sitesFound = 0;<BR>sitesSearched = 0;<BR>updateStats();<BR>searchWeb(root,startSite); // Search the Web<BR>messageArea.append("Done!\n\n");<BR>}<BR><BR>searchWeb()是一个接受搜索树父节点和搜索Web地址参数的递归方法。searchWeb()首先检查给出的站点是否已被访问和未被执行的搜索深度和站点。SearchWeb()接着允许SpiderControl运行(更新界面和检查停止搜索按钮是否按下)。如果所有正常，searchWeb()继续，否则返回。<BR><BR>在searchWeb()开始读和解析站点以前，它首先检验基于站点创建的URL对象是否具有正确的类型和主机。URL协议被检查来确认它是一个HTML地址或者一个文件地址(不必搜索mailt和其他协议)。接着检查文件扩展名(如果当前有)来确认它是一个HTML文件(不必解析pdf或者gif文件)。一旦这些工作完成，通过isDomainOk()方法检查根据用户指定的列表检查主机：<BR><BR>...URL url = new URL(urlstr); // Create the URL object from a string.<BR>String protocol = url.getProtocol(); // Ask the URL for its protocol<BR>if(!protocol.equalsIgnoreCase("http") &amp;&amp; !protocol.equalsIgnoreCase("file"))<BR>{<BR>messageArea.append(" Skipping : "+urlstr+" not a http site\n\n");<BR>return;<BR>}<BR><BR>String path = url.getPath(); // Ask the URL for its path<BR>int lastdot = path.lastIndexOf("."); // Check for file extension<BR>if(lastdot &gt; 0)<BR>{<BR>String extension = path.substring(lastdot); // Just the file extension<BR>if(!extension.equalsIgnoreCase(".html") &amp;&amp; !extension.equalsIgnoreCase(".htm"))<BR>return; // Skip everything but html files<BR>}<BR><BR>if(!isDomainOk(url))<BR><BR>{<BR><BR>messageArea.append(" Skipping : "+urlstr+" not in domain list\n\n");<BR>return;<BR>}<BR><BR>这里，searchWeb()公平的确定它是否有值得搜索的URL，接着它为搜索树创建一个新节点，添加到树中，打开一个输入流解析文件。下面的章节涉及很多关于解析HTML文件，处理相关URL和控制递归的细节。<BR><BR>1、解析HTML文件<BR><BR>这里有两个为了查找A HREF来解析HTML文件方法——一个麻烦的方法和一个简单的方法。<BR><BR>如果你选择麻烦的方法，你将使用Java的StreamTokenizer类创建你自己的解析规则。使用这些技术，你必须为StreamTokenizer对象指定单词和空格，接着去掉&lt;和&gt;符号来查找标签，属性，在标签之间分割文字。太多的工作要做。<BR><BR>简单的方法是使用内置的ParserDelegator类，一个HTMLEditorKit.Parser抽象类的子类。这些类在Java文档中没有完善的文档。使用ParserDelegator有三个步骤：首先为你的URL创建一个InputStreamReader对象，接着创建一个ParserCallback对象的实例，最后创建一个ParserDelegator对象的实例并调用它的public方法parse()：<BR><BR><BR><BR>UrlTreeNode newnode = new UrlTreeNode(url); // Create the data node <BR>InputStream in = url.openStream(); // Ask the URL object to create an input stream<BR>InputStreamReader isr = new InputStreamReader(in); // Convert the stream to a reader<BR>DefaultMutableTreeNode treenode = addNode(parentnode, newnode); <BR>SpiderParserCallback cb = new SpiderParserCallback(treenode); // Create a callback object<BR>ParserDelegator pd = new ParserDelegator(); // Create the delegator<BR>pd.parse(isr,cb,true); // Parse the stream<BR>isr.close(); // Close the stream<BR>parse()接受一个InputStreamReader，一个ParseCallback对象实例和一个指定CharSet标签是否忽略的标志。parse()方法接着读和解码HTML文件，每次完成解码一个标签或者HTML元素后调用ParserCallback对象的方法。<BR><BR>在示例代码中，我实现了ParserCallback作为Spider的一个内部类，这样就允许ParseCallback访问Spider的方法和属性。基于ParserCallback的类可以覆盖下面的方法：<BR><BR>■ handleStartTag()：当遇到起始HTML标签时调用，比如&gt;A &lt;<BR><BR>■ handleEndTag()：当遇到结束HTML标签时调用，比如&gt;/A&lt;<BR><BR>■ handleSimpleTag()：当遇到没有匹配结束标签时调用<BR><BR>■ handleText()：当遇到标签之间的文字时调用<BR><BR><BR>在示例代码中，我覆盖了handleSimpleTag()以便我的代码可以处理HTML的BASE和IMG标签。BASE标签告诉当处理相关的URL引用时使用什么URL。如果没有BASE标签出现，那么当前URL就用来处理相关的引用。HandleSimpleTag()接受三个参数，一个HTML.Tag对象，一个包含所有标签属性的MutableAttributeSet，和在文件中的相应位置。我的代码检查标签来判断它是否是一个BASE对象实例，如果是则HREF属性被提取出来并保存在页面的数据节点中。这个属性以后在处理链接站点的URL地址中被用到。每次遇到IMG标签，页面图片数就被更新。<BR><BR>我覆盖了handleStartTag以便程序可以处理HTML的A和TITLE标签。方法检查t参数是否是一个事实上的A标签，如果是则HREF属性将被提取出来。<BR><BR>fixHref()被用作清理大量的引用(改变反斜线为斜线，添加缺少的结束斜线)，链接的URL通过使用基础URL和引用创建URL对象来处理。接着递归调用searchWeb()来处理链接。如果方法遇到TITLE标签，它就清除存储最后遇到文字的变量以便标题的结束标记具有正确的值(有时网页的title标签之间没有标题)。<BR><BR>我覆盖了handleEndTag()以便HTML的TITLE结束标记可以被处理。这个结束标记指出前面的文字(存在lastText中)是页面的标题文字。这个文字接着存在页面的数据节点中。因为添加标题信息到数据节点中将改变树中数据节点的显示，nodeChanged()方法必须被调用以便树可以更新。<BR><BR>我覆盖了handleText()方法以便HTML页面的文字可以根据被搜索的任意关键字或者短语来检查。HandleText()接受一个包含一个子符数组和该字符在文件中位置作为参数。HandleText()首先将字符数组转换成一个String对象，在这个过程中全部转换为大写。接着在搜索列表中的每个关键字/短语根据String对象的indexof()方法来检查。如果indexof()返回一个非负结果，则关键字/短语在页面的文字中显示。如果关键字/短语被显示，匹配被记录在匹配列表的节点中，统计数据被更新：<BR><BR>public class SpiderParserCallback extends HTMLEditorKit.ParserCallback {<BR><BR>/**<BR><BR>* Inner class used to html handle parser callbacks<BR><BR>*/<BR><BR><BR>public class SpiderParserCallback extends HTMLEditorKit.ParserCallback {<BR><BR>/** URL node being parsed */<BR><BR>private UrlTreeNode node;<BR><BR>/** Tree node */<BR><BR>private DefaultMutableTreeNode treenode;<BR><BR>/** Contents of last text element */<BR><BR>private String lastText = "";<BR><BR>/**<BR><BR>* Creates a new instance of SpiderParserCallback<BR><BR>* @param atreenode search tree node that is being parsed<BR>*/<BR><BR>public SpiderParserCallback(DefaultMutableTreeNode atreenode) {<BR><BR>treenode = atreenode;<BR>node = (UrlTreeNode)treenode.getUserObject();<BR><BR>}<BR><BR>/**<BR>* Handle HTML tags that don't have a start and end tag<BR>* @param t HTML tag<BR>* @param a HTML attributes<BR>* @param pos Position within file<BR>*/ <BR>public void handleSimpleTag(HTML.Tag t,<BR><BR>MutableAttributeSet a,<BR>int pos)<BR><BR>{<BR>if(t.equals(HTML.Tag.IMG))<BR><BR>{<BR>node.addImages(1);<BR>return;<BR>}<BR><BR>if(t.equals(HTML.Tag.BASE))<BR>{<BR>Object value = a.getAttribute(HTML.Attribute.HREF);<BR><BR>if(value != null)<BR>node.setBase(fixHref(value.toString())); <BR>} <BR>}<BR><BR>/**<BR><BR>* Take care of start tags<BR><BR>* @param t HTML tag<BR><BR>* @param a HTML attributes<BR><BR>* @param pos Position within file<BR>*/<BR>public void handleStartTag(HTML.Tag t,<BR><BR>MutableAttributeSet a,<BR><BR>int pos)<BR>{<BR>if(t.equals(HTML.Tag.TITLE))<BR>{<BR><BR>lastText="";<BR>return;<BR><BR>}<BR><BR>if(t.equals(HTML.Tag.A))<BR><BR>{<BR><BR>Object value = a.getAttribute(HTML.Attribute.HREF);<BR>if(value != null)<BR>{<BR>node.addLinks(1); <BR>String href = value.toString();<BR>href = fixHref(href);<BR>try{<BR>URL referencedURL = new URL(node.getBase(),href);<BR>searchWeb(treenode, referencedURL.getProtocol()+"://"+referencedURL.getHost()+referencedURL.getPath());<BR>}<BR>catch (MalformedURLException e)<BR><BR>{<BR>messageArea.append(" Bad URL encountered : "+href+"\n\n"); return; <BR>}<BR>}<BR>}<BR>}<BR>/**<BR>* Take care of start tags<BR>* @param t HTML tag<BR>* @param pos Position within file<BR><BR>*/<BR>public void handleEndTag(HTML.Tag t,<BR>int pos)<BR><BR>{<BR>if(t.equals(HTML.Tag.TITLE) &amp;&amp; lastText != null)<BR>{<BR>node.setTitle(lastText.trim());<BR>DefaultTreeModel tm = (DefaultTreeModel)searchTree.getModel();<BR><BR>tm.nodeChanged(treenode);<BR><BR>}<BR><BR>}<BR><BR>/**<BR><BR>* Take care of text between tags, check against keyword list for matches, if<BR>* match found, set the node match status to true<BR>* @param data Text between tags<BR>* @param pos position of text within Webpage<BR>*/<BR>public void handleText(char[] data, int pos)<BR>{<BR><BR>lastText = new String(data);<BR>node.addChars(lastText.length());<BR>String text = lastText.toUpperCase();<BR>for(int i = 0; i &lt; keywordList.length; i++)<BR>{<BR>if(text.indexOf(keywordList) &gt;= 0)<BR>{<BR>if(!node.isMatch())<BR>{<BR>sitesFound++;<BR>updateStats();<BR>}<BR>node.setMatch(keywordList); <BR>return;<BR>}<BR>}<BR>}<BR><BR>}<BR><BR>2、处理和补全URL<BR><BR>当遇到相关页面的链接，你必须在它们基础URL上创建完整的链接。基础URL可能通过BASE标签在页面中明确的定义，或者暗含在当前页面的链接中。Java的URL对象为你解决这个问题提供了构造器，提供了根据它的链接结构创建相似的。<BR>URL(URL context, String spec)接受spec参数的链接和context参数的基础链接。如果spec是一个相关链接，构建器将使用context来创建一个完整引用的URL对象。URL它推荐URL遵循严格的(Unix)格式。使用反斜线，在Microsoft Windows中，而不是斜线，将是错误的引用。如果spec或者context指向一个目录(包含index.html或default.html)，而不是一个HTML文件，它必须有一个结束斜线。fixHref()方法检查这些引用并且修正它们：<BR><BR><BR>public static String fixHref(String href)<BR>{<BR><BR>String newhref = href.replace('\\', '/'); // Fix sloppy Web references<BR><BR>int lastdot = newhref.lastIndexOf('.');<BR><BR>int lastslash = newhref.lastIndexOf('/');<BR>if(lastslash &gt; lastdot)<BR>{<BR>if(newhref.charAt(newhref.length()-1) != '/')<BR>newhref = newhref+"/"; // Add missing /<BR><BR>}<BR>return newhref; <BR><BR>}<BR><BR><BR>3、 控制递归<BR><BR>searchWeb()开始是为了搜索用户指定的起始Web地址而被调用的。它接着在遇到HTML链接时调用自身。这形成了深度优先搜索的基础，也带来了两种问题。首先非常危险的内存/堆栈溢出问题将因为太多的递归调用而产生。如果出现环形的引用，这个问题就将发生，也就是说，一个页面链接另外一个链接回来的连接，这是WWW中常见的事情。为了预防这种现象，searchWeb()检查搜索树(通过urlHasBeenVisited()方法)来确定是否引用的页面已经存在。如果已经存在，这个链接将被忽略。如果你选择实现一个没有搜索树的蜘蛛，你仍然必须维护一个以访问站点的列表(在Vector或数组中)以便你可以判断是否你正在重复访问站点。<BR><BR>递归的第二个问题来自深度优先的搜索和WWW的结构。根据选择的入口，深度优先的搜索在初始页面的初始链接在完成处理以前造成大量的递归调用。这就造成了两种不需要的结果：首先内存/堆栈溢出可能发生，第二被搜索过的页面可能很久才被从初始入口众多的结果中删除。为了控制这些，我为蜘蛛添加了最大搜索深度设置。用户可以选择可以达到的深度等级(链接到链接到链接)，当遇到每个链接时，当前深度通过调用depthLimitExceeded()方法进行检查。如果达到限制，链接就被忽略。测试仅仅检查JTree中节点的级别。<BR><BR>示例程序也增加了站点限制，用户来指定，可以在特定数目的URL被检查以后停止搜索，这样确保程序可以最后停止！站点限制通过一个简单的数字计数器sitesSearched来控制，这个数字每次调用searchWeb()后都被更新和检查。<BR><BR>4、UrlTreeNode和UrlNodeRenderer<BR><BR>UrlTreeNode和UrlNodeRenderer是用来在SpiderControl用户界面中创建JTree中个性化的树节点的类。UrlTreeNode包含每个搜索过的站点钟的URL信息和统计数据。UrlTreeNode以作为用户对象属性的标准DefaultMutableTreeNode对象形式存储在JTree中。数据包括节点中跟踪关键字出现的能力，节点的URL，节点的基础URL，链接的数量，图片的数量和字符的个数，以及节点是否符合搜索规则。<BR><BR>UrlTreeNodeRenderer是DefaultTreeCellRenderer界面的实现。UrlTreeNodeRenderer使节点包含匹配关键字显示为蓝色。UrlTreeNodeRenderer也为JtreeNodes加入了个性化的图标。个性化的显示通过覆盖getTreeCellRendererComponent()方法(如下)实现。这个方法在树中创建了一个Component对象。大部分的Component属性通过子类来进行设置，UrlTreeNodeRenderer改变了文字的颜色(前景色)和图标：<BR><BR><BR><BR>public Component getTreeCellRendererComponent(<BR>JTree tree,<BR>Object value,<BR>boolean sel,<BR>boolean expanded,<BR>boolean leaf,<BR>int row,<BR>boolean hasFocus) {<BR><BR>super.getTreeCellRendererComponent(<BR><BR>tree, value, sel,<BR>expanded, leaf, row,<BR>hasFocus);<BR>UrlTreeNode node = (UrlTreeNode)(((DefaultMutableTreeNode)value).getUserObject());<BR>if (node.isMatch()) // Set color<BR>setForeground(Color.blue);<BR>else <BR>setForeground(Color.black);<BR><BR>if(icon != null) // Set a custom icon<BR>{<BR>setOpenIcon(icon);<BR>setClosedIcon(icon);<BR>setLeafIcon(icon);<BR>}<BR>return this;<BR>}<BR><BR>5、 总结<BR><BR>这篇文章向你展示了如何创建网络蜘蛛和控制它的用户界面。用户界面使用JTree来跟踪蜘蛛的进展和记录访问过的站点。当然，你也可以使用Vector来记录访问过的站点和使用一个简单的计数器来显示进展。其他增强可以包含通过数据库记录关键字和站点的接口，增加通过多个入口搜索的能力，用大量或者很少的文字内容来显现站点，以及为搜索引擎提供同义搜索的能力。<BR><BR>这篇文章中展示的Spider类使用递归调用搜索程序，当然，一个新蜘蛛的独立线程可以在遇到每个链接时开始。这样的好处是允许链接远程URL并发执行，提高速度。然而记住那些叫做DefaultMutableTreeNode的JTree对象，不是线程安全的，所以程序员必须自己实现同步。<img src ="http://www.cnitblog.com/kenlen/aggbug/4228.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-09 22:43 <a href="http://www.cnitblog.com/kenlen/articles/4228.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>你又曾经感动谁 - Visual Studio.NET by 郭安定 </title><link>http://www.cnitblog.com/kenlen/articles/4127.html</link><dc:creator>Kenlen</dc:creator><author>Kenlen</author><pubDate>Tue, 08 Nov 2005 09:31:00 GMT</pubDate><guid>http://www.cnitblog.com/kenlen/articles/4127.html</guid><wfw:comment>http://www.cnitblog.com/kenlen/comments/4127.html</wfw:comment><comments>http://www.cnitblog.com/kenlen/articles/4127.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/kenlen/comments/commentRss/4127.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/kenlen/services/trackbacks/4127.html</trackback:ping><description><![CDATA[<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman">2002</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">4</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">月，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">VS.NET</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在台湾上市，我受邀回台湾担任发布会的司仪，我看到许多为科技努力而接棒的新生代，不仅对未来充满希望，同时也忧心忡忡后进对微软产品的感悟。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">信息本身只是一种力量，它不是永恒；永恒应该是象艺术、历史、神硛这些不会随着时间轻易淘汰的东西。我们从前学过的</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">OLE</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">ActiveX</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">COM</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">已经被更新的技术取代，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">.NET</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的未来或许也会有更好的改进，这条路永远不会停息，翻翻书架上过时的书籍，谁又深思在这些朝代的替换中留下了什么？</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">微软只能提供工具，它没有办法提供你知识，知识在你心里，只是透过微软的工具加以适时放大，工具追随者的快感只会持续一阵子，它没有办法让你十年后写一篇历史的感想与回顾，因为记忆里的每个阶段都是如此模糊。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果你为编程的前途感到困惑，那么不妨看看过去的前辈现在都在做什么？</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">我不知道有多少人听过</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Alan Cooper</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，它是</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Basic</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">之父，如果</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">VS.NET</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">非常重要，如果</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">VB.NET</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是不可忽略的工具，那么这位在业界超过</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">30</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年的人物可以带来一些启示，我最近读到他的一本早期着作</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">“</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">The inmates are running the asylum</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">”</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，有些人可能会好奇，他为什么不写一本</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Studio.NET Programmer</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">’</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">s Guide</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Basic.NET Fundamental</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的书？因为有比工具更重要的东西</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">－</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">那是你的思想。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman">.NET Framework</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的精神是</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">“交互无所不在”</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，当计算机的运算越来越快，生活的接触也将不限于个人计算机，我们需要走出</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">“个人计算机”</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的历史印象，把过去对信息的热诚带到其它移动设备。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对过去转换到</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Office</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">开发平台的编程人员，现在是关键时刻，开发的重心应该在</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">XML</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">网络服务器的设计，并且将它和</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Office</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">充分整合，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Office</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的对象模型将更丰富，亟须具备巧思的编程人员发挥它的威力，你可以预期</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Office</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Studio</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">将更加整合，未来将充满挑战与希望……</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">即使</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Basic</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">历经了这么多版本的改进，回首最初</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">1.0</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">版本，我们仍然可以发现它最重要的精神，那就是</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">“将可见与不可见的状态抽象成组件，并用这些东西构筑成梦境的延伸”</SPAN><FONT face="Times New Roman"> </FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Basic</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">并非全部，它只是实现理想的工具，只有跳脱工具，我们才能看到更真实的自己。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">走过十年来，七个版本的更替与无数合作的伙伴，我们的前辈造就了今天的学习环境，我们的努力也将影响未来的后进。在信息市场有很多无名英雄默默努力带动经济的成长，他很可能只是某家书商的老板、陪伴成长的工作伙伴或教授</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Basic</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的老师。无论如何，这十年来的成就值得所有爱好</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Visual Basic</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的编程人员共同喝采。</SPAN></P><img src ="http://www.cnitblog.com/kenlen/aggbug/4127.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/kenlen/" target="_blank">Kenlen</a> 2005-11-08 17:31 <a href="http://www.cnitblog.com/kenlen/articles/4127.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>