<?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>BlogJava-飞艳小屋-文章分类-.net</title><link>http://www.blogjava.net/songfei/category/4997.html</link><description>程序--人生--哲学___________________欢迎艳儿的加入</description><language>zh-cn</language><lastBuildDate>Thu, 28 Jun 2007 22:20:43 GMT</lastBuildDate><pubDate>Thu, 28 Jun 2007 22:20:43 GMT</pubDate><ttl>60</ttl><item><title>.net下程序的暴力修改</title><link>http://www.blogjava.net/songfei/articles/126776.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Thu, 28 Jun 2007 04:53:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/126776.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/126776.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/126776.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/126776.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/126776.html</trackback:ping><description><![CDATA[最近为了使单片机的通信扩展到Internet使用了Lantronix公司生产的XPort（串口—网络口数据转发模块），在使用它带的软件DeviceInstaller进行软件的配置的时候，发现安装完毕启动时出现异常，异常信息如下 <br><br>未处理的&#8220;System.NotSupportedException&#8221;类型的异常出现在 mscorlib.dll 中。 <br><br>其他信息: 区域性&#8220;zh-CHS&#8221;是非特定区域性。它不能用于格式化和分析，因此不能设置为线程的当前区域性。 <br><br>使用。Net反编译工具，打开主程序XTool.exe,找到主窗体，此软件没有綺过混淆，代码很清晰，在Form1种 <br>public Form1() <br>{ <br>CultureInfo info1; <br>Version version1; <br>string text1; <br>DialogResult result1; <br>DialogResult result2; <br>string text2; <br>string text3; <br>string[] array1; <br>int num1; <br>ListViewColumnSorter sorter1; <br>base..ctor(); <br>CultureInfo.CurrentCulture.ClearCachedData(); <br>info1 = CultureInfo.CurrentCulture; <br>if (info1.TwoLetterISOLanguageName == "zh") <br>{ <br>&nbsp;Thread.CurrentThread.CurrentCulture = new CultureInfo(info1.Parent.LCID);// //此部分出错 <br>Thread.CurrentThread.CurrentUICulture = new CultureInfo(info1.Parent.LCID);// <span class=Ppi468>dl.bitsCN.com网管软件下载</span> <br>&nbsp;} <br>this.InitializeComponent(); <br>&nbsp; <br>} <br>就是Thread.CurrentThread.CurrentCulture = new CultureInfo(info1.Parent.LCID);将new 的 <br>CultureInfo(info1.Parent.LCID)赋给Thread.CurrentThread.CurrentCulture，出现了异常，因此不能设置为线程的当前区域性的异常。可能是程序为了比较好的支持国际化，而这样写的带代码，看来有些多余。看来只要不让出错的语句运行即可，而这最好的实现方式就是修改比较条件。 <br><br>可以使用两种方法进行修改 <br>1．使用ildasm进行反汇编，得到中间代码，修改中间代码，然后重新编译即可。 <br>使用ildasm进行反汇编后得到一大堆代码，下面仅仅列出相关的 <br>// 相关的汇编代码 <br>.maxstack 6 <br>.locals (CultureInfo V_0, Version V_1, string V_2, Exception V_3, DialogResult V_4, DialogResult V_5, string V_6, string V_7, string[] V_8, int V_9, ListViewColumnSorter V_10) <br>.try L_0132 to L_014a catch object L_014a to L_014d <br>.try L_014d to L_0574 catch Exception L_0574 to L_0584 <br>.try L_0739 to L_0754 catch object L_0754 to L_0768 <br>L_0000: ldarg.0 <br>L_0001: call Form..ctor <br>L_0006: call CultureInfo.get_CurrentCulture <span class=Ppi468>bitsCN全力打造网管学习平台</span> <br>L_000b: callvirt CultureInfo.ClearCachedData <br>L_0010: call CultureInfo.get_CurrentCulture <br>L_0015: stloc.0 <br>L_0016: ldloc.0 <br>L_0017: callvirt CultureInfo.get_TwoLetterISOLanguageName <br>L_001c: ldstr "zh" ------------将此处的字符串改为aa <br><br><br>再使用ilasm 将修改后的相关代码进行编译得到新程序即可。重新编译后，生成的程序比訽来的小了（可能有部分资源没有编译进去），不知道什么訽因，但是没有发现影响使用的状况 <br><br>2．简单的处理方式，一般的.net程序中使用的常量字符串，在.net程序中都有相应的储存位置（存储格式为unicode 形式），所以使用WinHex打开程序，查找字符串zh，选中unicode项，发现文件中仅能搜索到一次，毫无疑问，就是他了，直接将字符串 <br>7A 00 68 00 改为其他的不同字节即可。存盘后，程序正常运行<br><br><br>小弟最近一直在做.net下串行通信方面的程序，.net下没有自带的串口控件，使用以前的com组件又不甘心，所以在网上找到了sax.net组件（破解）版本，使用的时候，比较满意，但是在使用的过程中，出现了 <br><br>System.ArgumentOutOfRangeException: 长度不能小于 0。 <br><br>参数名: newlength <br><br>&nbsp;at System.Text.StringBuilder.set_Length(Int32 value) <br><br>&nbsp;at Sax.Communications.SerialConnection.a(UInt32 A_0) <br><br>&nbsp;at Sax.Communications.SerialConnection.a(IntPtr A_0, c&amp; A_1) <br><br>&nbsp;at Sax.Communications.SerialConnection.get_Available() <br><br>&nbsp;at Sax.Communications.SerialConnection.b() <br><br>&nbsp; <br><br>异常 <br><br>尤其是使用ISP线与单片机通信的时候，几乎每次插拔ISP线都会出现异常。异常后，控件的串口数据监视线程退出以后再也不能收发数据，只能重新启动软件。綺过仔细调试，发现 <br>异常出现在插拔数据线的时候，有数据收到，产生了DataAvailable事件中 <br><br>private void serialConnection_DataAvailable(object sender, EventArgs e) //受到数据时的处理函数 <br>&nbsp; { <br>&nbsp;//在这之前出现了异常
<div class=Air240>dl.bitsCN.com网管软件下载</div>
<br>&nbsp;int length = serialConnection.Available; <br>// Debug.WriteLine(length.ToString()); <br>&nbsp;if(length&lt;=0) return; <br>&nbsp; <br>&nbsp;Byte[] buffer = new byte[length]; //输入缓冲区有多少数据？ <br>&nbsp;serialConnection.Read(buffer,0,length); <br>&nbsp; <br>&nbsp;//分析数据 <br>&nbsp;if( length &gt; 0 ) <br>&nbsp;{ <br>&nbsp; HandleDataReceive(buffer); <br>&nbsp;} <br>&nbsp; } <br><br><br>就是在发生事件之后，在int length = serialConnection.Available;之前发生了异常。根据VS.net <br>的提示信息，初步确定是在给一个StringBuilder对象builder设置其Length的时候，传入的参数&lt;0导 <br>致的。 <br><br>好了，使用.net的反编译工具Reflector（我用的版本4.0.18）打开控件,找到SerialConnection类；由于此控件綺过混淆，所以在反编译后出现了大量的比如a，或者b这样的看不出作者意图的函数名。不过没有关系，因为异常出现在serialConnection.Available附近，直接找到Available的实现代码 <br><br>public override int get_Available() <br><br>{ <br><br>&nbsp; z.c c1; <br><br>&nbsp; this.c(); <br><span class=Air240>dl.bitsCN.com网管软件下载</span> <br>&nbsp; c1 = new z.c(); <br><br>&nbsp; this.a(this.a, ref c1); <br><br>&nbsp; if (this.b != -1) <br><br>&nbsp; { <br><br>&nbsp; return ((int) (c1.b + 1)); <br><br>&nbsp; } <br><br>&nbsp; return ((int) c1.b); <br><br>} <br>&nbsp; <br><br>在里面寻找出错的函数 <br>找到this.a(this.a, ref c1);这个 <br><br>private string a(IntPtr A_0, ref z.c A_1) <br><br>{ <br><br>&nbsp; uint num1 = 0; <br><br>&nbsp; z.ClearCommError(A_0, ref num1, ref A_1); <br><br>&nbsp; if (num1 != 0) <br><br>&nbsp; { <br><br>&nbsp; return this.a(num1); <br><br>&nbsp; } <br><br>&nbsp; return null; <br><br>} <br>&nbsp; <br><br>有追踪到return this.a(num1); <br><br>private string a(uint A_0) <br><br>{ <br><br>&nbsp; StringBuilder builder1 = new StringBuilder("UART Error:", 60); <br><br>&nbsp; if (builder1.Length &lt;= 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_005F; <br><br>&nbsp; } <br><br>&nbsp; goto Label_00D0; <span class=Air240>bitsCN.com中国网管联盟</span> <br><br>Label_001B: <br><br>&nbsp; builder1 = builder1.Append(" Parity,"); <br><br>&nbsp; goto Label_00B1; <br><br>Label_002C: <br><br>&nbsp; builder1 = builder1.Append(" Receive Overflow,"); <br><br>&nbsp; goto Label_0069; <br><br>Label_003D: <br><br>&nbsp; builder1 = builder1.Append(" Framing,"); <br><br>&nbsp; goto Label_0095; <br><br>Label_004E: <br><br>&nbsp; builder1 = builder1.Append(" Transmit Overflow,"); <br><br>&nbsp; goto Label_00A3; <br><br>Label_005F: <br><br>&nbsp; if ((A_0 &amp; 8) == 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_0095; <br><br>&nbsp; } <br><br>&nbsp; goto Label_003D; <br><br>Label_0069: <br><br>&nbsp; if ((A_0 &amp; 4) == 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_00B1; <br><br>&nbsp; } <br><br>&nbsp; goto Label_001B; <br><br>Label_0073: <br><br>&nbsp; builder1 = builder1.Append(" &lt;Unknown&gt;,"); <br><br>&nbsp; goto Label_00FA; <font color=#ffffff>bbs.bitsCN.com国内最早的网管论坛</font> <br><br>Label_0084: <br><br>&nbsp; builder1 = builder1.Append(" Overrun,"); <br><br>&nbsp; goto Label_00ED; <br><br>Label_0095: <br><br>&nbsp; if ((A_0 &amp; 1024) == 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_00E3; <br><br>&nbsp; } <br><br>&nbsp; goto Label_00BF; <br><br>Label_00A3: <br><br>&nbsp; if ((A_0 &amp; 1287) == 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_00FA; <br><br>&nbsp; } <br><br>&nbsp; goto Label_0073; <br><br>Label_00B1: <br><br>&nbsp; if ((A_0 &amp; 256) == 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_00A3; <br><br>&nbsp; } <br><br>&nbsp; goto Label_004E; <br><br>Label_00BF: <br><br>&nbsp; builder1 = builder1.Append(" IO,"); <br><br>&nbsp; goto Label_00E3; <br><br>Label_00D0: <br><br>&nbsp; builder1.Remove(0, builder1.Length); <br><br>&nbsp; goto Label_005F; <br><br>Label_00E3: <br><br>&nbsp; if ((A_0 &amp; 2) != 0) <br><br>&nbsp; { <font color=#ffffff>bitsCN.com中国网管联盟</font> <br><br>&nbsp; goto Label_0084; <br><br>&nbsp; } <br><br>Label_00ED: <br><br>&nbsp; if ((A_0 &amp; 1) == 0) <br><br>&nbsp; { <br><br>&nbsp; goto Label_0069; <br><br>&nbsp; } <br><br>&nbsp; goto Label_002C; <br><br>Label_00FA: <br><br>&nbsp; builder1.Length -= 1;//!!!!!!!!!!!!!!!!!!错误的来源 <br><br>&nbsp; return builder1.ToString(); <br><br>} <br><br>我靠，也没有想细读为什么要将builder1.Length 减去1，节省内存，没有必要把？！！ <br>反正是这句出错了，将后builder1.Length 小于0了 <br><br>查看前面的代码，没有对builder1.Length 赋值的语句，看来将这句改掉就行了， <br><br>查看il代码， <br>最后的几句为 <br>L_00fa: ldloc.0 <br>L_00fb: ldloc.0 <br>L_00fc: callvirt StringBuilder.get_Length <br>L_0101: ldc.i4.1 //装载常数1--将此处改为装载常数0，即可 <br>L_0102: sub <br>L_0103: callvirt StringBuilder.set_Length <br>L_0108: ldloc.0 <br>L_0109: callvirt StringBuilder.ToString <br>L_010e: ret <br><br>查找il的汇编指令 <br>发现（格式指令 ---十六进制数） <br>ldloc.0 ----06 <font color=#ffffff>play.bitsCN.com累了吗玩一下吧</font> <br>ldloc.0 ----06 <br>callvirt StringBuilder.get_Length ---- 6F X X X X (X不知道具体的值，可以通过模糊匹配来确定要修改的指令在文件的位置) <br>ldc.i4.1 ---- 17 <br><br><br><br>sub ----59 <br>L_0103: callvirt---- StringBuilder.set_Length 6F X X X X <br>L_0108: ldloc.0---- 06 <br>L_0109: callvirt---- StringBuilder.ToString 6F X X X X <br>L_010e: ret ----28 <br><br><br>使用WinHEx打开文件，使用16进制搜索，对于上面的X 使用模糊匹配的訽则 <br>找到偏移0x34B0处，在0x34BD处的将0x17 改为0x16(IL指令ldc.i4.0 ，就是将Length-0等于不减) <br>存盘退出，将修改好的dll文件覆盖訽来的dll,运行软件，怎么插拔ISP线都没有问题出现了！ <br><br><br>参考资料 <br>Ｉｎｓｉｄｅ　Ｍｉｃｒｏｓｏｆｔ．ｎｅｔ　ＩＬ　Ａｓｓｅｓｍｂｌｅｒ <br><br><br><br>
<img src ="http://www.blogjava.net/songfei/aggbug/126776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2007-06-28 12:53 <a href="http://www.blogjava.net/songfei/articles/126776.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应同学之邀,破解一个软件,学习逆向工程,文章如下</title><link>http://www.blogjava.net/songfei/articles/126610.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Wed, 27 Jun 2007 07:55:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/126610.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/126610.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/126610.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/126610.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/126610.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: [.NET][爆破]我的Microsoft Math 3.0激活之路            [标&nbsp; &nbsp; 题][原创][.NET][爆破]我的Microsoft Math 3.0激活之路[作&nbsp; &nbsp;者]快雪时晴，2007年6月16日[目&nbsp; &nbsp; 标]&nbsp; &nbsp; &nbsp; &nbsp; 在CNBeta看到一则新闻：Micr...&nbsp;&nbsp;<a href='http://www.blogjava.net/songfei/articles/126610.html'>阅读全文</a><img src ="http://www.blogjava.net/songfei/aggbug/126610.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2007-06-27 15:55 <a href="http://www.blogjava.net/songfei/articles/126610.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> C#基础全接触</title><link>http://www.blogjava.net/songfei/articles/93800.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Sun, 14 Jan 2007 12:18:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/93800.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/93800.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/93800.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/93800.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/93800.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a href="http://blog.csdn.net/njuyama/archive/2006/09/26/1288202.aspx">
						<img height="13" src="http://blog.csdn.net/images/zhuan.gif" width="15" border="0" /> C#基础全接触</a>
		</div>
		<br />
		<div class="postText">
				<font face="Courier New"> </font>
				<p>引用类型是类型安全的指针，它们的内存是分配在堆（保存指针地址）上的。<br />String、数组、类、接口和委托都是引用类型。<br />强制类型转换与as类型转换的区别：当类型转换非法时，强制类型转换将抛出一个System.InvalidCastException异常，<br />而as不会抛出异常，它返回一个null值。<br />用using创建别名：using console = System.Console;<br />访问限定符：<br />public  该成员可以被其他任何类访问<br />protected 该成员只能被其派生类访问<br />private  该成员只能被本类的其他成员访问<br />internal 该成员只能在当前编译单元的其他成员访问<br />带参数列表和返回值的Main方法：<br />class Test<br />{<br />  public static int Main(string[] args)<br />  {<br />    foreach (string arg in args)<br />    {<br />    ...<br />    }<br />  }<br />}<br />构造函数（constructor）包括实例构造函数和静态构造函数。<br />构造函数与类名相同，且不能有返回值。例：<br />class TestClass<br />{<br />  TestClass()  //实例构造函数：可以访问静态成员和实例成员，用于初始化实例成员<br />  {<br />  ...<br />  }</p>
				<p>  static TestClass() //静态构造函数：只能访问静态成员，用于初始化静态成员<br />  {<br />  ...<br />  }<br />}<br />类的静态成员属于类所有，不必生成实例就可以访问，它是在载入包含类的应用程序时创建的，<br />但静态方法不能访问类的实例变量和方法。通常，静态变量是在定义时就赋初始值的。<br />类的实例成员属于类的实例所有，不创建实例对象就无法对其进行访问，实例成员可以访问类的<br />静态成员和其它实例成员。<br />调用基类的析构函数：<br />class A<br />{<br />  public A()<br />  {<br />  ...<br />  }<br />}<br />class B<br />{<br />  public B(): base()  //调用基类的析构函数<br />  {<br />  ...<br />  }<br />}<br />常量：其值是在编译时设定的，必须是数值文字。默认状态下常量是静态的。例：<br />class A<br />{<br />  public const double pi = 3.1415;<br />}<br />常量是编译时就确定的值，只读字段是在运行才能确定的值。比如运行时才能确定的屏幕分辨率。<br />只读字段只能在类的析构函数中赋值。<br />静态只读字段：<br />class A<br />{<br />  public static readonly int ScreenWidth;  //静态只读字段<br />  static A()   //静态析构函数<br />  {<br />    ScreenWidth = 1024;  //在静态析构函数中初始化<br />  }<br />}<br />在类的继承中，类的析构函数是不会被继承的。<br />一个派生类只能从一个基类继承，不能同时从多个基类继承，但可以通过继承多个接口来<br />达到相同目的。实现多继承的唯一方法就是使用接口。例：<br />class MyFancyGrid: Control, ISerializable, IDataBound<br />{<br />...<br />}<br />密封类是不能继承的类，抽象类不能被定义为密封类，且密封类的私有成员不能用protected修饰，<br />只能用private。例：<br />sealed class A<br />{<br />...<br />}<br />关键字ref和out用于指定用引用方式传递方法的参数。<br />它们的区别是：ref参数必须初始化，而out参数不需要初始化。所以在方法处理代码依赖参数的<br />初始化值时使用ref，不依赖初始化值时使用out。<br />对out参数即使在传递前对其进行了初始化，其值也不会传递到方法处理函数内部。传递时系统<br />会将其设为未初始化。所以在方法内部必须对out参数进行初始化。<br />方法重载时，必须参数数目和参数类型其中之一不同，返回值不同不能作为重载。<br />C#不支持方法的默认值，只能通过方法重载来实现。例：<br />class A<br />{<br />  int Method(int a)<br />  {<br />  ...<br />  }</p>
				<p>  void Method(int a, int b) //参数数目不同<br />  {    //返回值不同不能作为重载<br />  ...<br />  }<br />}<br />params参数用于一个不定数目参数的方法，一般后面跟一个数组。例：<br />class A<br />{<br />  public void Method(params int[] i)<br />  {<br />  ...<br />  }<br />}<br />方法的覆盖：指派生类覆盖基类的同名方法，有二种方法<br />1）第一种是在派生类要覆盖的方法前面加new修饰，而基类不需要作任何改动。<br />这种方法的缺点是不能实现多态。例：<br />class A<br />{<br />  public void Method()  //无需任何修饰<br />  {<br />  ...<br />  }<br />}</p>
				<p>class B: A   //从基类继承<br />{<br />  new public void Method() //覆盖基类的同名方法<br />  {<br />  ...<br />  }<br />}</p>
				<p>class TestClass<br />{<br />  A Instance = new B();<br />  Instance.Method();  //这时将调用类A的Method方法，而不是类B的Method方法<br />}</p>
				<p>2）第二种是在派生类要覆盖的方法前面加override修饰，而基类的同名方法前面加virtual修饰。<br />这样就能实现多态，例：</p>
				<p>class A<br />{<br />  virtual public void Method()   //基类定义虚方法<br />  {      //虚拟方法不能定义为private，因为private成员对派生类是无法访问的<br />  ...<br />  }<br />}</p>
				<p>class B: A     //从基类继承<br />{<br />  override public void Method()   //派生类覆盖基类的同名虚方法<br />  {<br />  ...<br />  }<br />}</p>
				<p>class TestClass<br />{<br />  protected void Test()<br />  {<br />    A Instance = new B();   //定义一个实例，类型为基类，从派生类创建<br />      //派生类总是能够向上转换为其基类<br />    Instance.Method();    //将调用派生类B的Method方法，而不是基类的，这就是多态<br />  }<br />}</p>
				<p>说明：new修饰的方法覆盖不能实现多态的原因，是因为使用new时编译器只会实现早期绑定（early binding）。<br />即调用的方法在编译时就决定了：编译器看到Instance.Method()而Instance的类是A，就会调用类A的Method()方法。<br />override修饰的方法覆盖可以实现多态的原因，是因为实现了后期绑定（late binding）。<br />使用override时强制编译器在运行时根据类的真正类型正确调用相应的方法，而不是在编译时。<br />而基类的同名方法必须加virtual修饰。<br />类的静态方法可能通过 类名.静态方法名 这种格式来调用，不能使用 实例名.静态方法名 这种方法调用。<br />因为类的静态方法为类所有（是属于类本身的），而非实例所有（不是属于类的实例的）。<br />类的静态方法可以访问类的任何静态成员，但不能访问类的实例成员。<br />C#中类的变量称为字段。类的public变量称为类的公共字段。<br />类的属性由一个protected（也可以是private）字段和getter和setter方法构成：<br />class Address<br />{<br />  protected string zipCode; //protected字段，注意大小写<br />  public string ZipCode<br />  {<br />    get    //getter方法<br />    {<br />      return zipCode;<br />    }<br />    set    //setter方法<br />    {<br />      zipCode = value;  //被传递的值自动被在这个value变量中<br />    }<br />  };<br />}<br />只读属性是指省略setter方法的属性，只读属性只能读取，不能设置。<br />属性也可以用限定符virtual，override和abstract修饰，功能同其他类的方法。<br />属性有一个用处称为懒惰的初始化（lazy initialization）。即在需要类成员时才对它们进行<br />初始化。如果类中包含了很少被引用的成员，而这些成员的初始化又会花费大量的时候和系统<br />资源的话，懒惰的初始化就很有用了。<br />C#中数组对象共同的基类是System.Array。<br />将数组声明为类的一个成员时，声明数组与实例化数组必须分开，这是因为只能在运行时创建了<br />类的实例对象之后，才能实例化数组元素值。<br />声明：<br />int[] intArray;  //一维数组<br />int[,,] int3Array; //三维数组<br />初始化：<br />intArray = new int[3] {1,2,3};<br />int[,] int2Array = new int[2,3] {{1,2,3},{4,5,6}}; //声明时可以初始化<br />遍历：<br />1）一维数组<br />for (int i = 0; i &lt; intArray.Length; i++); //Array.Length返回数组所有元素的个数<br />foreach (int i in intArray);<br />for (int i = 0; i &lt; intArray.GetLength(0); i++);//Array.GetLength(0)返回数组第一维的个数<br />2）多维数组<br />for (int i = 0; i &lt; int3Array.GetLength(0); i++) //遍历三维数组<br />  for (int j = 0; j &lt; int3Array.GetLength(1); j++)<br />    for (int k = 0; k &lt; int3Array.GetLength(2); k++)<br />    {<br />    ...<br />    }<br />数组的维数就是该数组的秩（Rank）。Array.Rank可以返回数据的秩。<br />锯齿数组（jagged Array）是元素为数组的数组，例：<br />int[][] jaggedArray = new int[2][]; //包含二个元素，每个元素是个数组<br />jaggedArray[0] = new int[2];  //每个元素必须初始化<br />jaggedArray[1] = new int[3];<br />for (int i = 0; i &lt; jaggedArray.Length; i++) //遍历锯齿数组<br />  for (int j = 0; j &lt; jaggedArray[i].Length; j++)<br />  {<br />  ...<br />  }<br />类的属性称为智能字段，类的索引器称为智能数组。由于类本身作数组使用，所以用<br />this作索引器的名称，索引器有索引参数值。例：<br />using System;<br />using System.Collections;<br />class MyListBox<br />{<br />  protected ArrayList data = new ArrayList();<br />  public object this[int idx]  //this作索引器名称，idx是索引参数<br />  {<br />    get<br />    {<br />      if (idx &gt; -1 &amp;&amp; idx &lt; data.Count)<br />      {<br />        return data[idx];<br />      }<br />      else<br />      {<br />        return null;<br />      }<br />    }<br />    set<br />    {<br />      if (idx &gt; -1 &amp;&amp; idx &lt; data.Count)<br />      {<br />        data[idx] = value;<br />      }<br />      else if (idx = data.Count)<br />      {<br />        data.Add(value);<br />      }<br />      else<br />      {<br />        //抛出一个异常<br />      }<br />    }<br />  }<br />}<br />接口是二段不同代码之间约定，通过约定实现彼此之间的相互访问。<br />C#并不支持多继承，但通过接口可实现相同功能。<br />当在接口中指定了实现这个接口的类时，我们就称这个类“实现了该接口”或“从接口继承”。<br />一个接口基本上就是一个抽象类，这个抽象类中除了声明C#类的其他成员类型——例如属性、<br />事件和索引器之外，只声明了纯虚拟方法。<br />接口中可以包含方法、属性、索引器和事件——其中任何一种都不是在接口自身中来实现的。例：<br />interface IExampleInterface<br />{<br />  //property declaration<br />  int testProperty { get; }</p>
				<p>  //event declaration<br />  event testEvevnt Changed;</p>
				<p>  //mothed declaration<br />  function void testMothed();</p>
				<p>  //indexer declaration<br />  string this[int index] { get; set; }<br />}<br />说明：定义接口时，在方法、属性、事件和索引器所有这些接口成员都不能用public之类的访问限定符，<br />因为所有接口成员都是public类型的。<br />因为接口定义了一个约定，任何实现一个接口的类都必须定义那个接口中每一个成员，否则将编译失败。例：<br />using System;<br />public class FancyControl<br />{<br />  protected string data;<br />  public string Data<br />  {<br />    get {return this.data;}<br />    set {data = value;}<br />  }<br />}</p>
				<p>interface IValidate<br />{<br />  bool Validate(); //接口方法<br />}</p>
				<p>public class MyControl: FancyControl, IValidate<br />{<br />  public MyControl()<br />  {<br />    data = "my control data";<br />  }</p>
				<p>  public bool Validate()  //实现接口<br />  {<br />    if (data == "my control data")<br />      return true;<br />    else<br />      return false;<br />  }<br />}<br />class InterfaceApp<br />{<br />  MyControl myControl = new MyControl();<br />  <br />  IValidate val = (IValidate)myControl;  //可以将一个实现某接口的类，转换成该接口<br />  bool success = val.Validate();  //然后可调用该接口的方法<br />}<br />也可以用：<br />bool success = myControl.Validate();<br />这种方法来调用Validate方法，因为Validate在类MyControl中是被定义成public的，如果去除public，Validate方法被隐藏，<br />就不能用这种方法调用了，这样隐藏接口方法称为名字隐藏（name hiding）。<br />可以用：类实例 is 接口名 来判断某个类是否实现了某接口，例：<br />myControl is IValidate  //MyControl类的实例myControl是否实现了IValidate接口<br />当然，也可用as来作转换，根据转换结果是否为null来判断某个类是否实现了某接口，例：<br />IValidate val = myControl as IValidate;<br />if (null == val)<br />{<br />...  //没有实现IValidate接口<br />}<br />else<br />{<br />...  //实现了IValidate接口<br />}</p>
				<p>如果一个类从多个接口继承，而这些接口中如果定义的同名的方法，则实现接口的方法时，必须加接口名来区别，<br />写成 接口名.方法名。假设Test类从IDataStore和ISerializable二个接口继承，而这二个接口都有SaveData()方法，<br />实现SaveData()方法时必须写成：<br />class Test: ISerializable, IDataStore<br />{<br />  void ISerializable.SaveData()<br />  {<br />  ...<br />  }</p>
				<p>  void IDataStore.SaveData()<br />  {<br />  ...<br />  }<br />}</p>
				<p>如果一个类从多个接口继承，为了方便可以定义一个新的接口，这个接口继续多个接口，然后类直接从这个接口继承就<br />可以了，这个叫合并接口。例：<br />interface ISaveData: ISerializable, IDataStore<br />{  //不需要定义任何方法或成员，只是用作合并<br />}<br />class Test: ISaveData  //只要继承ISaveData就可以了<br />{<br />...<br />}<br />C# 操作符优先级（从高到低）<br />初级操作符 () x.y f(x) a[x] x++ x-- new typeof sizeof checked unchecked<br />一元操作符 + - | ~ ++x --x (T)x<br />乘除操作符 * / %<br />加减操作符 + -<br />位移操作符 &lt;&lt; &gt;&gt;<br />关系操作符 &lt; &gt; &lt;= &gt;= is<br />等于操作符 ==<br />逻辑与  &amp;<br />逻辑异或 ^<br />逻辑或  |<br />条件与  &amp;&amp;<br />条件或  ||<br />条件操作符 ?:<br />赋值操作符 = *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=<br />所有的二元操作符除赋值符外都是左联合的，即从左到右计算。<br />typeof()运算符可以从一个类名得到一个System.Type对象，而从System.Object对象继承来的GetType()方法<br />则可从一个类实例来得到一个System.Type对象。例：<br />Type t1 = typeof(Apple); //Apple是一个类名<br />Apple apple = new Apple(); //apple是Apple类的一个实例<br />Type t2 = apple.GetType(); //t1与t2是相同的<br />通过反射得到一个类的所有成员和方法：<br />Type t = typeof(Apple);<br />string className = t.ToString(); //得到类名<br />MethodInfo[] methods = t.GetMethods(); //得到所有方法<br />foreach (MethodInfo method in methods)<br />{<br />//用method.ToString()得到方法名<br />}<br />MemberInfo[] members = t.GetMembers(); //得到所有成员<br />foreach (MemberInfo member in members)<br />{<br />//用member.ToString()得到成员名<br />}<br />sizeof()操作符用来计算值类型变量在内存中占用的字节数（Bytes），并且它只能在unsafe（非安全）<br />代码中使用。例：<br />static unsafe public void ShowSizes()<br />{<br />  int i, j;<br />  j = sizeof(short);<br />  j = sizeof(i);<br />}<br />尽可能使用复合赋值操作符，它比不用复合赋值操作符的效率高。<br />for语句的语法为：<br />for (initialization; Boolean-expression; step)<br />  embedded-statement<br />在initialization和step部份还可以使用逗号操作符，例：<br />for (int i = '0', j = 1; i &lt;= '\xFF'; i++, j++)<br />for (int i = 1, j = 1; i &lt; 1000; i += j, j = i - j) //输出斐波那契数列<br /> Console.Write("{0} ", i);<br />在switch语句中执行一个分支的代码后还想执行另一个分支的代码，可以用：<br />goto case 分支;<br />操作符重载是为了让程序更加自然，容易理解。想要为一个类重新定义一个操作符，使用以下语法：<br />public static 返回值 operator 操作符 (操作对象1[，操作对象2])<br />说明：<br />1)所有重载的操作符方法都必须定义为public和static<br />2)从技术上说返回值可以是任何类型，但通常是返回所定义方法使用的类型<br />3)操作对象的数目取决于重载是一元操作符还是二元操作符，一元操作符只要一个操作对象，二元操作符则需要二个。<br />4)不管重载是一元操作符还是二元操作符，第一个操作对象的类型都必须与返回值的类型一致；而对于二元操作符的第二个<br />操作对象的类型则可以是任何类型。<br />5)只有下列操作符可以被重载：<br />一元：+ - ! ~ ++ -- true false<br />二元：+ - * / % &amp; | ^ &lt;&lt; &gt;&gt; == != &gt; &lt; &gt;= &lt;=<br />赋值操作符（+=,-=,*-,/=,%=等等）无法被重载。<br />[]和()操作符也无法被重载。<br />6)操作符的优先级是无法改变的，运算优先级的规则是静态的。</p>
				<p>例：假设一个Invoice发票类由多个InvoiceDetailLine类（成员只有一个Double类型的Amount金额属性）组成，<br />我们重载+操作符，使之可以将InvoiceDetailLine类的内容（注意不是金额合计）加在一起。<br />class Invoice<br />{<br />  public ArrayList DetailLine;<br />  <br />  public Invoice   //类的析构函数<br />  {<br />    DetailLine = new ArrayList(); //ArrayList存放多个InvoiceDetailLine类的实例<br />  }</p>
				<p>  public static Invoice operator+ (Invoice Invoice1, Invoice Invoice2) //参数与返回值的类型一致<br />  {<br />    //Invoice1与Invoice2的内容合并<br />    Invoice ReturnInvoice = new Invoice();<br />    foreach(InvoiceDetailLine detailLine in Invoice1.DetailLines)<br />      ReturnInvoice.DetailLine.Add(detailLine);<br />    foreach(InvoiceDetailLine detailLine in Invoice2.DetailLines)<br />      ReturnInvoice.DetailLine.Add(detailLine);<br />    return ReturnInvoice;<br />  }<br />}</p>
				<p>class InvoiceAddApp  //调用示例<br />{<br />  public static void main()<br />  {<br />    Invoice i1 = new Invoice();<br />    for(int i = 0; i &lt; 3; i++)<br />      i1.DetailLine.Add(new InvoiceDetailLine(i + 1));</p>
				<p>    Invoice i2 = new Invoice();<br />    for(int i = 0; i &lt; 3; i++)<br />      i2.DetailLine.Add(new InvoiceDetailLine(i + 1));</p>
				<p>    Invoice summaryInvoice = i1 + i2;  //调用重载的操作符+方法<br />  }<br />}</p>
				<p>
						<br />自定义类型转换可以编写代码实际二个不同的类、结构体之间的转换。<br />语法：public static implicite/explicite operator 输出类型 (输入类型)<br />说明：<br />1)转换方法必须是静态的。<br />2)implicite表示隐式转换，explicite表示显式转换。<br />3)输入类型和输出类型其中之一必须与包含转换的类或结构体类型。即转换必须与本类相关。<br />例：<br />struct Celisus<br />{<br />  public float t;</p>
				<p>  public Celisus(float t)<br />  {<br />    this.t = t;   //this.t是结构体的字段,t是参数<br />  }</p>
				<p>  public static implicite operator Celisus(float t) //float=&gt;Celisus<br />  {<br />    return new Celisus(t);<br />  }</p>
				<p>  public static implicite operator float(Celisus c) //Celisus=&gt;float<br />  {<br />    return ((c.t - 32) / 9) * 5;<br />  }<br />}</p>
				<p>
						<br />代表的(delegate)目的与C++中的函数指针相同，代表不是在编译时被定义的，而是在运行时被定义的。<br />代表主要有二个用途：回调(Callback)和事件处理(event)<br />回调通常用于异步处理和自定义处理。例：<br />class DBManager<br />{<br />  static DBConnection[] activeConnections;<br />  //声明回调函数<br />  public void delegate EnumConnectionCallback(DBConnection connection);</p>
				<p>  public static void EnumConnections(EnumConnectionCallback callback)<br />  {<br />    foreach (DBConnection connection in activeConnections)<br />    {<br />      callback(connection);  //执行回调函数<br />    }<br />  }<br />}</p>
				<p>//调用<br />class DelegateApp<br />{<br />  public static void ActiveConncetionCallback(DBConnection connection) //处理函数<br />  {<br />  ...<br />  }</p>
				<p>  public void main()<br />  {<br />    //创建指向具体处理函数的代表实例（新建一个代表，让它指向具体的处理函数）<br />    DBManager.EmnuConnectionCallback myCallback = new DBManager.EmnuConnectionCallback(ActiveConncetionCallback);<br />    DBManager.EnumConnections(myCallback);<br />  }<br />}</p>
				<p>//使用静态代表，上面的调用改为<br />class DelegateApp<br />{<br />  //创建一个指向处理函数的静态代表<br />  public static DBManager.EmnuConnectionCallback myCallback<br />    = new DBManager.EmnuConnectionCallback(ActiveConncetionCallback);<br />  public static void ActiveConncetionCallback(DBConnection connection)<br />  {<br />  ...<br />  }</p>
				<p>  public void main()<br />  {<br />    DBManager.EnumConnections(myCallback);<br />  }<br />}</p>
				<p>//在需要时才创建代表，上面的调用改为<br />class DelegateApp<br />{<br />  //将创建代表放在属性的getter方法中<br />  public static DBManager.EmnuConnectionCallback myCallback<br />  {<br />    get<br />    {<br />      retun new DBManager.EmnuConnectionCallback(ActiveConncetionCallback);<br />    }<br />  }<br />  public static void ActiveConncetionCallback(DBConnection connection)<br />  {<br />  ...<br />  }</p>
				<p>  public void main()<br />  {<br />    DelegateApp app = new DelegateApp(); //创建应用程序<br />    DBManager.EnumConnections(myCallback);<br />  }<br />}</p>
				<p>
						<br />可以将多个代表整合成单个代表，例：<br />class CompositeDelegateApp<br />{<br />  public static void LogEvent(Part part)<br />  {<br />  ...<br />  }</p>
				<p>  public static void EmailPurchasingMgr(Part part)<br />  {<br />  ...<br />  }</p>
				<p>  public static void Main()<br />  {<br />    //定义二个代表<br />    InventoryManager.OutOfStockExceptionMethod LogEventCallback<br />      = new InventoryManager.OutOfStockExceptionMethod(LogEvent);<br />    InventoryManager.OutOfStockExceptionMethod EmailPurchasingMgrCallback<br />      = new InventoryManager.OutOfStockExceptionMethod(EmailPurchasingMgr);<br />    //整合为一个代表，注意后加的代表先执行（这里是先执行LogEventCallback）<br />    InventoryManager.OutOfStockExceptionMethod onHandExceptionEventsCallback<br />      = EmailPurchasingMgrCallback + LogEventCallback;<br />    //调用代表<br />    InventoryManager mgr = new InventoryManager();<br />    mgr.ProcessInventory(onHandExceptionEventsCallback);<br />    //InventoryManager类的ProcessInventory方法的原型为：<br />    //public void ProcessInventory(OutOfStockExceptionMethod exception);<br />  }<br />}</p>
				<p>可以根据需要将多个代表自由地组合成单个代表，例：<br />class CompositeDelegateApp<br />{<br />  //代表指向的处理函数（三个代表三个函数）<br />  public static void LogEvent(Part part)<br />  {<br />  ...<br />  }</p>
				<p>  public static void EmailPurchasingMgr(Part part)<br />  {<br />  ...<br />  }</p>
				<p>  public static void EmailStoreMgr(Part part)<br />  {<br />  ...<br />  }</p>
				<p>  public static void Main()<br />  {<br />    //通过数组定义三个代表<br />    InventoryManager.OutOfStockExceptionMethod[] exceptionMethods<br />      = new InventoryManager.OutOfStockExceptionMethod[3];<br />    exceptionMethods[0] = new InventoryManager.OutOfStockExceptionMethod(LogEvent);<br />    exceptionMethods[1] = new InventoryManager.OutOfStockExceptionMethod(EmailPurchasingMgr);<br />    exceptionMethods[2] = new InventoryManager.OutOfStockExceptionMethod(EmailStoreMgr);</p>
				<p>    int location = 1;<br />    //再定义一个代表（用于组合成单代表）<br />    InventoryManager.OutOfStockExceptionMethod compositeDelegate;<br />    //根据需要组合<br />    if (location = 2)<br />    {<br />      compositeDelegate = exceptionMethods[0] + exceptionMethods[1];<br />    }<br />    else<br />    {<br />      compositeDelegate = exceptionMethods[0] + exceptionMethods[2];<br />    }<br />    //调用代表<br />    InventoryManager mgr = new InventoryManager();<br />    mgr.ProcessInventory(compositeDelegate);<br />  }<br />}<br />C#的事件遵循“发布——预订”的设计模式。在这种模式中，一个类公布能够出现的所有事件，<br />然后任何的类都可以预订这些事件。一旦事件产生，运行环境就负责通知每个订户事件已经发生了。<br />当代表作为事件的处理结果时（或者说定义具有代表的事件），定义的代表必须指向二个参数的方法：<br />一个参数是引发事件的对象（发布者），另一个是事件信息对象（这个对象必须从EventArgs类中派生）。<br />例：<br />using System;</p>
				<p>class InventoryChangeEventArgs: EventArgs //事件信息对象，从EventArgs类派生<br />{<br />... //假设定义二个public属性string Sku和int Change<br />}</p>
				<p>class InventoryManager    //事件的发布者<br />{<br />  //声明代表<br />  public delegate void InventoryChangeEventHander(object source, InventoryChangeEventArgs e);<br />  //发布事件，event关键字可将一个代表指向多个处理函数<br />  public event InventoryChangeEventHandler onInventoryChangeHander;<br />  <br />  public void UpdateInventory(string sku, int change)<br />  {<br />    if (change == 0)<br />      return;<br />    InventoryChangeEventArgs e = new InventoryChangeEventArgs(sku, change);<br />    //触发事件<br />    if (onInventoryChangeHandler != null) //如果有预订者就触发<br />      onInventoryChangeHandler(this, e); //执行代表指向的处理函数<br />  }<br />}</p>
				<p>class InventoryWatcher    //事件的预订者<br />{<br />  public InventoryWatcher(InventoryManager mgr) //mgr参数用于联结发布者<br />  {<br />    this.inventoryManager = mgr;<br />    //预订事件，用 += 调用多个处理函数<br />    mgr.onInventroyChangeHandler += new InventoryManager.InventoryChangeEventHandler(onInventoryChange);<br />    //事件处理函数<br />    void onInventroyChange(object source, InventroyChangeEventArgs e)<br />    {<br />    ...<br />    }</p>
				<p>    InventoryManager inventoryManager;<br />  }<br />}</p>
				<p>class EventsApp     //主程序<br />{<br />  public static void Main()<br />  {<br />    InventoryManager inventoryManager = new InventoryManager();<br />    InventoryWatcher inventoryWatcher = new InventoryWatcher(inventoryManager);</p>
				<p>    inventoryManager.UpdateInventory("111 006 116", -2);<br />    inventoryManager.UpdateInventory("111 006 116", 5);<br />  }<br />}</p>
				<p>
						<br />Microsoft Windows NT和IBM OS/2等操作系统都支持占先型多任务。在占先型多任务执行中，处理器负责<br />给每个线程分配一定量的运行时间——一个时间片(timeslice)。处理器接着在不同的线程之间进行切换，<br />执行相应的处理。在单处理器的计算机上，并不能真正实现多个线程的同时运行，除非运行在多个处理器<br />的计算机上。操作系统调度的多线程只是根据分配给每个线程时间片进行切换执行，感觉上就像同时执行。</p>
				<p>上下文切换(context switching)是线程运行的一部分，处理器使用一个硬件时间来判断一个指定线程的时间片<br />何时结束。当这个硬件计时器给出中断信号时，处理器把当前运行的线程所用的所有寄存器(registers)数据<br />存储到堆栈中。然后，处理器把堆栈里那些相同的寄存器信息存放到一种被称为“上下文结构”的数据结构中。<br />当处理器要切换回原来执行的线程时，它反向执行这个过程，利用与该线程相关的上下文结构，在寄存器里<br />重新恢复与这一线程相关的信息。这样的一个完整过程称为“上下文切换”。</p>
				<p>多线程允许应用程序把任务分割为多个线程，它们彼此之间可以独立地工作，最大限度地利用了处理器时间。</p>
				<p>using System;<br />using System.Threading;</p>
				<p>class SimpleThreadApp<br />{<br />  public static void WorkerThreadMethod() //线程的执行体<br />  {<br />  ...      //执行一些操作<br />  }</p>
				<p>  public static void Main()<br />  {<br />    //创建一个线程代表指向线程的执行体，ThreadStart是创建新线程必须用到的代表<br />    ThreadStart worker = new ThreadStart(WorkerThreadMethod);<br />    Thread t = new Thread(worker);  //用线程代表创建线程<br />    t.Start();     //执行线程<br />  }<br />}</p>
				<p>可以通过两种方式来得到一个Thread对象：一种是通过创建一个新线程来得到，如上例；另一种在正在执行的线程调用<br />静态的Thread.CurrentThread方法。<br />静态方法Thread.Sleep(int ms)可以让当前线程（它自动调用Thread.CurrentThread）暂停指定毫秒的时间。<br />如果使用Thread.Sleep(0)那么当前线程将一直处于等待中，直到另一个线程调用这个线程的实例方法Thread.Interrupt方法，<br />等待才会结束。<br />使用Thread.Suspend方法也能挂起线程，Thread.Suspend方法可以被当前线程或其他线程调用，而Thread.Sleep(0)<br />只能由当前线程在执行体中调用。当线程用Thread.Suspend挂起时，必须用Thread.Resume方法恢复。不论Thread.Suspend<br />方法调用了多少次，只要调用Thread.Resume方法一次就可以线程恢复执行。用Thread.Suspend方法并不会阻塞线程，<br />调用立即返回。而Thread.Sleep(0)则会阻塞线程。所以确切地说Thread.Sleep(0)暂停线程，而不是挂起线程。<br />使用Thread.Abort方法可以终止正在执行的线程。当Thread.Abort方法被调用时，线程不会立即终止执行。运行环境将会<br />等待，直到线程到达文档中所描述的“安全点”。如果要确保线程已经完全停止，可以使用Thread.Join方法。这是一个同步<br />调用，同步调用意味着直到线程完全停止，调用才会返回。<br />Thread.Priority属性用于设置的线程的优先级。其值是Thread.ThreadPriority枚举值，可以设为Highest, AboveNormal,<br />Normal, BelowNormal, Lowest。缺省值是Thread.ThreadPriority.Normal。<br />线程的同步是为了解决多个线程同时使用同一对象产生的一些问题。通过同步，可以指定代码的临界区(critical section)，<br />一次只有一个线程可以进入临界区。<br />使用System.Monitor类（锁定与信号量）进行线程同步：<br />using System;<br />using System.Threading;</p>
				<p>public void SaveData(string text) //线程执行函数或线程执行函数调用的对象的方法<br />{<br />  ...   //执行其他一些不需要同步的处理</p>
				<p>  Monitor.Enter(this); //获取对象的Monitor锁<br />  ...   //执行需要同步的处理<br />  Monitor.Exit(this); //释放对象的Monitor锁</p>
				<p>  ...   //执行其他一些不需要同步的处理<br />}<br />说明：当执行Monitor.Enter方法时。这个方法会试图获取对象上的Monitor锁，如果另一个线程已经拥有了<br />这个锁，这个方法将会阻塞(block)，直到这个锁被释放。<br />也可用C#的lock语句来获得和释放一个Monitor锁。上面同步写成：<br />public void SaveData(string text) //线程执行函数或线程执行函数调用的对象的方法<br />{<br />  ...   //执行其他一些不需要同步的处理</p>
				<p>  lock(this)  //获取对象的Monitor锁，代码块执行完成后释放Monitor锁<br />  {<br />  ...   //执行需要同步的处理<br />  }</p>
				<p>  ...   //执行其他一些不需要同步的处理<br />}</p>
				<p>也可以使用System.Threading名称空间的Mutex类（互斥类）进行线程同步。与Monitor锁一样，一次只有一个线程<br />能获得一个给定的互斥。但Mutex要慢得多，但它增加了灵活性。例：<br />using System;<br />using System.Threading;<br />class Database<br />{<br />  Mutex mutex = new Mutex(false); //创建一个互斥，但不立即获得它<br />     //注意：创建互斥在需要同步的方法之外，实际上它只要创建一个实例<br />  public void SaveData(string text) //需要同步的方法<br />  {<br />    mutex.WaitOne();  //等待获得互斥<br />    ...    //需要同步的处理<br />    mntex.Close();  //释放互斥<br />  }<br />}</p>
				<p>Mutex类重载了三个构造函数：<br />Mutex()       //创建并使创建类立即获得互斥<br />Mutex(bool initiallyOwned)    //创建时可指定是否要立即获得互斥<br />Mutex(bool initiallyOwned, string muterName)  //还可以指定互斥的名称<br />Mutex.WaitOne方法也重载了三次：<br />Mutex.WaitOne()      //一直等待<br />Mutex.WaitOne(TimeSpan time, bool exitContext)  //等待TimeSpan指定的时间<br />Mutex.WaitOne(int milliseconds, bool exitContext) //等待指定的毫秒<br />线程的用法：<br />1)并发操作：比如一个程序监视多个COM口，当每个COM接到信息时执行一段处理时。<br />2)复杂长时间操作：一个长时间的复杂操作可能会使界面停滞，停止用户响应，如果还允许用户停止它，<br />或者显示进度条、显示操作执行进程信息时。<br />反射(Reflection)就是能够在运行时查找类型信息，这是因为.NET编译的可执行(PE)文件中包括MSIL和元数据(metadata)。<br />反射的中心是类System.Type。System.Type是一个抽象类，代表公用类型系统(Common Type System, CTS)中的一种类型。<br />using System;<br />using System.Reflection; //反射命名空间，必须引用</p>
				<p>public static void Main(string[] args)<br />{<br />  int i = 6;<br />  Type t = i.GetType();   //根据实例得到类型<br />  t = Type.GetType("System.Int32"); //根据类型的字符名称得到类型<br />}</p>
				<p>通过Assembly类可以得到已经编译.NET Framework程序的中所有类型，例：<br />using System;<br />using System.Diagnostics;  //为了使用Process类<br />using System.Reflection;  //为了使用Assembly类</p>
				<p>class GetTypesApp<br />{<br />  protected static string GetAssemblyName(string[] args)<br />  {<br />    string assemblyName;<br />    if (0 == args.Length) //如果参数为空，取当前进程的名称<br />    {<br />      Process p = Process.GetCurrentProcess();<br />      assemblyName = p.ProcessName + ".exe";<br />    }<br />    else<br />      assemblyName = args[0]; //取第一个参数，即当前运行程序名</p>
				<p>    return assemblyName;<br />  }</p>
				<p>  public static void Main(string[] args)<br />  {<br />    string assemblyName = GetAssemblyName(args);<br />    Assembly a = Assembly.LoadFrom(assemblyName); //调用编译程序集<br />    Type[] types = a.GetTypes();   //得到多个类型<br />    foreach (Type t in types)    //遍历类型数组<br />    {<br />    ...  //取得t.FullName,t.BaseType.FullName等类型信息<br />    }<br />  }<br />}<br />一个应用程序可以包括多个代码模块。若要将一个cs文件编译一个模块，只要执行下面的命令：<br />csc /target:module 要编译的模块.cs  //csc是C Sharp Compiler(C#编译器)<br />然后在应用程序中using编译的模块.cs中的NameSpace即可应用了。<br />要反射应用程序中所有代码模块(Module)，只要：<br />Assembly a = Assembly.LoadFrom(assemblyName); //应用程序的物理文件名<br />Module[] modules = a.GetModules();<br />foreach(Module m in modules)<br />{<br />... //显示m.Name等<br />}<br />后期绑定(latebinding),例：<br />string[] fileNames = Directory.GetFiles(Environment.CurrentDirectory, "*.dll");<br />foreach (string fileName in fileNames)<br />{<br />  Assembly a = Assembly.LoadFrom(fileName);<br />  Type[] types = a.GetTypes();<br />  foreach(Type t in types)<br />  {<br />    if (t.IsSubclassOf(typeof(CommProtocol)))  //判断是否有CommProtocol的派生类<br />    {<br />      object o = Activator.CreateInstance(t);  //生成实例<br />      MethodInfo mi = t.GetMethod("DisplayName");<br />      mi.Invoke(o, null);    //调用方法<br />    }<br />  }<br />}<br />//带参数的例子<br />namespace Programming_CSharp<br />{<br />  using System;<br />  using System.Reflection;<br /> <br />  public class Tester<br />  {<br />    public static void Main( )<br />    {<br />      Type t = Type.GetType("System.Math");<br />      Object o = Activator.CreateInstance(t);</p>
				<p>      // 定义参数类型<br />      Type[] paramTypes = new Type[1];<br />      paramTypes[0]= Type.GetType("System.Double");</p>
				<p>      MethodInfo CosineInfo = t.GetMethod("Cos", paramTypes);</p>
				<p>      //设置参数数据<br />      Object[] parameters = new Object[1];<br />      parameters[0] = 45;</p>
				<p>      //执行方法<br />      Object returnVal = CosineInfo.Invoke(o, parameters);<br />      Console.WriteLine("The cosine of a 45 degree angle {0}", returnVal);<br />    }<br />  }<br />}<br />动态生成代码和动态调用的完整例子：<br />//动态生成代码的部分<br />using System;<br />using System.Reflection;<br />using System.Reflection.Emit;  //动态生成代码必须引用</p>
				<p>namespace ILGenServer<br />{<br />  public class CodeGenerator<br />  {<br />    public CodeGenerator()<br />    {<br />      currentDomain = AppDomain.CurrentDomain;  //得到当前域<br />      assemblyName = new AssemblyName();  //从域创建一个程序集<br />      assemblyName.Name = "TempAssembly";<br />      //得到一个动态编译生成器，AssemblyBuilerAccess.Run表示只在内存中运行，不能保存<br />      assemblyBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilerAccess.Run);<br />      //从编译生成器得到一个模块生成器<br />      moduleBuilder = assemblyBuilder.DefineDynamicModule("TempModule");<br />      //模块生成器得到类生成器<br />      typeBuilder = moduleBuilder.DefineType("TempClass", TypeAttributes.Public);<br />      //为类添加一个方法<br />      methodBuilder = typeBuilder.DefineMethod("HelloWord", MethodAttributes.Public, null, null);<br />      //为方法写入代码，生成代码必须使用到IL生成器<br />      msil = methodBuilder.GetILGenerator();<br />      msil.EmitWriteLine("Hello World");<br />      msil.Emit(OpCodes.Ret);<br />      //最后还需要编译(build)一下类<br />      t = typeBuilder.CreateType();<br />    }<br />    AppDomain currentDomain;<br />    AssemblyName assemblyName;<br />    AssemblyBuilder assemblyBuilder;<br />    ModuleBuilder moduleBuilder;<br />    TypeBuilder typeBuilder;<br />    MethodBuilder methodBuilder;<br />    ILGenerator msil;<br />    object o;<br />    Type t;<br />    public Type T<br />    {<br />      get<br />      {<br />        return this.t;<br />      }<br />    }<br />  }<br />}<br />//动态调用的部分<br />using System;<br />using System.Reflection;<br />using ILGenServer;  //引用动态生成代码的类<br />public class ILGenClientApp<br />{<br />  public static void Main(<br />  {<br />    CodeGenerator gen = new CodeGenerator(); //创建动态生成类<br />    Type t = gen.T;<br />    if (null != t)<br />    {<br />      object o = Activator.CreateInstance(t);<br />      MethodInfo helloWorld = t.GetMethod("HelloWorld"); //为调用方法创建一个MethodInfo<br />      if (null != helloWorld)<br />      {<br />        helloWorld.Invoke(o, null);  //调用方法<br />      }<br />    }<br />  }<br />}<br />调用DLL<br />using System;<br />using System.Runtime.InteropServices; //为了使用DLLImport特性</p>
				<p>class PInvokeApp<br />{<br />  [DllImport("user32.dll", CharSet=CharSet.Ansi)] //CharSet.Ansi指定Ansi版本的函数(MessageBoxA),CharSet.Unicode指定Unicode版本的函数 (MessageBoxW)<br />  static extern int MessageBox(int hWnd, string msg, string caption, int type);  //声明DLL中的函数<br />  <br />  //[DllImport("user32.dll", EntryPoint="MessageBoxA")] //用这种方法使用不同的函数名<br />  //static extern int MsgBox(int hWnd, string msg, string caption, int type);<br />  <br />  //[DllImport("user32.dll", CharSet=CharSet.Unicode)]  //调用Unicode版的DLL函数<br />  //static extern int MessageBox(int hWnd, [MarshalAs(UnmanagedType.LPWStr)]string msg,<br />  // [MarshalAs(UnmanagedType.LPWStr)]string caption, int type); //将LPWStr翻译为string型，缺省情况系统只将LPStr翻译成string<br />  public static void Main()<br />  {<br />    MessageBox(0, "Hello, World!", "CaptionString", 0);  //调用DLL中的函数<br />  }<br />}<br />例2，使用回调：<br />class CallbackApp<br />{<br />  [DllImport("user32.dll")]<br />  static extern int GetWindowText(int hWnd, StringBuilder text, int count);</p>
				<p>  delegate bool CallbackDef(int hWnd, int lParam);</p>
				<p>  [DllImport("user32.dll")]<br />  static extern int EnumWindows(CallbackDef callback, int lParam);</p>
				<p>  static bool PrintWindow(int hWnd, int lParam)<br />  {<br />    StringBuilder text = new StringBuilder(255);<br />    GetWindowText(hWnd, text, 255);<br />    Console.WriteLine("Window Caption: {0}", text);<br />    return true;<br />  }</p>
				<p>  static void Main()<br />  {<br />    CallbackDef callback = new CallbackDef(PrintWindow);<br />    EnumWindows(callback, 0);<br />  }<br />}<br />关键字unsafe指定标记块在非控环境中运行。该关键字可以用于所有的方法，包括构造函数和属性，<br />甚至还有方法中的代码块。关键字fixed负责受控对象的固定(pinning)。Pinning是一种动作，向<br />垃圾收集器(Garbage Collector, GC)指定一些不能被移动的对象。为了不在内存中产生碎片，.NET<br />运行环境把对象四处移动，以便于最有效地利用内存。使用fixed后指定对象将不会被移动，所以就<br />可以用指针来访问它。<br />C#中只能得到值类型、数组和字符串的指针。在数组的情况下，第一个元素必须是值类型，因为C#<br />实际上是返回一个指向数组第一个元素的指针，而不是返回数组自身。<br />&amp; 取一个变量的内存地址（即指向该变量的指针）<br />* 取指针所指变量的值<br />-&gt; 取成员<br />例：<br />using System;<br />class UnsafeApp<br />{<br />  public static unsafe void GetValues(int* x, int* y)<br />  {<br />    *x = 6;<br />    *y = 42;<br />  }</p>
				<p>  public static unsafe void Main()<br />  {<br />    int a = 1;<br />    int b = 2;<br />    GetValues(&amp;a, &amp;b);<br />  }<br />}<br />fixed语法为：fixed(type* ptr = expression) statements<br />其中type也可以为非控类型，也可是void；expression是任何产生一个type指针的表达式；<br />statements是应用的代码块。例：<br />fixed (int* f = &amp;foo.x)  //foo是Foo类的一个实例，x是Foo类的一个int属性<br />{<br />  SetFooValue(f);  //SetFooValue方法的定义为unsafe static void SetFooValue(int* x)<br />}<br />传统的COM组件可以通过互操作层(COM Interop)与.NET运行环境交互。互操作层处理在托管运行环境和非托管区域<br />中的COM组件操作之间传递所有的消息。<br />要使COM组件能在.NET环境中使用,必须为COM组件生成元数据。.NET运行环境用元数据层业判断类型信息。在运行时刻<br />使用类型信息，以便生成RCW(Runtime Callable Wrapper,运行时可调用包装)。当.NET应用程序与COM对象交互时，<br />RCW处理对COM对象的装载和调用。RCW还完成许多其他的工作，如管理对象标识、对象生存周期以及接口缓冲区。<br />对象生存周期管理十分关键，因为.NET GC把对象到处移动，并且当对象不再使用时，自动处理这些对象。RCW服务告诉<br />.NET，应用程序正与托管.NET组件交互，同时又使非托管COM组件“觉得”COM对象是被传统的COM客户端调用的。<br />为了为COM组件生成元数据包装，必须使用tlbimp.exe(TypeLib Importer)工具：<br />tlbimp some_COM.tlb /out:som_COM.dll</p>
		</div>
<img src ="http://www.blogjava.net/songfei/aggbug/93800.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2007-01-14 20:18 <a href="http://www.blogjava.net/songfei/articles/93800.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VB中的Unicode 和 Ansi 格式 转换</title><link>http://www.blogjava.net/songfei/articles/55780.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Thu, 29 Jun 2006 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/55780.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/55780.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/55780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/55780.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/55780.html</trackback:ping><description><![CDATA[VB中的Unicode 和 Ansi 格式 <br /><br />Visual Basic 32-bit 版本的字串处理采用 Unicode，也就是说字串在 VB 内部是以Unicode 的格式来存放。何谓 Unicode？简单的说，就是每一个字符都是以 2-byte 的形式表示，而每个「实体字符」就是一个「字符」。因此，<br /><br />Len("大家好")<br /><br />Len("abc")<br /><br />所返回的值都是 3，因为「大」和「a」都是一个字符。<br /><br /><br />但是这对一些中文字串处理，例如纯文字的数据文件，却是一个大灾难，因为你必须以byte 来定位每个字符，可是 Unicode 却把一切的处理全搞砸了。例如：<br /><br />Len("Good Morning") 返回 12，而<br /><br />Len("今天天气很好") 返回 6<br /><br /><br />对初学者而言，好不容易能使用 VB 来写程序已经是件了不起的事了，却马上在中文处理上挨了一记闷棍，所受到的打击实在不小。但是不要怕，事实上只要再多了解一些指令，就可以把中文处理的问题解决了。<br /><br /><br />是什么指令呢？最重要的莫过于 StrConv 了。StrConv 函数的语法为：StrConv(待转换字串, 转换格式)<br /><br />其中转换格式在这里用到的是：<br /><br />vbUnicode 将 Ansi 字串转换为 Unicode<br /><br />vbFromUnicode 将 Unicode 字串转换为 Ansi<br /><br />将字串转成 Ansi 之后，所有的字串处理指令都要加个 B，例如：LeftB, RightB,<br /><br />MidB, ChrB, InstrB, LenB, InputB 等。例用这些指令来处理就行了。<br /><br /><br />当你处理完毕之后，你可以再将它再转回 Unicode，这样就可以使用一般的字串处理指令了。这样讲看得懂吗？如果还是不了解，看看下面的实例说明：<br /><br />简易使用范例<br /><br />看看下面的基本范例您应该就会对 VB 的字串处理方式有些概念。<br /><br /><br />Private Sub Command1_Click ()<br /><br />Dim sUnicode As String<br /><br />Dim sAnsi As String<br /><br />' Unicode 运算<br /><br />sUnicode = "王小明,A123456789,651023,上海市中山路100号,(02)2345678"<br /><br />Debug.Print Len(sUnicode) ' 返回 44<br /><br />Debug.Print Mid$(sUnicode, 5, 10) ' 返回 A123456789<br /><br />Debug.Print Instr(sUnicode, "上海市") ' 返回 23<br /><br />' 将 Unicode 字串转成 Ansi<br /><br />sAnsi = StrConv(sUnicode, vbFromUnicode)<br /><br />' Ansi 运算<br /><br />Debug.Print LenB(sAnsi) ' 返回 54<br /><br />Debug.Print MidB$(sAnsi, 8, 10) ' 返回 ?????，因为忘了转回 Unicode<br /><br />Debug.Print StrConv(MidB$(sAnsi, 8, 10), vbUnicode) ' 返回 A123456789，请注意转回 Unicode 的动作一定要做<br /><br /><br />Debug.Print InStrB(sAnsi, StrConv("上海市", vbFromUnicode)) ' 返回 23, 不要忘了要把"上海市"也转成 Ansi，否则会找不到<br /><br />End Sub<br /><br />读入文本文件<br /><br />在 VB 的小技巧中，有一个是快速读文件法：<br /><br />Private Sub Command1_Click ()<br /><br />Dim sFile As String<br /><br />Open "C:\filename.txt" For Input As #1<br /><br />sFile = Input$(LOF(1), #1)<br /><br />Close #1<br /><br />End Sub<br /><br /><br />但是很不幸地，如果你读取的文件内含中文字，那上面这段程序会出现 Input pastend of file 的错误。因为 LOF 返回的是文件的 byte 数，而 Input 函数读取的是字符数，由于文件内含中文，因此文件中的字符数将会小于 byte 数，于是就发生错误了。要解决这个问题，我们就要用到 StrConv 和 InputB 这两个函数了：<br /><br />Private Sub Command1_Click ()<br /><br />Dim sFile As String<br /><br />Open "C:\filename.txt" For Input As #1<br /><br />sFile = StrConv(InputB$(LOF(1), #1), vbUnicode)<br /><br />Close #1<br /><br />End Sub<br /><br />上面修正程序先用 InputB 将文件读进来，不过使用 InputB 所读入的文件是 Ansi格式的，所以要再用 StrConv 转成 Unicode 才行。随机数据文件许多文字数据文件是以固定字节的位置来加以区格，例如下面的数据格式：王小民650110上海市中山路100号 (02)1234567<br /><br />张大呆660824花莲县大甲镇广东街23号(03)9876543<br /><br /><br />像这种类型的文件要如何处理呢？这是就必须用到 Type 以及 byte array 了。<br /><br />Private Type tagRecord<br /><br />Username(5) As Byte ' 姓名 6 bytes<br /><br />Birthday(5) As Byte ' 生日 6 bytes<br /><br />Address(21) As Byte ' 地址 22 bytes<br /><br />TEL(11) As Byte ' 电话 12 bytes<br /><br />CrLf(1) As Byte ' 换行字符 2 bytes<br /><br />End Type<br /><br /><br />Private Sub Command1_Click()<br /><br />Dim uRecord As tagRecord<br /><br />Open "C:\filename.dat" For Random As #1 Len = LenB(uRecord)<br /><br />Get #1, 2, uRecord ' 取第二笔数据<br /><br />With uRecord ' With ... End With 应该会用吧<br /><br />Debug.Print .Username ' 返回 ???<br /><br />Debug.Print StrConv(.Username, vbUnicode) ' 返回 "张大呆"<br /><br />End With<br /><br />Close #1<br /><br />End Sub<br /><br /><br />在这个例子中，一定要用到 byte array，因为只有 byte array 才能正确地定位到每个 byte 的位置。以前使用字串来定位的方法已经不适用了，千万要记住！但是使用byte array 所读入的数据是 Ansi 格式，若要处理或是做运算的话，记得还要转成Unicode 格式才行。<br /><br /><br />[●] 使用 Byte Array<br /><br />除了上面必须使用 byte 精确定位的例子之外，纯文字的处理基本上是用不到 bytearray 的。byte array 通常是用在处理 binary 数据。这方面的问题我们将另文讨论。看吧！只要熟悉使用 StrConv，你就可以在 Unicode 及 Ansi 格式之间自由自在地变来变去，相信当您看完这篇文章之后，对处理中文应该不再烦恼了吧！<br /><br /><img src ="http://www.blogjava.net/songfei/aggbug/55780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2006-06-29 16:54 <a href="http://www.blogjava.net/songfei/articles/55780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从VB 6到VB.NET——窗体特殊应用</title><link>http://www.blogjava.net/songfei/articles/55748.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Thu, 29 Jun 2006 06:49:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/55748.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/55748.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/55748.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/55748.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/55748.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 从VB 6到VB.NET——窗体特殊应用李洪根一、   摘要    VB .NET做为VB6的升级版本，具备了许多新的功能，它可以简便快捷地创建 .NET 应用程序（包括 XML Web services 和 ASP.NET Web 应用程序），还是一个功能强大的面向对象的编程语言（如继承、接口和重载）。新的语言功能包括自由线程处理和结构化异常处理。VB.NET 还完全集成了.NET 框架和公共语...&nbsp;&nbsp;<a href='http://www.blogjava.net/songfei/articles/55748.html'>阅读全文</a><img src ="http://www.blogjava.net/songfei/aggbug/55748.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2006-06-29 14:49 <a href="http://www.blogjava.net/songfei/articles/55748.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C#与vb.net的区别 </title><link>http://www.blogjava.net/songfei/articles/50774.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Tue, 06 Jun 2006 07:17:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/50774.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/50774.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/50774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/50774.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/50774.html</trackback:ping><description><![CDATA[
		<p>都说在.NET里，.NET framework和CLR是应用运行的基础。那么VB.NET和C#是不是就完全一样，只是语法不同吗？</p>
		<p>一、C#</p>
		<p>--- 像VB一样简单，像C++一样强大的新语<br />C#是第一流的面向组件的语言<br />由 Turbo Pascal, Delphi, and Visual J++的首席设计师Anders Hejlsberg 倾心3年设计<br />所有的语言元素都是真正的对象<br />开发强壮和可重用的软件<br />所有的.NET Framework中的基类库（Base Class Library）都由C# 编写</p>
		<p>
				<br />二、VB.NET</p>
		<p>--- 完全面向对象的BASIC语言</p>
		<p>1.新语言特性<br />完全面向对象–  继承(Inheritance), 重载(overloading), 共享的成员, 结构化异常处理<br />强壮的语言 –  严格的类型检查, 变量声明时初始化，支持垃圾收集(Garbage collection)<br />强大 –  支持委托(Delegates), free threading，Variant 数据类型被 Object 代替<br />2.与VB6一致的语法</p>
		<p>三、2者的区别？<br />1.语法上的差异<br />        例如循环的写法</p>
		<p>VB.NET</p>
		<p>For I = 1 To 10<br />   ‘ for loop<br />Next I</p>
		<p>C#</p>
		<p>For (i=1;i&lt;11;i++) {<br />// for loop<br />}<br />另外Visual Basic 对大小写不敏感，而C#是大小写敏感的。</p>
		<p>2.C# 具有但 Visual Basic 不具有的特性<br />           指针, 移位操作符, 内嵌的文档(XML)<br />          重载操作符</p>
		<p>3.Visual Basic具有但 C# 不具有的特性</p>
		<p>更丰富的语法: Events, Try…Catch, Select…Case, 实现 Interface <br />后期绑定(Late binding), 动态数组, 模块(modules), 可选参数, 参数属性(parameterized properties)<br />后台编译</p>
		<p>
				<br />C#与VB.net间移植的技巧</p>
		<p>
				<br />按理说，这两种语言没有什么移植的必要，因为他们生成的代码可以完全通用。但是如果一个工程基本上是VB写成的，却需要少许已经存在的C#过程，用组件并不是一种效率很高的办法。就算是学习C#或VB，学会他们之间的移植可以双倍的利用已经存在的代码（如好玩的Donkey.net就只有VB版）。 <br />   <br />  有人比较过这两种语言，得出的结论是他们很相似。但即便是这样，VB初学者看到诸如(（Button）sender).Text = “启动”;之类的语法不知道如何移植到VB，而C#初学者看到Handles Button1.Click等语法也会为移植而感到头痛。下面就看看他们最难移植的部分： <br />   <br />  1、Option语句。VB的Option语句可以打开或关闭变量声明检查和类型转换检查。尤其是Option Strict被关闭后，VB变成弱类型语言，很多类型转换都是自动的，移植到C#中会产生无数错误。因此，如果要将带有Option Strict Off语句的VB程序移植到C#，最好先在VB中将Option Strict打开，然后把所有类型转换出错的地方变成强类型转换，然后再进行移植。 <br />   <br />  2、类型转换。VB提供了很多类型转换函数型运算符，如CInt(), CSng(), CStr()等，在C#中只要用(int) , (float), (String)即可。然而如果不是标准类型，如下面的C#语句： <br />   <br />  ((System.Button)sender).Text = “启动”; <br />   <br />  就要使用VB的函数型运算符CType来实现。上面的代码正确的移植方法是： <br />   <br />  CType(sender, System.Button).Text = “启动” <br />   <br />  千万不要使用某些人推荐的，将Option Strict关闭，然后用后期绑定调用sender对象的方法，这根本不符合程序移植不能改变本质的要求。 <br />   <br />  3、修饰符和属性标签。VB和C#的修饰符是完全对等存在的，但是拼写往往不同，给移植带来了很多麻烦，尤其是表示相同意思的关键字从字面理解完全不同的时候。下面就给出了VB和C#对应的关键字： <br />   <br />  VB <br />   C# <br />   VB <br />   C# <br />   <br />  Inherits <br />   : <br />   Implements <br />   : <br />   <br />  MustInherit <br />   abstract <br />   NotInheritable <br />   sealed <br />   <br />  Overridable <br />   virtual <br />   NotOverridable <br />   sealed <br />   <br />  MustOverride <br />   abstract <br />   Overrides <br />   override <br />   <br />  [Overloads] <br />   无 <br />   Shadows <br />   new <br />   <br />  Shared <br />   static <br />   Public <br />   public <br />   <br />  Protected <br />   protected <br />   Friend <br />   internal <br />   <br />  Protected Friend <br />   protected internal <br />   Private <br />   private <br />   <br />  Static <br />   用别的方法实现 <br />   ByVal <br />   无 <br />   <br />  ByRef <br />   ref <br />   Optional <br />   无 <br />   <br />  ParamArray <br />   params <br />   无法实现 <br />   unsafe <br />   <br />  无法实现 <br />   fixed <br />   <br />   <br />  可以看出，VB的关键字比较长，而且使用上也比C#更加严格。从C#向VB移植的时候，要分外注意哪些VB有而C#没有的关键字以及在C#拼写相同，在VB中拼写不同的关键字（如MustOverride和MustInherit）。有些关键字如unsafe，如果C#使用了他们，将无法移植到VB中。好在这些关键字在商业应用中并不常用。 <br />   <br />  属性标签在这两种语言中非常的相似，移植上应该没有任何难度，只要知道在C#中用方括号[]表示属性标签，而在VB中用的是尖括号&lt;&gt;。另外，如果要用名称结合传递参数，C#直接使用=号，而VB使用:=（冒号和等号）。 <br />   <br />  4、委派类型。委派类型就是安全的函数指针类型。在C#中，难以分辨是函数指针在工作还是函数本身在工作，因为他们的语法相同。当要为一个委派类型的变量复制的时候，直接等于一个函数即可，如： <br />   <br />  public delegate void FuncType(Object e) <br />   <br />  ... <br />   <br />  FuncType func; <br />   <br />  func = new FuncType(this.SampleFunction1); <br />   <br />  //调用 <br />   <br />  func(something); <br />   <br />  //换指向另外一个函数 <br />   <br />  func = this.SampleFunction2 <br />   <br />  然而VB中，委派类型就像是一般的对象，有它的方法，与函数本身明显不同。你不能将过程的名字直接赋给一个委派类型的对象，而必须使用AddressOf运算符，下面的例子就是上文C#程序的VB版，注意那些实现不同的地方： <br />   <br />  Public Delegate Sub FuncType(ByVal e As Object) <br />   <br />  ... <br />   <br />  Dim func As FuncType <br />   <br />  func = New FuncType(AddressOf Me.SampleFunc1) <br />   <br />  ‘ 调用 <br />   <br />  func.Invoke(something) <br />   <br />  ‘ 换指向另外一个函数 <br />   <br />  func = AddressOf Me.SampleFunction2 <br />   <br />  5、事件处理。这是两种语言最大的差别之一，VB传承以前版本强大的事件处理机制，许多语法都比C#更加灵活。好在无论什么情况，他们之间都是可以互相移植的。 <br />   <br />  对于事件定义，两种语言都是一个委派类型加一个事件属性，如： <br />   <br />  [C#] <br />   <br />  public delegate void MyEventHandler(Object sender, EventArgs e); <br />   <br />  public event MyEventHandler MyEvent; <br />   <br />  [Visual Basic] <br />   <br />  Public Delegate Sub MyEventHandler(ByVal sender As Object, ByVal e As EventArgs) <br />   <br />  Public Event MyEvent As MyEventHandler <br />   <br />  VB还支持另外一种更加紧凑的定义方法，只有一条语句： <br />   <br />  Public Event MyEvent(ByVal sender As Object, ByVal e As EventArgs) <br />   <br />  移植的时候，要把参数部分分离出来，成为一个委派类型，再按照普通方法定义事件即可。 <br />   <br />  关于事件响应，C#与Delphi等语言一样，是动态绑定事件过程的，其语法类似于下： <br />   <br />  internal MyClass myobj; <br />   <br />  ... <br />   <br />  myobj = new MyClass(); <br />   <br />  ... <br />   <br />  myobj.MyEvent += this.myobj_MyEvent; <br />   <br />  ... <br />   <br />  protected void myobj_MyEvent(Object sender, EventArgs e) <br />   <br />  { <br />   <br />   //语句 <br />   <br />  } <br />   <br />  可以看到，C#是利用运算符连接事件过程和事件属性的。之后，还可以用-=运算符解除事件过程与事件属性的绑定。VB不支持运算符重载，但仍然支持这种动态绑定的事件过程，方法是使用AddHandler和RemoveHandler关键字。如上面黑体部分可以移植为： <br />   <br />  AddHandler myobj.MyEvent, AddressOf Me.myobj_MyEvent <br />   <br />  解除绑定的语法与此类似，只是关键字为RemoveHandler而已。一定不要忘记过程前面还有一个AddressOf关键字！ <br />   <br />  动态绑定的事件过程工作起来比较慢，VB支持一种更快的静态绑定事件过程。一旦为对象设置了静态的事件过程，就不能解除绑定，一般大多数情况都是如此。语法如下： <br />   <br />  ‘ 定义变量时使用WithEvents关键字 <br />   <br />  Friend WithEvents myobj As MyClass <br />   <br />  ‘ 直接书写事件过程，注意Handles的语法： <br />   <br />  Protected Sub myobj_MyEvent(ByVal sender As Object, ByVal e As EventArgs) _ <br />   <br />  Handles myobj.MyEvent <br />   <br />   ‘ 语句 <br />   <br />  End Sub <br />   <br />  它表示myobj_MyEvent这个过程仅仅响应myobj.MyEvent这个过程。如果一个过程要响应很多个事件，把他们列在Handles后面，用逗号隔开，如Handles Event1, Event2, ... <br />   <br />  遇到这种情况，要看清Handles后面的所有对象和事件，将它们一一改写成动态绑定的语句： <br />   <br />  Protected Sub XXX(...) Handles myobj1.MyEvent, myobj2.MyEvent <br />   <br />  ==&gt; <br />   <br />  myobj1.MyEvent += this.XXX; <br />   <br />  myobj2.MyEvent += this.XXX; <br />   <br />  ... <br />   <br />  protected void XXX(...){} <br />   <br />  当事件比较多时，C#显著变得比较麻烦，幸好一个过程响应一大堆事件的情况也不太多（不过我就编写过一个过程相应8个事件，移植起来好麻烦！）。原则上说，将静态事件过程移植为动态事件过程并没有完全遵守移植的规定，但我估计他们实现的原理不会相差太多，所以也不用担心。 <br />   <br />  6、异常处理。VB支持两种形式的异常，即.net框架的异常和VB自己的错误号码。而C#只支持第一种。用到VB自己的错误号码的程序几乎无法移植到C#中，所以应该尽量使用.net框架的异常，如下面VB语句： <br />   <br />  Try <br />   <br />   ‘ 发生错误的代码 <br />   <br />  Catch When Err.Number = 52 <br />   <br />   ‘ 解决错误的代码 <br />   <br />  End Try <br />   <br />  这段代码无法直接移植到C#中，只有用Exception对象取代Err对象获得异常信息，才能顺利移植。另外VB的When语句带给Try语句十分灵活的用法，必须用很高的技巧才能在C#中实现，这就需要具体问题具体分析了。 <br />   <br />  VB支持Exit Try语句，可以直接从Try块或Catch块跳转到Finally块。C#没有提供类似的语法，可以用以下技巧： <br />   <br />  [Visual Basic] <br />   <br />  Try <br />   <br />   ‘ 一些语句 <br />   <br />   Exit Try <br />   <br />  Finally <br />   <br />   ‘ 一些语句 <br />   <br />  End Try <br />   <br />  [C#] <br />   <br />  try <br />   <br />  { <br />   <br />   //一些语句 <br />   <br />   goto __leave; <br />   <br />  } <br />   <br />  finally <br />   <br />  { <br />   <br />   //一些语句 <br />   <br />  } <br />   <br />  __leave: //别忘了这里还有哦！ <br />   <br />  总之是利用了finally块无法跳过的特征，用goto语句模拟了Exit Try语句。 <br />   <br />  如果VB程序用的是VB特有的On Error GoTo语句实现的错误处理，问题就麻烦了。代码可能在过程中上下跳跃，无法预料语句的执行方式。这种代码理解起来就头痛，更不要说移植了。总体来说，把所有语句统统转移到try块中，然后用catch一一处理错误。遇到要返回（Resume语句）的时候，只好Copy代码了。反正不是一件容易的事情，慢慢改就是了。 <br />   <br />  7、模块。VB支持模块，C#不支持。但也没有关系，只要在C#中制造一个abstract类，共享所有成员，就和模块一样了。当然不能像VB一样直接访问模块中的成员，需要用到“类名.成员名”的用法。 <br />   <br />  8、接口。C#在接口方面也没有VB强大（怎么这么重要的功能也不做得好一点？），VB采用Implements语句结合接口的成员和类的实现成员，而C#是用名称结合。因此VB就可以随便修改实现成员的访问级别和名称，而C#就不能改名称。将C#移植为VB时，最好利用VB.net编辑器直接实现接口，比较简单。把VB移植为C#时，就必须把改过的名字都改回来，遇到名字冲突就更讨厌了（这时候我几乎不想再移植为C#了）。给一个例子： <br />   <br />  [Visual Basic] <br />   <br />  Public Class Class1 : Implements IMyInterface <br />   <br />   Public Sub DoSth() Implements IMyInterface.Method1 <br />   <br />   End Sub <br />   <br />  End Class <br />   <br />  [C#] <br />   <br />  public class Class1 : IMyInterface <br />   <br />  { <br />   <br />   public void Method1() <br />   <br />  { <br />   <br />  } <br />   <br />  } <br />   <br />  9、运算符重载。这会遇到VB头痛了，既然VB不支持运算符重载，那么就必须用子程序和函数来模拟运算符。比如建立Plus和Minus方法模拟+和-的运算。当然还是有很多情况（比如遇上了explicit和implicit语句）就真的没有办法了，只好不移植了。运算符重载是一个很不错的功能，它能使很多操作简单地完成，如果VB支持它，就真的是完美语言了。 <br />   <br />  好了，想必最麻烦的地方已经说完了，剩下的就是简单的Copy了。虽然有些地方还没有说清楚，但基本上阐明了两种语言的不同（一看，不同还挺多的吧），反正也不用移植大的工程，了解这些内容主要是为了利用双倍的利用已经存在的代码，但愿本文对你有用。</p>
<img src ="http://www.blogjava.net/songfei/aggbug/50774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2006-06-06 15:17 <a href="http://www.blogjava.net/songfei/articles/50774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Ajax.Net的快速入门 </title><link>http://www.blogjava.net/songfei/articles/41823.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Wed, 19 Apr 2006 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/41823.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/41823.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/41823.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/41823.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/41823.html</trackback:ping><description><![CDATA[
		<table class="p21" style="BORDER-BOTTOM: #000000 1px solid" cellspacing="0" cellpadding="4" width="100%" border="0">
				<tbody>
						<tr>
								<td align="middle">
										<b>关于Ajax.Net的快速入门 </b>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="content" cellspacing="0" cellpadding="2" width="100%" bgcolor="#fff7bc" border="0">
				<tbody>
						<tr>
								<td align="middle">
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<script language="JavaScript"><![CDATA[
function doZoom(size){
document.getElementById('zoom').style.fontSize=size+'px';
}
]]&gt;</script>
		<table cellspacing="0" cellpadding="0" width="95%" border="0">
				<tbody>
						<tr>
								<td class="p14" style="WORD-WRAP: break-word">
										<span id="zoom">
												<span id="tech_art_center">
												</span>
												<p style="TEXT-INDENT: 2em">现在的项目准备用ajax，用ajax.net实现，而不是atlas，所以先看下ajax.net，Ajax.Net现在的最新版本是AjaxPro5.11.4.2,下载地址是：www.schwarz-interactive.de 
</p>
												<p style="TEXT-INDENT: 2em">首先我们新建个项目，名字是AjaxPro,我用的是vs2005beta2版本。 
</p>
												<p style="TEXT-INDENT: 2em">右击站点名字点add reference添加对我们刚刚下载来的那个叫AjaxPro.2.dll的引用，如果你用的是vs2003，则添加对AjaxPro.dll的引用，然后我们在添加个web.config文件（很郁闷的是vs2005不再自动添加web.config文件拉），修改web.config如下： 
</p>
												<p style="TEXT-INDENT: 2em">
												</p>
												<center>
														<ccid_nobr>
																<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
																		<tbody>
																				<tr>
																						<td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6">
																								<pre>
																										<ccid_code>&lt;system.web&gt; &lt;httpHandlers&gt; 
&lt;add verb="POST,GET" path="ajaxpro/*.ashx" 
type="AjaxPro.AjaxHandlerFactory, 
AjaxPro.2"/&gt; &lt;/httpHandlers&gt;</ccid_code>
																								</pre>
																						</td>
																				</tr>
																		</tbody>
																</table>
														</ccid_nobr>
												</center>
												<p style="TEXT-INDENT: 2em">意思是所有的ajaxpro/*.ashx请求都由Ajax.PageHandlerFactory处理，而不是由默认的System.Web.UI.PageHandlerFactory处理程序工厂来处理。 
</p>
												<p style="TEXT-INDENT: 2em">我们现在给Default.aspx.cs文件添加个名字空间namespace MyDemo，这里更加郁闷的是为什么vs2005beta2怎么不给你自动添加名字空间啊？和2003怎么完全不同呢？ 
</p>
												<p style="TEXT-INDENT: 2em">现在我们写个AjaxMethod服务器端方法，他和普通的服务器方法唯一不同的地方就是他必须要在方法的上面添加个[AjaxPro.AjaxMethod],代码如下： 
</p>
												<p style="TEXT-INDENT: 2em">
												</p>
												<center>
														<ccid_nobr>
																<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
																		<tbody>
																				<tr>
																						<td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6">
																								<pre>
																										<ccid_code>[AjaxPro.AjaxMethod]
public DateTime GetServerTime()
{
return DateTime.Now;
}
[AjaxPro.AjaxMethod]public int 
AddTwo(int firstInt, int secondInt)
{
return firstInt + secondInt;
}</ccid_code>
																								</pre>
																						</td>
																				</tr>
																		</tbody>
																</table>
														</ccid_nobr>
												</center>
												<p style="TEXT-INDENT: 2em">我们还必须在Page_Load里面把这个类注册下，如下： 
</p>
												<p style="TEXT-INDENT: 2em">
												</p>
												<center>
														<ccid_nobr>
																<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
																		<tbody>
																				<tr>
																						<td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6">
																								<pre>
																										<ccid_code>protected void Page_Load
(object sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax
(typeof(_Default));
}</ccid_code>
																								</pre>
																						</td>
																				</tr>
																		</tbody>
																</table>
														</ccid_nobr>
												</center>
												<p style="TEXT-INDENT: 2em">这个时候我们还必须修改aspx页面的指令行，因为我们在后台搞了个名字空间，如下：Inherits="MyDemo._Default"也就是要把名字空间也写上。我们再写客户端脚本来调用服务器方法。代码里有详细的注释，前台Default.aspx代码： 
</p>
												<p style="TEXT-INDENT: 2em">
												</p>
												<center>
														<ccid_nobr>
																<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
																		<tbody>
																				<tr>
																						<td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6">
																								<pre>
																										<ccid_code>&lt;%@ Page Language="C#" 
AutoEventWireup="true"CodeFile="Default.aspx.cs" 
Inherits="MyDemo._Default" %&gt;
&lt;!DOCTYPE html PUBLIC "-
//W3C//DTD XHTML 1.1//EN" 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml" &gt;
&lt;head runat="server"&gt;&lt;title&gt;Untitled Page
&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;form id="form1"
runat="server"&gt;&lt;div&gt;&lt;input id="Button1"
type="button" value="获得服务器时间" 
onclick="getServerTime();" /&gt;&lt;input 
id="Text1" type="text" /&gt;&lt;input 
id="Text2" type="text" /&gt;&lt;input
id="Button2" type="button" 
value="得到两个文本框的和" 
onclick="add(document.getElementById('Text1').
value,document.getElementById('Text2').value)" 
/&gt;&lt;/div&gt;&lt;/form&gt;&lt;script 
type="text/javascript"&gt;
function getServerTime()
{
//MyDemo._Default.GetServerTime()
得到从服务器传来的数据是object，
要写.valuealert
(MyDemo._Default.GetServerTime().value);
}
function add(a,b)
{
//把文本框的值转换成intvar 
a1 = parseInt(a);var b1 = parseInt(b);
//第1、2参数为服务器方法所需要的参数，
后面一个是如果服务器返回数据
//客户端要处理这些数据的js函数名，
他有个参数就是从服务器传来的数据
MyDemo._Default.AddTwo(a1,b1,getAdd);
}
function getAdd(rel)
{
//要加上.valuealert(rel.value);
}
&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;
后台Default.aspx.cs代码：
using System;using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;using 
System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace MyDemo
{
public partial class _Default : 
System.Web.UI.Page{protected void 
Page_Load(object sender, EventArgs e){AjaxPro.Utility.RegisterTypeForAjax
(typeof(_Default));
}
[AjaxPro.AjaxMethod]public 
DateTime GetServerTime()
{return DateTime.Now;
}[AjaxPro.AjaxMethod]public int AddTwo
(int firstInt, int secondInt)
{
return firstInt + secondInt;
}}} 
按F5运行结果如下，
firefox里面测试通过： 
using System;using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace MyDemo
{
public partial class _Default :
System.Web.UI.Page
{
protected void Page_Load
(object sender, EventArgs e)
{AjaxPro.Utility.RegisterTypeForAjax
(typeof(_Default));}
[AjaxPro.AjaxMethod]public DateTime 
GetServerTime(){return DateTime.Now;
}
[AjaxPro.AjaxMethod]public 
int AddTwo(int firstInt, 
int secondInt)
{return firstInt + secondInt;}}}</ccid_code>
																								</pre>
																						</td>
																				</tr>
																		</tbody>
																</table>
														</ccid_nobr>
												</center>
										</span>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/songfei/aggbug/41823.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2006-04-19 10:24 <a href="http://www.blogjava.net/songfei/articles/41823.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写高性能Web 应用程序的10 个技巧</title><link>http://www.blogjava.net/songfei/articles/23039.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Thu, 08 Dec 2005 13:18:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/23039.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/23039.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/23039.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/23039.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/23039.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 固定链接关闭http://spaces.msn.com/members/siskin/Blog/cns!1p4AQvJXh0OPYIKEvlZ7bkbg!108.entry编写高性能 Web 应用程序的 10 个技巧编写高性能 Web 应用程序的 10 个技巧 发布日...&nbsp;&nbsp;<a href='http://www.blogjava.net/songfei/articles/23039.html'>阅读全文</a><img src ="http://www.blogjava.net/songfei/aggbug/23039.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/songfei/" target="_blank">天外飞仙</a> 2005-12-08 21:18 <a href="http://www.blogjava.net/songfei/articles/23039.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用客户端脚本</title><link>http://www.blogjava.net/songfei/articles/23037.html</link><dc:creator>天外飞仙</dc:creator><author>天外飞仙</author><pubDate>Thu, 08 Dec 2005 13:16:00 GMT</pubDate><guid>http://www.blogjava.net/songfei/articles/23037.html</guid><wfw:comment>http://www.blogjava.net/songfei/comments/23037.html</wfw:comment><comments>http://www.blogjava.net/songfei/articles/23037.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/songfei/comments/commentRss/23037.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/songfei/services/trackbacks/23037.html</trackback:ping><description><![CDATA[<TABLE class="fixedTable blogpost" cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=ellipse><SPAN class=bvTitle id=subjcns!1p4AQvJXh0OPYIKEvlZ7bkbg!108><STRONG>使用客户端脚本</STRONG></SPAN></TD></TR>
<TR>
<TD class=bvh8><STRONG></STRONG></TD></TR>
<TR>
<TD id=msgcns!1p4AQvJXh0OPYIKEvlZ7bkbg!108>使用客户端脚本 
<DIV class=date>发布日期： 9/20/2004 | 更新日期： 9/20/2004</DIV>
<DIV class=overview>
<DIV id="">
<P>Scott Mitchell</P></DIV>
<DIV id="">
<P>4GuysFromRolla.com</P></DIV>
<DIV id="">
<P><B>摘要</B>：尽管 ASP.NET 在服务器上执行其大多数操作，但是某些操作在客户端进行处理可能会更好。Scott Mitchell 说明了 ASP.NET 页面和控件如何添加客户端代码。</P></DIV>
<DIV id="">
<P><A href="http://download.microsoft.com/download/A/8/C/A8C44419-05F8-450C-9FB4-F1AED34527F1/MSDNClientSideScript.msi" target=_blank><FONT color=#004377>下载本文的源代码</FONT></A>。</P></DIV></DIV><IMG title="" height=6 alt=* src="http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.gif" width=30 border=0> 
<DIV style="HEIGHT: 18px"></DIV>本页内容 <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EJAA"><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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EJAA"><FONT color=#004377>简介</FONT></A> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EIAA"><FONT color=#004377>创建基类作为添加客户端脚本的基础</FONT></A> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EHAA"><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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EHAA"><FONT color=#004377>从代码隐藏类添加客户端脚本</FONT></A> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EGAA"><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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EGAA"><FONT color=#004377>根据对用户操作的响应执行客户端代码</FONT></A> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EFAA"><FONT color=#004377>实现常用客户端功能</FONT></A> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EEAA"><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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EEAA"><FONT color=#004377>小结</FONT></A> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EDAA"><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> <A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.mspx#EDAA"><FONT color=#004377>相关书籍</FONT></A> 
<DIV id=""><A name=EJAA></A>简介 
<DIV id="">
<P>当使用动态的、基于 Web 的脚本技术时，与传统 ASP 或 PHP 类似，开发人员必须对客户端和服务器间的逻辑、暂时和物理分隔有着敏锐的理解。例如，对于触发服务器端代码执行的用户操作，使用传统 ASP 的开发人员必须明确地使用户的浏览器将请求返回到 Web 服务器。创建这样的交互可能会轻易地占用大量开发时间，并且导致不易读的代码。</P></DIV>
<DIV id="">
<P>Microsoft ASP.NET 通过使用 Web 窗体，有助于减轻将用户事件绑定到特定服务器端代码执行的负担，这就模糊了客户端和服务器间的界线。使用 ASP.NET 和最少的工作，开发人员就可以快速地创建如下的网页，它具有大量的交互式用户界面元素按钮、下拉列表等，而这些都基于最终用户的操作，可以选择性地运行服务器端代码。例如，利用 ASP.NET 添加下拉列表，只要选定的下拉列表项目更改则执行某些操作，您只需添加 <B>DropDownList</B> Web 控件、将其 <B>AutoPostBack</B> 属性设置为 True，然后为该下拉列表创建一个 <B>SelectedIndexChanged</B> 事件处理程序。如果利用传统的 ASP 完成上述任务，则会要求编写许多复杂的 HTML、客户端 JavaScript 和服务器端脚本代码；利用 ASP.NET，则为您提供了必要的脚本代码和服务器端事件模型。</P></DIV>
<DIV id="">
<P>尽管在执行客户端操作时，ASP.NET 中的 Web 窗体极大地简化了运行服务器端脚本，但是，如果误用这种功能可能会导致无法接受的性能。尽管 Web 窗体隐藏了所涉及的复杂性，每次需要执行服务器端代码时，最终用户的浏览器必须通过重新提交窗体，将请求返回到 Web 服务器。当提交窗体时，所有窗体字段（文本框、下拉列表和复选框等）必须同时返回它们的值。此外，页面的视图状态也被返回到 Web 服务器。总而言之，每次回发网页时，几千字节的数据将需要潜在地发送回 Web 服务器。于是，经常回发可能很快就会导致 Web 应用程序不可使用，尤其是对于那些仍然使用拨号连接的用户。通过将功能推到客户端可以降低经常回发的需要。</P></DIV>
<DIV id="">
<P><B>注</B> ASP.NET Web 窗体发出一个标题为 VIEWSTATE 的隐藏窗体字段，它包含 Web 窗体中 Web 控件已更改状态的基于 64 位编码的表示。根据出现的 Web 控件，视图状态的范围可以从几十字节到几万字节。要学习有关视图状态的更多知识，请查阅我的文章 <A href="http://msdn.microsoft.com/library/en-us/dnaspp/html/viewstate.asp" target=_blank><FONT color=#004377>Understanding ASP.NET View State</FONT></A>。</P></DIV>
<DIV id="">
<P>利用传统的 ASP，添加数据驱动、自定义客户端脚本非常简单，但并不是非常易读。例如，要在传统的 ASP 中显示根据某个 ID 字段加载 URL 的弹出窗口，您可以使用 语法来插入 ID 字段的值，在适当的客户端脚本中进行输入。ASP.NET 允许您利用 Page 类中的各种方法，创建这种数据驱动的客户端脚本。</P></DIV>
<DIV id="">
<P>本文分析了向 ASP.NET 网页添加客户端脚本的技术。客户端脚本是运行在访问者浏览器中的脚本代码，如其名字所示。我们将看到如何完成常见的客户端任务，例如显示警告、确认框和弹出窗口。（客户端脚本窗体字段验证的一个主要用途可能与 ASP.NET 主题有点不相关，因为验证程序 Web 控件提供了随取随用的客户端窗体验证。）本文的重点在于插入客户端脚本的服务器端类、方法和技术；我们不会详细地分析实际的客户端脚本，因为该信息涉及了围绕 Web 的众多其他文章和站点。</P></DIV></DIV>
<DIV id="">
<DIV><A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.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/webservices/asp.net/WorkwithClientSideScript.mspx#top"><FONT color=#004377>返回页首</FONT></A></DIV><A name=EIAA></A>创建基类作为添加客户端脚本的基础 
<DIV id="">
<P>传统的 ASP 和 ASP.NET 之间的主要差别之一在于各自技术的编程模型。ASP 页面是原子的、程序上的脚本，解释每个页面的访问。然而，ASP.NET 完全是面向对象的编程技术。所有 ASP.NET 网页都是带有属性、方法和事件的类。所有网页直接或间接地派生自 <B>System.Web.UI</B> 命名空间中的 <B>Page</B> 类，<B>Page</B> 类包含了 ASP.NET 网页的基本功能。</P></DIV>
<DIV id="">
<P>面向对象编程的概念之一就是继承。继承使您可以创建一个扩展其他类功能的新类。（如果类 <I>B</I> 继承类 <I>A</I>，也可以说扩展了 <I>A</I>；类 <I>A</I> 被称为基类。）当使用代码隐藏模型来创建 ASP.NET 网页时，可以非常清楚地看到代码隐藏类继承了 <B>Page</B> 类：</P></DIV>Public Class WebForm1 Inherits System.Web.UI.Page ... End Class 
<DIV id="">
<P>通过使您的代码隐藏类继承 <B>Page</B> 类，它自动接收在 <B>Page</B> 类中继承的功能，例如 <B>Request</B>、<B>Response</B>、<B>Session</B> 和 <B>ViewState</B> 对象以及常见事件，如 <B>Init</B>、<B>Load</B>、<B>Render</B> 等等。我们将在本文中看到，如果您需要可用于所有 ASP.NET 网页的某个常见功能，一种方法是创建派生自 <B>Page</B> 类并具有完成这些所需增强功能的其他方法和属性的类。然后，要使 ASP.NET 网页利用这些增强功能，我们只需更新页面代码隐藏类中的 <B>Inherits</B> 语句，以使用扩展 <B>Page</B> 类的类。 </P></DIV>
<DIV id="">
<P>在本文中，我们将创建一个名为 <B>ClientSidePage</B> 的类，它派生自 <B>Page</B> 类并提供额外的方法以协助执行常见的客户端任务。通过让代码隐藏类继承 <B>ClientSidePage</B>，而不是继承 <B>Page</B>，添加脚本代码将会像调用方法和传送几个参数那样简单。具体说来，该类包括用于下列用途的方法： </P></DIV>• 
<DIV id="">
<P>显示模式客户端对话框。 </P></DIV>• 
<DIV id="">
<P>在页面加载时将焦点设置到特定的窗体字段。 </P></DIV>• 
<DIV id="">
<P>使用模式确定对话框来确定用户是否希望回发该窗体。 </P></DIV>• 
<DIV id="">
<P>显示弹出窗口。 </P></DIV>
<DIV id="">
<P>在我们深入研究 <B>ClientSidePage</B> 类之前，首先让我们分析一下 <B>Page</B> 类中的有关方法，以便将客户端脚本插入到网页中。在我们讨论这些 <B>Page</B> 方法后，我们将开始利用 <B>ClientSidePage</B> 类扩展它们的功能，并且查看如何将所有内容结合在一起以及如何在 ASP.NET 网页中使用扩展的类。</P></DIV></DIV>
<DIV id="">
<DIV><A href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/WorkwithClientSideScript.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/webservices/asp.net/WorkwithClientSideScript.mspx#top"><FONT color=#004377>返回页首</FONT></A></DIV><A name=EHAA></A>从代码隐藏类添加客户端脚本 
<DIV id="">
<P>所有 ASP.NET 网页必须直接或间接地派生自 <B>System.Web.UI </B>命名空间中的 <B>Page</B> 类。<B>Page</B> 类包含正常运行的网页所要求的方法、属性和事件的基本集合。在该类的众多方法中，一些方法旨在将客户端脚本插入到生成的 HTML 中。这些方法从代码隐藏类调用，因此可以用于发出数据驱动的客户端脚本。用于发出客户端脚本的相关 <B>Page</B> 类方法如下所示。</P></DIV>
<DIV id="">
<P>该基类派生自 <B>System.Web.UI.Page</B> 类，因此可以通过从代码隐藏类直接调用 <B>Page</B> 类的公共方法来访问它们。 </P></DIV>
<DIV id="">
<P><B>注</B> 要访问 <B>Page</B> 类的方法，您可以直接键入方法名，或者通过输入 <B>MyBase</B>.（对于 Microsoft Visual Basic .NET）、<B>this</B>. （对于 C#）或者 <B>Page</B>.（对于 C# 或 Visual Basic .NET），利用 Microsoft Visual Studio .NET 中的 IntelliSense 来实现。如果使用 Visual Basic .NET 作为选择的编程语言，请确保将 Visual Studio .NET 配置为不 隐藏高级成员，否则将无法看到这些客户端脚本方法。（要显示高级成员，请转到 <B>Tools</B> | <B>Options</B> | <B>Text</B><B>Editor</B> | <B>Basic</B>，然后清除<B> Hide advanced members</B> 复选框。）</P></DIV>
<DIV id="">RegisterClientScriptBlock(key, script) 
<DIV id="">
<P>在 Web 窗体已呈现的 &lt;<B>form</B>&gt; 元素之后，在包含于 Web 窗体中的任意 Web 控件之前，<B>RegisterClientScriptBlock</B> 方法会添加一个客户端脚本块。<I>key</I> 输入参数允许您指定与该脚本块相关联的唯一的密钥，而 <I>script</I> 参数包括要发出的完整的脚本代码。（这个 <I>script</I> 参数应该包括实际的 &lt;<B>script</B>&gt; 元素，同时还包括客户端 JavaScript 或 Microsoft VBScript。）</P></DIV>
<DIV id="">
<P>在通过 ASP.NET 网页的代码隐藏类发出客户端脚本时，通常情况下，<I>key</I> 参数的值就不是非常重要了。简单地选择一个说明性的密钥值。在通过自定义编译的服务器控件插入客户端脚本代码时，<I>key</I> 参数就更加适用。编译的控件有可能需要一组客户端函数。一个页面上服务器控件的多个实例可以共享这些公用客户端脚本函数，因此对于整个页面而言，这些函数只需要发出一次即可，不需要每个控件实例发送一次。例如，验证控件利用客户端代码来增强用户体验。如果页面上存在任意验证控件，这种客户端代码就必须存在，但是如果存在多个验证控件，那么全部控件都可以使用这个单个的共享函数的集合。</P></DIV>
<DIV id="">
<P>通过为脚本块提供一个密钥，利用公用客户端函数集合构建控件的控件开发人员可以检查所要求的公用函数集合是否已经被页面上控件的另一个实例添加。如果已添加，它不需要重新添加公用脚本。要检查脚本块是否已经使用相同的密钥添加，请使用 <B>IsClientScriptBlockRegistered</B>(key) 方法，它将返回布尔值，表示带有相同密钥的脚本块是否已经进行了注册。需要注意的是可以在不首先检查它是否注册的情况下添加脚本块。如果尝试利用已经注册的密钥添加脚本块，添加的脚本块将被忽略，并且原来的脚本将保持分配到该密钥。</P></DIV>
<DIV id="">
<P><B>注</B><B>IsClientScriptBlockRegistered</B> 方法在以下两种情况下尤为有用。第一，当添加相似但又独特的脚本块时它很方便，您需要确保每个脚本块都给予唯一的密钥。本文稍后分析的代码说明了“is registered”方法的实用性。第二个用途就是当构建需要某个公用脚本的控件时，尤其是如果特别的生成该脚本。通过使用 <B>IsClientScriptBlockRegistered</B> 方法，可以确保对页面上服务器控件的所有脚本通用的脚本在每次页面加载时只生成一次，而不是对页面上的每个控件实例都生成一次。</P></DIV>
<DIV id="">
<P><B>RegisterClientScriptBlock</B> 方法对于添加客户端脚本非常有用，该脚本不依赖于 Web 窗体内出现的任意窗体字段。该方法的常见使用就是显示客户端警告框。例如，设想您具有一个带有一些 <B>TextBox</B> Web 控件和一个“Save”按钮的网页。<B>TextBox</B> 控件可能会具有来自数据库的特殊值。设想该页面允许用户修改这些值并通过单击“Save”按钮提交他们所做的更改。当单击“Save”时，网页将会回发，并且会触发按钮的 <B>Click</B> 事件。您可以为更新数据库的事件创建一个服务器端事件处理程序。要使用户知道他们的更改已经保存，您可能希望显示一个警告框“Your changes have been saved”。通过将以下代码行添加到按钮的 <B>Click</B> 事件处理程序中，可以实现这个任务：</P></DIV>RegisterClientScriptBlock("showSaveMessage", _ "&lt;script language=""JavaScript""&gt; _ alert('Your changes have been saved.'); _ &lt;/script&gt;") 
<DIV id="">
<P>上述代码将会在页面的 &lt;<B>form</B>&gt; 内添加指定的脚本内容，但是在该窗体的内容前。当在用户浏览器中生成页面时，他们将会看到根据页面加载显示的客户端警告框，如图 1 所示。</P></DIV>&lt;form method="post" ...&gt; &lt;script language="JavaScript"&gt; alert('Your changes have been saved.'); &lt;/script&gt; ... &lt;/form&gt; 
<DIV style="WIDTH: 230px"><IMG height=119 alt="" src="http://www.microsoft.com/china/msdn/library/webservices/asp.net/art/clientside_fig01.gif" width=230 border=0><BR>
<P class=figureCaption><B>图</B><B> 1</B>. 客户端 JavaScript 的结果</P>
<DIV class=figureRule></DIV></DIV>
<DIV id="">
<P><B>注</B> 上面示例中一个潜在地不需要的副作用就是，警告框将会在浏览器接收到 &lt;<B>form</B>&gt; 标记后立即显示。在用户单击警告框的“OK”按钮之前，浏览器将挂起网页的呈现。这意味着用户在单击“OK”之前，将看到一个空白的浏览器页面。如果希望在显示警告框之前完全显示该页面，您可以使用 <B>RegisterStartupScript</B> 方法（我们将在下面进行讨论），在 &lt;<B>form</B>&gt; 元素的结尾处插入 JavaScript。</P></DIV></DIV>
<DIV id="">RegisterStartupScript(key, script) 
<DIV id="">
<P><B>RegisterStartupScript</B> 方法与 <B>RegisterClientScriptBlock</B> 方法非常相似。主要的区别在于发出客户端脚本的位置。在 &lt;<B>form</B>&gt; 元素开始后，在窗体的内容前，记住用 <B>RegisterClientScriptBlock</B> 发出脚本。另一方面，<B>RegisterStartupScript</B> 在窗体的<I>结尾</I> 处、在所有窗体字段后，添加指定的脚本。使用 <B>RegisterStartupScript</B> 来放置与呈现的 HTML 元素交互的客户端脚本。（稍后我将研究根据页面加载将焦点设置到窗体字段的示例；要完成这个操作，您将要使用 <B>RegisterStartupScript</B> 方法。）</P></DIV>
<DIV id="">
<P>与 <B>RegisterClientScriptBlock</B> 相似，由 <B>RegisterStartupScript</B> 添加的脚本块需要一个唯一的密钥值。同样，该密钥值主要由自定义控件开发人员使用。并不奇怪，还有一个 <B>IsStartupScriptRegistered(key) </B>方法，它返回布尔值，表示带有指定密钥 的脚本块是否已经进行了注册。</P></DIV>
<DIV id="">
<P><B>注</B> 有关使用 <B>RegisterStartupScript</B> 和 <B>RegisterClientScriptBlock</B> 来创建自定义编译的服务器控件的详细信息，请阅读我以前的文章： <A href="http://msdn.microsoft.com/library/en-us/dnaspp/html/aspnet-injectclientsidesc.asp" target=_blank><FONT color=#004377>Injecting Client-Side Script from an ASP.NET Server Control</FONT></A>.</P></DIV></DIV>
<DIV id="">RegisterArrayDeclaration(arrayName, arrayValue) 
<DIV id="">
<P>如果需要创建带有某些设置值的客户端 JavaScript <B>Array</B> 对象，请使用该方法向特定的数组添加值。例如，当使用 ASP.NET 网页中的验证控件时，就会构建 <B>Array</B> 对象 (<B>Page_Validators</B>)，以包含对页面上验证控件集合的引用。当提交窗体时，就会枚举该数组以检查各种验证控件是否有效。</P></DIV>
<DIV id="">
<P>要将值 1、2 和 3 添加到名为 <B>FavoriteNumbers</B> 的客户端 <B>Array</B> 对象，要使用以下服务器端代码：</P></DIV>RegisterArrayDeclaration("FavoriteNumbers", "1") RegisterArrayDeclaration("FavoriteNumbers", "2") RegisterArrayDeclaration("FavoriteNumbers", "3") 
<DIV id="">
<P>这段代码会发出以下客户端脚本：</P></DIV>&lt;script language="javascript"&gt; &lt;!-- var FavoriteNumbers = new Array(1, 2, 3); // --&gt; &lt;/script&gt; 
<DIV id="">
<P>请注意，被传递的每个数组值都必须是字符串；但是，呈现的客户端脚本将 <B>Array</B> 对象的值设置为字符串的内容。也就是说，如果希望创建带有字符串值“Scott”和“Jisun”的 <B>Array</B>，要使用以下代码：</P></DIV>RegisterArrayDeclaration("FavoriteFolks", "'Scott'") RegisterArrayDeclaration("FavoriteFolks ", "'Jisun'") 
<DIV id="">
<P>请注意，第二个输入参数是包含 'Scott' 和 'Jisun' 的字符串 － 文本由单撇号分隔。这会显示以下客户端脚本：</P></DIV>&lt;script language="javascript"&gt; &lt;!-- var FavoriteFolks = new Array('Scott', 'Jisun'); // --&gt; &lt;/script&gt; </DIV>
<DIV id="">RegisterHiddenField(hiddenFieldName, hiddenFieldValue) 
<DIV id="">
<P>在传统的 ASP 中，通常需要将各种信息从一个页面分发到另一个页面。完成这个操作的常用方法就是使用隐藏窗体字段。（隐藏窗体字段是不显示的窗体字段，但是它的值会根据窗体的提交而发送。创建隐藏窗体字段的语法是 。）在 ASP.NET 中，通过自定义隐藏窗体字段传递信息的需要会极大地减少，因为页面中的控件状态会自动保持。但是，如果您发现需要创建自定义隐藏窗体字段，可以通过 <B>RegisterHiddenField</B> 方法来完成。</P></DIV>
<DIV id="">
<P><B>RegisterHiddenField</B> 方法接受两个输入参数。隐藏字段的名称和值。例如，要创建带有名称 foo 和值 bar 的隐藏窗体字段，请使用以下代码：</P></DIV>RegisterHiddenField("foo", "bar") 
<DIV id="">
<P>这会在页面的 &lt;<B>form</B>&gt; 元素中添加隐藏窗体字段，如下所示：</P></DIV>&lt;form name="_ctl0" method="post" action="test.aspx" id="_ctl0"&gt; &lt;input type="hidden" name="foo" value="bar" /&gt; ... &lt;/form&gt; </DIV>
<DIV id="">理解客户端元素是如何呈现的 
<DIV id="">
<P><B>Page</B> 类包含负责呈现在上面讨论的方法中注册的客户端脚本的两个 <B>internal</B> 方法：<B>OnFormRender</B> 和 <B>OnFormPostRender</B>。（标记为 <B>internal</B> 的方法只能被相同程序集中的其他类调用。因此，无法从 ASP.NET Web 应用程序中的代码隐藏类调用 <B>Page</B> 的 <B>internal</B> 方法。）这两个方法都在 <B>HtmlForm</B> 类的 <B>RenderChildren</B> 方法中进行调用。位于 <B>System.Web.UI.HtmlControls</B> 命名空间中的 <B>HtmlForm</B> 类表示 Web 窗体；也就是说，ASP.NET 网页中的服务器端窗体&lt;form runat="server"&gt;...&lt;/form&gt; 在页面的实例化阶段中作为 <B>HtmlForm</B> 类的实例加载。</P></DIV>
<DIV id="">
<P>因为由 <B>Page</B> 类的众多方法注册的客户端脚本是在 <B>OnFormRender</B> 和 <B>OnFormPostRender</B> 方法中呈现的，并且因为这些方法只能由 <B>HtmlForm</B> 类进行调用，所以通过这些方法以编程方式添加的客户端脚本，只有在网页包含 Web 窗体时才会呈现。也就是说，通过上面讨论的任意方法以编程方式添加的任意脚本元素，在 ASP.NET 网页包含服务器端窗体（&lt;form runat="server"&gt;...&lt;/form&gt;）时只会在页面的最后标记中发出。</P></DIV>
<DIV id="">
<P>通过首先添加开始 &lt;<B>form</B>&gt; 元素，会呈现 ASP.NET 网页上的 Web 窗体。之后，会调用 Web 窗体的 <B>RenderChildren</B> 方法，它包含三行代码：</P></DIV>Page.OnFormRender(...) MyBase.RenderChildren(...) Page.OnFormPostRender(...) 
<DIV id="">
<P>对 <B>Page</B> 类的 <B>OnFormRender</B> 方法的调用会添加以下标记： </P></DIV>• 
<DIV id="">
<P>通过对 <B>RegisterHiddenField</B> 进行调用而添加的任意隐藏窗体字段。 </P></DIV>• 
<DIV id="">
<P>隐藏窗体字段中名为<B> __VIEWSTATE</B> 的基于 64 位编码的视图状态。 </P></DIV>• 
<DIV id="">
<P>通过对 <B>RegisterClientScriptBlock</B> 进行调用而添加的任意脚本块。 </P></DIV>
<DIV id="">
<P>Web 窗体的 <B>RenderChildren</B> 方法中的第二行代码调用基类的 <B>RenderChildren</B> 方法，它会在 Web 窗体中呈现内容。在呈现所有窗体的内容后，对 <B>Page</B> 类的 <B>OnFormPostRender</B> 方法进行调用，这将会添加以下客户端内容： </P></DIV>• 
<DIV id="">
<P>由 <B>RegisterArrayDeclaration</B> 方法添加的任意 <B>Array</B> 对象。 </P></DIV>• 
<DIV id="">
<P>通过对 <B>RegisterStartupScript</B> 进行调用而添加的任意脚本块。 </P></DIV>
<DIV id="">
<P>最后，在 Web 窗体的 <B>RenderChildren</B> 方法完成后，则会呈现关闭窗体标记 ()。图 2 以图表形式说明了这个呈现过程。</P></DIV>
<DIV id="">
<P><B>注</B> 图 2 假设您有些熟悉 ASP.NET 页面的生命周期。如果您对学习更多有关页面生命周期的知识感兴趣，请考虑阅读 <A href="http://msdn.microsoft.com/library/en-us/dnaspp/html/viewstate.asp" target=_blank><FONT color=#004377>Understanding ASP.NET View State</FONT></A>，将重点放在标题为“The ASP.NET Page Life Cycle”的节上。</P></DIV>
<DIV style="WIDTH: 450px"><IMG height=736 alt="" src="http://www.microsoft.com/china/msdn/library/webservices/asp.net/art/clientside_fig02thumb.gif" width=450 border=0><BR>
<P class=figureCaption><B>图</B><B> 2.</B> ASP.NET 中呈现的页面</P>
<DIV class=figureRule></DIV></DIV></DIV>
<DIV id="">分析脚本块的呈现顺序 
<DIV id="">
<P>乍看 Page 类的注册方法，在网页中呈现的已注册元素的顺序好像是对应于它们被添加到代码中的顺序。也就是说，设想 ASP.NET 网页的代码隐藏类中有以下两行代码：</P></DIV>RegisterClientScriptBlock("showSaveMessage", _ "&lt;script language=""JavaScript""&gt;var name='" &amp; _ someDataDrivenValue &amp; "'; &lt;/script&gt;") RegisterClientScriptBlock("showSaveMessage", _ "&lt;script language=""JavaScript""&gt;alert('Hello, ' + name + '!'); &lt;/script&gt;") 
<DIV id="">
<P>当发现页面呈现以下客户端脚本块（假设 someDataDriveValue 的值为 Sam）时，您不要太奇怪：</P></DIV>&lt;script language="JavaScript"&gt;var name='Sam';&lt;/script&gt; &lt;script language="JavaScript"&gt;alert('Hello, ' + name + '!');&lt;/script&gt; 
<DIV id="">
<P>访问该页面的用户将会看到一个警告框显示“Hello, Sam!”。</P></DIV>
<DIV id="">
<P>基于这个测试，您可能会认为这样的事实始终成立：在 HTML 页面中发出脚本块的顺序就是在服务器端代码中为它们指定的顺序。但是，这是一个不正确的假设，并且可以导致页面中断。例如，设想前面添加的脚本块在 HTML 页面中以相反的顺序发出。那么，您将会得到：</P></DIV>&lt;script language="JavaScript"&gt;alert('Hello, ' + name + '!');&lt;/script&gt; &lt;script language="JavaScript"&gt;var name='Sam';&lt;/script&gt; 
<DIV id="">
<P>这将会显示警告框“Hello, !”，因为变量 name 尚未分配值。显然，有些时候发出脚本块的顺序非常重要的。</P></DIV>
<DIV id="">
<P><B>Page</B> 类的注册方法 － <B>RegisterClientScriptBlock</B>、<B>RegisterStartupScript</B>、<B>RegisterArrayDeclaration</B> 和 <B>RegisterHiddenFields</B> － 全部将提供的脚本内容写入到内部 <B>HybridDictionary</B> 中。<B>HybridDictionary</B> 是在 <B>System.Collections.Specialized</B> 命名空间中发现的数据结构，设计用于在有很多项目未知的字典中存储项目。对于小型的项目集合，<B>ListDictionary</B> 是最有效的数据结构，但是对于较大的字典，<B>Hashtable</B> 会更有效。<B>HybridDictionary</B> 分离差异 － 它通过使用 <B>ListDictionary</B> 存储项目来开始。当 <B>ListDictionary</B> 添加了它的第九个项目后，<B>HybridDictionary</B> 会从使用 <B>ListDictionary</B> 切换到使用 <B>Hashtable</B>。</P></DIV>
<DIV id="">
<P>尽管这种方法对于性能而言非常理想，但是如果您使用几个脚本块而且脚本块的顺序非常重要，那么它可能会带来严重的破坏。这是因为，<B>ListDictionary</B> 维护元素被添加的顺序，而 <B>Hashtable</B> 没有进行维护。因此，如果您将八个或更少项目添加到任意一个特定的注册方法中，项目将会以它们被添加的顺序发出。但是，如果添加了第九个项目，脚本发出的顺序看起来将是随机的。</P></DIV>
<DIV id="">
<P><B>注</B><B>ListDictionary</B> 使用 <I>linked list </I>存储它的元素，而 <B>Hashtable</B> 将它的元素存储在数组中，该数组的内容按照字符串键的哈希值进行排序。有关链接列表和哈希表的完整讨论已经远远超出了本文所讨论的范围。有关详细信息，包括对其性能的分析，请考虑阅读 <A href="http://msdn.microsoft.com/library/en-us/dv_vstechart/html/datastructures_guide.asp" target=_blank><FONT color=#004377>An Extensive Examination of Data Structures</FONT></A>，尤其是 <A href="http://msdn.microsoft.com/library/en-us/dv_vstechart/html/datastructures_guide2.asp" target=_blank><FONT color=#004377>Part 2</FONT></A> 和 <A href="http://msdn.microsoft.com/library/en-us/dv_vstechart/html/datastructures_guide4.asp" target=_blank><FONT color=#004377>Part 4</FONT></A>。</P></DIV>
<DIV id="">
<P>如果您计划出现如下情况：可能会存在使用特殊注册方法添加的多于八个客户端元素，并且元素的出现顺序比较重要，那么您可能希望研究一下 <A href="http://www.peterblum.com/Home.aspx" target=_blank><FONT color=#004377>Peter Blum</FONT></A> 的免费的 <A href="http://www.peterblum.com/RegScripts/RegScriptsHome.aspx" target=_blank><FONT color=#004377>RegisterScripts</FONT></A> 库。对于所发出客户端元素的顺序，RegisterScripts 提供了更大的控制能力，并且还提供了无需手动添加 &lt;<B>script</B>&gt; 标记的选项，当