歲月輕品's profile蚂蚁的窝PhotosBlogLists Tools Help

Blog


    11 June

    Format时间显示

    最近改一个VB的老项目,大量的用到了不同格式的时间显示,比较讨厌,虽然没什么技术含量,
    但要用的时候还真是急死人,所以顺便把format的东西总结一下:
     
    Format(Now, "...")
    年: yy/yyyy 两位年/四位年 输出: 08/2008
    月: m/mm 一位月两位月 输出:1/01 (1-12)/(01-12)
    mmm/mmmm 英文简写月/全称月 输出: Jan/January
    ooo ¦oooo 中文显示月 输出为:一月
    日: dd/d ¦y 一位日/两位日 输出: 8/08 (1-31)/(01-31)
    时: h/hh 一位时/两位时 输出: 9/09 (0-23)/(00-23)
    分: n/nn 一位分/两位分 输出: 30/30 (0-59)/(00-59)
    秒: s/ss 一位秒/两位秒 输出: 40/40 (0-59)/(00-59)
    星期: aaa ¦aaaa 中文星期 输出:星期六
    ddd/dddd 英文星期简称/全称 输出: Sat/Satday
    w ¦ww  用数字表示星期 输出:7 表示星期六,星期日为1
    综合显示:
    c 以短时间格式显示时间 输出:2008-1-8 9:30:40
    ddddd 以短时间格式显示年月日 输出:2008-1-8
    dddddd 中文显示年月日 输出:2008年1月8日
    ttttt 以长时间格式显示时间 输出:9:30:40
    ampm 显示上午/下午 Format(Now,'ampm') 输出为:上午
    差不多就这些了吧!
    19 May

    三分钟

    从来没有这样被国人震撼过,三分钟,将永远留在我的心中。作为一个中国人。
    街上静立默哀的人群,停车鸣笛车流
    愿国人都能永远记住这一刻,愿国人自强!
    无论身在何方,愿都为自己是中国人骄傲!

    国殇

    天灾?人祸?
    暂时不想也不愿去考虑
    为地震中逝去的同胞默哀!!
    逝者已矣,生者当坚强。
    从5月12日以来,感受中国人的团结,和少数官员的良心,中国似乎还没有到不可药救的地步。从中央地反应,到身边每一个关心灾区的百姓,用我老婆的话说:第一次真正感觉到自己的爱国心复活了。中国还是有她的脊梁的。
    中国人民从来就是伟大的,接下的重建希望我们的“老爷们”能真正负起责来,不要愧对全国乃至全世界华人的爱心,天灾过后,但愿没有人祸!
     
     
    08 May

    小丫头越来越无法无天了

    开始只是敢和妈妈对着来,现在竟然敢和爷爷对“打”了
    估摸着那天好好教训一顿,狠下心在她小屁屁上啪几下,不然都不知道怎么带她了
    不过可能还真下不了手,呵呵呵,可爱的小东西...
    26 April

    一代佳人

    20年前的歌,20年后再一次听到:
     
    胭脂红粉只能点缀青春
    却不能掩饰岁月留下的伤痕
    有甚麽可让我刻骨铭心
    唯有你唯有你爱人
    海誓山盟说是情深意浓
    问谁真心为爱厮守一生
    你有血你有泪淋漓尽致
    那热情熨烫着我的一颗心
    悲欢岁月浮华人生
    难得有这一份情
    让我在今生今世记忆深深
    是我最心爱的人
    胭脂红粉只能点缀青春
    绝不能掩饰岁月留下的伤痕
    有甚麽可让我刻骨铭心
    唯有你唯有你爱人
    海誓山盟说是情深意浓
    问谁真心为爱厮守一生
    你有血你有泪淋漓尽致
    那热情熨烫着我的一颗心
    悲欢岁月浮华人生
    难得有这一份情
    让我在今生今世记忆深深
    你是我最心爱的人
    让我在今生今世记忆深深
    你是我最心爱的人
    23 April

    c#的com开发,比较烦

    • 步驟一:
      建立“windows控制項程式庫”工程,
    • 步驟二:
      在我們這個例子中用不到帶界面的com控件所以我們將界面的size屬性設置為 0, 0
      我們要用C#寫COM組件必須要實現  public interface IObjectSafety接口因為這是。IObjectSafety接口,把ActiveX控件标记为安全的ActiveX控件。每個接口需要一個GUID特性。要生成變個唯一的Guid,需要運行guidgen.exe工具軟體,要選擇第四項“Registry Format”.
    • 我們還有定義一個接口如下
      [Guid("1B1DFAE7-FC2E-4f05-BC5A-5267408EF2A9")]
       public interface IOLpt1
       {
        [DispId(1)] bool WriteCommand(string com,string flag);
        [DispId(2)] void WhenLoad();
       }
      功能是提供一個其他語言利用我們所寫的方法的接口。如果不用接口的話其他語言將無法使用我們在程序裡面定義的方法(我在其他的地方也看到過不用接口的例子,不知道為什麼,在我的電腦上如果不用接口編譯隻後將看不到我定義的方法)。這個接口定義了兩個方法分別是 bool WriteCommand(string com,string flag); 和 void WhenLoad();
      第一個方法是將命令寫入到並口,第二個方法是當控件啟動的時候讀取Access的命令的內碼並存如到數組中。這裡我們隻介紹如何寫COM組件,其他的將不涉及。
      定義類 UserControl1 繼承 System.Windows.Forms.UserControl 實現接口IOLpt1於IObjectSafety

       [Guid("AC8C1E64-B501-4f8b-A026-B4F1D8EF7DF2")]
       [ ClassInterfaceAttribute(ClassInterfaceType.AutoDual) ]
       public class UserControl1 : System.Windows.Forms.UserControl,IOLpt1,IObjectSafety
    • 步驟三
      我們還要給我們的控件一個強名。強名需要用sn.exe來得到 命令格式
      sn -k myKey.snk 將得到的強名文件myKey.snk復制到你的工程的目錄下,並在我們的工程文件 AssemblyInfo.cs裡面的[assembly: AssemblyKeyFile("")]項中加入你的強名的絕對路徑F:\webpriteCon\PrintExe\mykey.snk
      例如:[assembly: AssemblyKeyFile("F:\\webpriteCon\\PrintExe\\mykey.snk")]
    • 步驟四
      如圖:shuxing 
      將建置項裡面的「注冊COM組件Interop」項設置為true然後編譯隻後就可以注冊到其他的機器讓別的語言進行引用了。
    • 下面是詳細代碼。
    • 我將陸續寫出如何將COM組件發布成ActiveX控件。


    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Data.OleDb;

    namespace PrintExe
    {
     /*必須要實現IObjectSafety接口,
      * 把ActiveX控件標記為安全的ActiveX控件。
      * IObjectSafety 的實現方式如下
      */
     [Guid("10615515-3979-4f87-A0C9-F786F050AE8B"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
     public interface IObjectSafety
     {
      // methods
      [DispId(1)] void GetInterfacceSafyOptions(
       System.Int32 riid,
       out System.Int32 pdwSupportedOptions,
       out System.Int32 pdwEnabledOptions);
      [DispId(2)] void SetInterfaceSafetyOptions(
       System.Int32 riid,
       System.Int32 dwOptionsSetMask,
       System.Int32 dwEnabledOptions);
     }


     [Guid("1B1DFAE7-FC2E-4f05-BC5A-5267408EF2A9")]
     public interface IOLpt1
     {

      [DispId(1)] bool WriteCommand(string com,string flag);
      [DispId(2)] void WhenLoad();
     }

     [Guid("AC8C1E64-B501-4f8b-A026-B4F1D8EF7DF2")]
     [ ClassInterfaceAttribute(ClassInterfaceType.AutoDual) ]
     public class UserControl1 : System.Windows.Forms.UserControl,IOLpt1,IObjectSafety
     {
      string sqlStr;
      static string connStr = @"Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;Jet OLEDB:Database Locking Mode=1;Data Source='C:\\printTypeDB.mdb';Mode=Share Deny None;Jet OLEDB:Engine Type=5;Provider='Microsoft.Jet.OLEDB.4.0';Jet OLEDB:System database=;Jet OLEDB:SFP=False;persist security info=False;Extended Properties=;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Create System Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;User ID=Admin;Jet OLEDB:Global Bulk Transactions=1";

      OleDbConnection myConn = new OleDbConnection(connStr);
      OleDbCommand myComm;
      OleDbDataReader myDr;
      public string comlist="";
      string[,] myArray;

      private System.ComponentModel.Container components = null;

      public UserControl1()
      {
       InitializeComponent();
      }
      protected override void Dispose( bool disposing )
      {
       if( disposing )
       {
        if( components != null )
         components.Dispose();
       }
       base.Dispose( disposing );
      }

      #region 元件設計工具產生的程式碼
      /// <summary>
      /// 此為設計工具支援所必須的方法 - 請勿使用程式碼編輯器修改
      /// 這個方法的內容。
      /// </summary>
      private void InitializeComponent()
      {
       //
       // UserControl1
       //
       this.Name = "UserControl1";
       this.Size = new System.Drawing.Size(0, 0);

      }
      #endregion

      #region IOLpt1 成員

      public bool WriteCommand(string com,string flag)
      {
       //MessageBox.Show("WriteCommand方法已經運行","Error");
       //com為指令碼 str為要列印字符串
       ExeFunction ef = new ExeFunction();
       string[] str = com.Split(new Char[]{'~'});
       //開始將textBox中的外碼取出找到數組中對應的指令碼組成完整的指令
       string Instruction = "";
       string outI = str[0].Trim();
       if(outI!="SET")
       {
        for(int i = 0;i<myArray.Length/5;i++)
        {
         if(myArray[i,0].Trim() == outI)
         {
          char temp1 = Convert.ToChar(Convert.ToInt32(myArray[i,1].Trim()));
          char temp2='1';

          Instruction = temp1.ToString();

          if(myArray[i,2].Trim() != "")
          {
           temp2 = Convert.ToChar(Convert.ToInt32(myArray[i,2].Trim()));
           Instruction += temp1.ToString();
          }

          if(myArray[i,3].Trim() != "")
           Instruction += myArray[i,3].Trim();
         
          Instruction += str[1] + Convert.ToChar(Convert.ToInt32(myArray[i,4].Trim()));
          comlist += Instruction + "~";
          if(flag == "0") //則將指令預存起來
           return true;      
          //MessageBox.Show("方法已經運行完畢","Error");
         }
        }
        if(comlist!="")
        {
         string[] str1 = comlist.Split(new Char[]{'~'});
         comlist="";
         for(int j=0;j<str1.Length-1;j++)
         {
          byte[] buffer = System.Text.Encoding.Default.GetBytes(str1[j]);
          ef.StartP(buffer);
         }
        }
       }else if(outI == "SET") //如果為SET則為設置本機收銀機
       {
        sqlStr = "update printTypeSet set Flag = false";
        myComm = new OleDbCommand(sqlStr,myConn);
        myConn.Open();
        myDr = myComm.ExecuteReader();
        myDr.Close();
        sqlStr = "update printTypeSet set Flag = true where PrintType='" + str[1].Trim() + "'";
        myComm = new OleDbCommand(sqlStr,myConn);
        int i = myComm.ExecuteNonQuery();
        myDr.Close();
        myConn.Close();
        if(i>0)
         MessageBox.Show("設定成功!","OK");
        else
         MessageBox.Show("沒有合適的型號!","OK");
       }
       return true;
      }

      public void WhenLoad()
      {
       //MessageBox.Show("WhenLoad方法已經運行","Error");
       int i = 0;
       //先取得資料的行數
       //將資料取出放入到數組裡面
       try
       {
        sqlStr = "SELECT count(*) FROM printTypeSet WHERE Flag = true";
        myComm = new OleDbCommand(sqlStr,myConn);
        myConn.Open();
        myDr = myComm.ExecuteReader();
        if(myDr.HasRows)
        {
         while(myDr.Read())
         {
          i = (int)myDr[0];
         }
        }
        myDr.Close();
        myArray = new string[i,5];
        sqlStr = "SELECT Instruction,FirstI,SecondI,Parameter,EndI FROM printTypeSet WHERE Flag = true";
        myComm = new OleDbCommand(sqlStr,myConn);
        myDr = myComm.ExecuteReader();
        i = 0;
        if(myDr.HasRows)
        {
         while(myDr.Read())
         {
          myArray[i,0] = myDr[0].ToString();
          myArray[i,1] = myDr[1].ToString();
          myArray[i,2] = myDr[2].ToString();
          myArray[i,3] = myDr[3].ToString();
          myArray[i,4] = myDr[4].ToString();
          i++;
         }
        }
        myDr.Close();
       }
       catch(Exception ex)
       {
        MessageBox.Show("WhenLoad方法運行錯誤" + ex.ToString(),"Error");
       }
       finally
       {
        myConn.Close();
        //MessageBox.Show("方法已經運行完畢","Error");
       }
      }

      #endregion

      #region IObjectSafety 成員

      public void GetInterfacceSafyOptions(Int32 riid, out Int32 pdwSupportedOptions, out Int32 pdwEnabledOptions)
      {
       // TODO:  加入 UserControl1.GetInterfacceSafyOptions 實作
       pdwSupportedOptions = new Int32 ();
       pdwEnabledOptions = new Int32 ();
      }

      public void SetInterfaceSafetyOptions(Int32 riid, Int32 dwOptionsSetMask, Int32 dwEnabledOptions)
      {
       // TODO:  加入 UserControl1.SetInterfaceSafetyOptions 實作
      }

      #endregion
     }
    }

    28 February

    换工作

    很是奇怪,年前没有投简历,几乎每天都接到面试邀请,现在正儿八经投了一大堆简历结果一个电话都没有,郁闷,怎么回事,很是受打击,妈的,做技术的老了真的不值钱????
    25 February

    周末陪丫头,开心

    小丫头精神恢复了不少,趁周末好好陪了她一下,买了些小玩具,丫头很开心,很久没看她笑得那么开心了,而且她很喜欢和我玩,让老爸我很是自豪一把。不过就是不愿意让我抱,想想原因,可能是住院的时候每次打针、换药什么的都是我抱她去,估计在她心里已经有了这样的概念:老爸一抱就不会有什么好事情。唉,这么点小就有心理阴影了,可怜的孩子,老天保佑,我的宝贝健健康康!!!!
    22 February

    昨天被郁闷了一下

    昨天元宵节,公司没有提前下班。下班后急忙忙往家赶,半路接到电盈那边电话,大概意思是他们的项目有些延迟,如果我没有辞职就不要那么急,等一段时间在过去。而现实是我刚刚辞职了,娘的,也是被他们崔的要死才辞的,为了离职时间可以早点不晓得和领导摸了多少嘴皮子。现在竟然这样,怎一个郁闷可以形容。
    今天打电话过去问个明白,结果又说没关系,还是按约定入职,早点过去也没什么,靠,什么意思吗?
    21 February

    关于08年的一点想法

    新年了,虽然有个不是很好的开始,但是还是想对这一年做个畅想,08年很大压力的一年,不过应该也是一个可以有所突破的年程。
    首先是雅思,希望可以最迟在6月份的时候通过。这样就可以在下半年开始申请,年底基本可以走完ACS评估,09年中应该就可以走了。如果可以这样就最好了,实在不行就多考两次,反正08年必须要过雅思,否则就手不过去了。
    再就是换了新的工作,新环境,新同事,新项目。希望可以有所突破,起码如果出不了国,在国内还有一份不错的工作。
    另外就是小丫头的身体实在不好,要想办法调理,这个小丫头从出生就没让我这个当老爸的消停过,但愿现在大一点了身体会好点。
    三件大事,几乎压得我喘不过气,不过相信拼一把还是会有很大的突破的。老天保佑!!!!!!
    20 February

    辞职了,终于要动一下了

    在数网干了差不多4年,付出努力和汗水,说实话收获和自己的付出不成比例。很早就打算走,一来是朋友的挽留,加上一些承诺,当然这些承诺都没实现。再就是要考雅思,在这边似乎可以有多谢时间学外语,其实不然,也忙得要死。新的东家已经找好,离职就直接过去,薪水比较合理,福利也比这边好了很多,只是刚过去可能会忙一些,没有多的时间复习。不过,时间这东西关键看自己,只要挤总还是有的。
    上次雅思没有过,要努力了,时间不等人,过了30岁再移民感觉会多很多不确定的因素。加油!!!!!

    丫头又手术了

    小丫头一岁生日又手术了,医生说是上次手术的并发症。进手术室的那一瞬间,我几乎要晕过去了。可怜的孩子,才那么小,我愿意这一切发生在我身上。我愿意替她受一切罪。老天保佑,思思永远健康!!
    20 November

    小丫头恢复了

    小丫头8个月的时候作手术,可怜一点点小就遭这么大的罪!
    现在一个月了,小丫头终于完全恢复,哈哈,调皮捣蛋的家伙!!
    25 September

    C#异步编程2

    基于事件的异步模式是比 IAsyncResult 模式更高级的一种异步编程模式,也被用在更多的场合。对于相对简单的应用程序可以直接用 .Net 2.0 新增的 BackgroundWorker 组件来很方便的实现,对于更复杂的异步应用程序则需要自己实现一个符合基于事件的异步模式的类。这两者对我都是新东西,先从简单的入手,下一篇里我再去尝试复杂类模型的实现

      模式概述

      支持基于事件的异步模式的类会有若干个 MethodNameAsync 方法表示开始异步操作,并有对应的 MethodNameCompleted 事件。类里面还可能会有 CancelAsync或 MethodNameAsyncCancel 方法用于取消异步操作,并可以有 ProgressChanged 或 MethodNameProgressChanged事件来跟踪执行进度。下面分别作一下解释

      MethodNameAsync 方法可以有两个重载:单调用和多调用,多调用有一个额外的状态对象参数 userState。userState 参数用来区分各次异步操作,使得我们可以多次调用多调用形式的方法而不需要等待任何异步操作的完成(在学习 IAsyncResult 模式时我把状态对象仅仅当成传给回调方法的一个条件来用,可能在使用模式时这么做并没有什么关系,但在实现模式时不把状态对象用作异常调用的唯一标识而另作他用就值得商榷了)。而单调用形式的方法如果在前一个调用尚未完成时调用将会抛出 InvalidOperationException 异常

      如果有多个异步方法,则应使用 CancelAsync 方法来取消挂起的操作,并可使用 userState 来取消指定的挂起任务。如果只有一个异步方法则可以使用 MethodNameAsyncCancel 方法

      另外 MSDN 上说:一次只支持一个挂起的操作的方法(如 Method1Async(string param))是不可取消的。这句话我还没有理解,不可能说是单调用的异步方法就不能取消吧,BackgroundWorker 上都是这样做的 先不管了,接着看ProgressChanged 事件。它有一个 ProgressChangedEventArgs 参数,事件处理程序通过检查该参数的 ProgressPercentage属性来获取任务完成的百分比。如果有多个异步操作挂起,也可以通过检查参数的UserState 属性来分辨操作。如果需要用 ProgressChanged 事件来报告增量结果,则可以把结果保存在派生自 ProgressChangedEventArgs 的类中,并在事件处理程序中使用

      BackgroundWorker

      BackgroundWorker 很好的符合了事件异步操作模式。它有两个重载版本的 RunWorkerAsync 方法(均为单调用形式)和 RunWorkerCompleted 事件,并有 CancelAsync 方法以及 ProcessChanged 事件。不同的是 BackgroundWorker增加了 DoWork 事件,在 RunWorkerAsync 方法调用时发生,以达到将实际执行的开始方法与 BackgroundWorker 分离的目的。还需要提一下的是 WorkerReportsProcess 属性和ReportProcess 方法,前者指示能否报告进度更新,后者引发 ProcessChanged 事件,它们会在接下来的 Demo 里用到

      尝试

      因为平时经常要处理几十兆的文本文件,这个 Demo 就做一个读取文件并显示进度的控制台程序。先看类名和字段

      class BackgroundWorkerDemo
       {
         private BackgroundWorker m_bw;
         string m_FilePath;
       }

      构造函数接收文件路径为参数,设置文件路径并初始化 BackgroundWorker

      public BackgroundWorkerDemo(string filePath)
         {
           m_FilePath = filePath;
      m_bw = new BackgroundWorker();
           m_bw.WorkerReportsProgress = true;
           m_bw.DoWork += new DoWorkEventHandler(BackgroundWorker_DoWork);
           m_bw.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
           m_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);
         }

      接下来看这三个事件的处理程序。每一个事件都有各自的 EventArgs 参数类型,都很简单就不多说了

    第一个 BackgroundWorker_DoWork 方法写得我有些郁闷。我在方法里取文件长度,先是直接取 StreamReader.BaseStream.Length 或 FileInfo.Length ,结果却导致很多文件读不到 100% 就结束了,不得已改成先把整个文件读一次得到字符串的长度。这样的方法当然性能不好了,主要是因为自己对 IO 一直就不够清楚,等下一个主题重新认识下 IO 再回头过来改吧。也望有经验的朋友赐教,感激不尽  /**//// <summary>
         /// DoWork event process method
         /// </summary>
         /// <param name="sender"></param>
         /// <param name="e"></param>
         private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
         {
           long length;
           using (StreamReader sr = new StreamReader(m_FilePath))
           {
             // Get file length
             length = sr.ReadToEnd().Length;
           }
      using (StreamReader sr = new StreamReader(m_FilePath))
           {
             long onePercentOfLength = length / 100;
             long currentPosition = 0;
             int i = 0;
      while (!sr.EndOfStream)
             {
               sr.Read();
               currentPosition ++;
      // Produce ProcessChanged event in each percent reading
               while (currentPosition > onePercentOfLength * i)
               {
                 ((BackgroundWorker)sender).ReportProgress(i++);
               }
             }
      // e.Result will be used in RunWorkerCompleted event process method
             e.Result = currentPosition;
           }
         }
      BackgroundWorker_ProgressChanged 方法,简单输出当前进度
      /**//// <summary>
         /// ProgressChanged event process method
         /// </summary>
         /// <param name="sender"></param>
         /// <param name="e"></param>
         private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
         {
           Console.WriteLine("Reading percents: " + e.ProgressPercentage + "%");
         }

      BackgroundWorker_RunWorkerCompleted 方法,输出结果。这里要注意如果 RunWorkerCompletedEventArgs 参数的 Error 属性不为空则读取其他属性会产生异常,然后如果 Cancelled 属性为 true 则读取 Result 属性也会产生异常,因此必须依次判断各属性的值

     /**//// <summary>
         /// RunWorkerCompleted event process method
         /// </summary>
         /// <param name="sender"></param>
         /// <param name="e"></param>
         private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
         {
           if (e.Error != null)
           {
             Console.WriteLine("Error occurs: " + e.Error.Message);
           }
           else if(e.Cancelled)
           {
             Console.WriteLine("Work cancelled");
           }
           else
           {
             Console.WriteLine("Read finished, the file length is: " + e.Result);
           }
         }

      向外提供一个入口方法

      /**//// <summary>
         /// Test portal
         /// </summary>
         public void ReadAsync()
         {
           if (File.Exists(m_FilePath))
           {
             Console.WriteLine("Begin read");
             m_bw.RunWorkerAsync();
           }
           else
           {
             throw new FileNotFoundException("Can't find file: " + m_FilePath);
           }
         }

      最后是 Main 方法,比昨天有了小小的改变,用 Console.ReadLine 代替了 Thread.Sleep 来达到阻止主线程退出的目的

      class BackgroundWorkerTest
       {
         static void Main(string[] args)
         {
           Console.Write("Input file path: ");
           string filePath = Console.ReadLine();
      BackgroundWorkerDemo demo = new BackgroundWorkerDemo(filePath);
           demo.ReadAsync();
      // Thread waiting
           Console.ReadLine();
         }
       }

      其他

      回顾一下我用委托实现 IAsyncResult 模式的 Demo ,与用 BackgroundWorker 实现的基于事件的异步模式很相似吧。而且应用程序可以通过委托的 BeginInvoke 和 EndInvoke 方法来异步执行现有的同步方法而不需要作额外的修改,BackgroundWorker 也差不多是一样。我把这两者看成实现对应异步操作模式的范本,在性能要求不是很高的一些异步操作场合,用好委托和 BackgroundWorker 就可以简单有效的完成开发了

    C#异步编程1

    。NET Framework 为异步操作提供了两种设计模式:使用 IAsyncResult 对象的异步操作与使用事件的异步操作。先来学习前者

      概述

      IAsyncResult 异步设计模式通过名为 BeginOperationName 和 EndOperationName 的两个方法来实现原同步方法的异步调用,如 FileStream 类提供了 BeginRead 和 EndRead 方法来从文件异步读取字节,它们是 Read 方法的异步版本

      Begin 方法包含同步方法签名中的任何参数,此外还包含另外两个参数:一个AsyncCallback 委托和一个用户定义的状态对象。委托用来调用回调方法,状态对象是用来向回调方法传递状态信息。该方法返回一个实现 IAsyncResult 接口的对象

      End 方法用于结束异步操作并返回结果,因此包含同步方法签名中的 ref 和 out 参数,返回值类型也与同步方法相同。该方法还包括一个 IAsyncResult 参数,用于获取异步操作是否完成的信息,当然在使用时就必须传入对应的 Begin 方法返回的对象实例

      开始异步操作后如果要阻止应用程序,可以直接调用 End 方法,这会阻止应用程序直到异步操作完成后再继续执行。也可以使用 IAsyncResult 的 AsyncWaitHandle 属性,调用其中的WaitOne等方法来阻塞线程。这两种方法的区别不大,只是前者必须一直等待而后者可以设置等待超时

      如果不阻止应用程序,则可以通过轮循 IAsyncResult 的 IsCompleted 状态来判断操作是否完成,或使用 AsyncCallback 委托来结束异步操作。AsyncCallback 委托包含一个 IAsyncResult 的签名,回调方法内部再调用 End 方法来获取操作执行结果

      尝试

      先来熟悉一下今天的主角,IAsyncResult 接口

    public interface IAsyncResult
    {
    object AsyncState { get; }
    WaitHandle AsyncWaitHandle { get; }
    bool CompletedSynchronously { get; }
    bool IsCompleted { get; }
    }
                

      我用一个 AsyncDemo 类作为异步方法的提供者,后面的程序都会调用它。内部很简单,构造函数接收一个字符串作为 name ,Run 方法输出 "My name is " + name ,而异步方法直接用委托的 BeginInvoke 和 EndInvoke 方法实现

    public class AsyncDemo
    {
    // Use in asynchronous methods
    private delegate string runDelegate();
    private string m_Name;
    private runDelegate m_Delegate;
    public AsyncDemo(string name)
    {
    m_Name = name;
    m_Delegate = new runDelegate(Run);
    }
    /**////
    /// Synchronous method
    ///
    ///
    public string Run()
    {
    return "My name is " + m_Name;
    }
    /**////
    /// Asynchronous begin method
    ///
    ///
    ///
    ///
    public IAsyncResult BeginRun(AsyncCallback callBack, Object stateObject)
    {
    try
    {
    return m_Delegate.BeginInvoke(callBack, stateObject);
    }
    catch(Exception e)
    {
    // Hide inside method invoking stack
    throw e;
    }
    }
    /**////
    /// Asynchronous end method
    ///
    ///
    ///
    public string EndRun(IAsyncResult ar)
    {
    if (ar == null)
    throw new NullReferenceException("Arggument ar can't be null");
    try
    {
    return m_Delegate.EndInvoke(ar);
    }
    catch (Exception e)
    {
    // Hide inside method invoking stack
    throw e;
    }
    }
                } 

      首先是 Begin 之后直接调用 End 方法,当然中间也可以做其他的操作

    class AsyncTest
    {
    static void Main(string[] args)
    {
    AsyncDemo demo = new AsyncDemo("jiangnii");
    // Execute begin method
    IAsyncResult ar = demo.BeginRun(null, null);
    // You can do other things here
    // Use end method to block thread until the operation is complete
    string demoName = demo.EndRun(ar);
    Console.WriteLine(demoName);
    }
                } 

      也可以用 IAsyncResult 的 AsyncWaitHandle 属性,我在这里设置为1秒超时

    class AsyncTest
    {
    static void Main(string[] args)
    {
    AsyncDemo demo = new AsyncDemo("jiangnii");
    // Execute begin method
    IAsyncResult ar = demo.BeginRun(null, null);
    // You can do other things here
    // Use AsyncWaitHandle.WaitOne method to block thread for 1 second at most
    ar.AsyncWaitHandle.WaitOne(1000, false);
    if (ar.IsCompleted)
    {
    // Still need use end method to get result, 
    // but this time it will return immediately
    string demoName = demo.EndRun(ar);
    Console.WriteLine(demoName);
    }
    else
    {
    Console.WriteLine("Sorry, can't get demoName, the time is over");
    }
    }
                } 

      不中断的轮循,每次循环输出一个 "."

    class AsyncTest
    {
    static void Main(string[] args)
    {
    AsyncDemo demo = new AsyncDemo("jiangnii");
    // Execute begin method
    IAsyncResult ar = demo.BeginRun(null, null);
    Console.Write("Waiting..");
    while (!ar.IsCompleted)
    {
    Console.Write(".");
    // You can do other things here
    }
    Console.WriteLine();
    // Still need use end method to get result, 
    // but this time it will return immediately
    string demoName = demo.EndRun(ar);
    Console.WriteLine(demoName);
    }
                } 

      最后是使用回调方法并加上状态对象,状态对象被作为 IAsyncResult 参数的 AsyncState 属性被传给回调方法。回调方法执行前不能让主线程退出,我这里只是简单的让其休眠了1秒。另一个与之前不同的地方是 AsyncDemo 对象被定义成了类的静态字段,以便回调方法使用

     class AsyncTest
    {
    static AsyncDemo demo = new AsyncDemo("jiangnii");
    static void Main(string[] args)
    {
    // State object
    bool state = false;
    // Execute begin method
    IAsyncResult ar = demo.BeginRun(new AsyncCallback(outPut), state);
    // You can do other thins here
    // Wait until callback finished
    System.Threading.Thread.Sleep(1000);
    }
    // Callback method
    static void outPut(IAsyncResult ar)
    {
    bool state = (bool)ar.AsyncState;
    string demoName = demo.EndRun(ar);
    if (state)
    {
    Console.WriteLine(demoName);
    }
    else
    {
    Console.WriteLine(demoName + ", isn't it?");
    }
    }
                } 

      其他

      对于一个已经实现了 BeginOperationName 和 EndOperationName 方法的对象,我们可以直接用上述方式调用,但对于只有同步方法的对象,我们要对其进行异步调用也不需要增加对应的异步方法,而只需定义一个委托并使用其 BeginInvoke 和 EndInvoke 方法就可以了 

    21 September

    窗口弹出形式

    项目里多处用到弹出窗口,现在小小地总结了一下三种open的形式,顺便把写title的方法也写下来:

    window.open('http://singlepine.cnblogs.com','title','height=100,width=200,top=0,left=0,toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,status=no')

    window.showModalDialog('http://singlepine.cnblogs.com','title','scrollbars=yes;resizable=no;help=no;status=no;dialogTop=25;
    dialogLeft=0;dialogHeight=400px');

    window.showModelessDialog('http://singlepine.cnblogs.com','title','scrollbars=yes;resizable=no;help=no;status=no;dialogTop=25;
    dialogLeft=0;dialogHeight=400px');

    'http://singlepine.cnblogs.com' 弹出窗口的目标文件名
    'title' 弹出窗口的标题
    height 弹出窗口的高度
    width 弹出窗口的宽度
    top 弹出窗口与屏幕上方的距离
    left 弹出窗口与屏幕左侧的距离
    toolbar=no 是否显示工具栏,如果显示则为yes
    menubar=no 是否显示菜单栏,如果显示则为yes
    scrollbars=no 是否显示滚动条,如果显示则为yes
    location=no 是否显示地址栏,如果显示则为yes
    status=no 是否显示状态栏,如果显示则为yes
    resizable=no 是否允许改变窗口大小,如果允许则为yes

    <script language="JavaScript1.2">
    <!--hide
    var isnMonth = new
    Array("1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月");
    var isnDay = new
    Array("星期日","星期一","星期二","星期三","星期四","星期五","星期六","星期日");
    today = new Date () ;
    Year=today.getYear();
    Date=today.getDate();
    if (document.all)
    document.title="今天是: "+Year+"年"+isnMonth[today.getMonth()]+Date+"日"+isnDay[today.getDay()]
    //--hide-->
    </script>

     

     

     
    13 September

    C#的四个基本技巧

    1.如果可能尽量使用接口来编程

      .NET框架包括类和接口,在编写程序的时候,你可能知道正在用.NET的哪个类。然而,在这种情况下如果你用.NET支持的接口而不是它的类来编程时,代码会变得更加稳定、可用性会更高。请分析下面的代码:

    private void LoadList (object [] items, ListBox l)
    {
     for (int i = 0; i < items.Length;i++)
      l.Items.Add (items[i].ToString ());
    }

      这个函数从一个可为任何对象的数组中加载ListBox,这段代码被限定为只能使用数组。假想过些时候你发现那些对象存在数据库中,或别的集合中。那么你需要修改程序来使用不同的集合类型。如果你用ICollection接口来写那段程序,你就不用修改那段程序了,对于任何实现ICollection接口的类型它都能很好的工作:

    private void LoadList (ICollection items,ListBox l)
    {
      foreach (object o in items)
      l.Items.Add (o.ToString ());
    }

      ICollection被数组和所有System.Collection中的集合实现。此外,多维数组也支持ICollection接口。如果那还不够的话,数据库.NET类同样支持ICollection接口。用接口写的这个函数不用需改就可以才许多中情况下使用。

      2. 使用属性代替原始数据

      因为属性已经成为语言本身的元素,所以声明数据元素时它的作用域等级没有必要大于private。因为代码本身会把属性看成数据元素,你并没有失去使用简单数据类型的便利性 。相反它会使你的代码更加灵活功能更加强大。属性使你的数据元素封装性更好。属性可以让你使用lazy evaluation来返回数据。lazy evaluation的意思是当用户请求时才计算它的值,而不是一直保留着它。

      最后,属性可以是virtual也可以是abstract。你也可以在接口中定义属性。

      这里还有维护方面的因素应当注意:尽管操作两者的方法是一样的,但是你把一个数据元素变成属性,那么原先客户端的程序便不能访问服务端的新版本程序了。实际上对于在Web service中你想实现序列化的值你可以把它们变成属性来使用:

    private int TheMonth = 0;

    [XmlAttribute ("Month")]
    public int Month
    {
     get {
      return TheMonth;
     }
     set {
      TheMonth = value;
     }
    }

      简单通过属性就可以使你的所有数据元素私有化。

      3. 在Producer/Consumer 的Idiom中使用Delegate

      当你生成一个实现producer idiom类的时候,使用deletate来通知consumer。这种方法相对于用接口更加灵活。Delegate是多点传送的,所以不用加额外的代码你就何以支持多用户。相对于用接口这样做可使类之间的耦合性降低。

      下面的类处理键盘输入并把它传给所有的registered listeners:

    public class KeyboardProcessor
    {
    private OnGetLine theFunc = null;

    public OnGetLine OnGetLineCallback {
     get {
      return theFunc;
     }
     set {
      theFunc = value;
     }
    }

    public void Run (){
    // Read input.
    // If there is any listeners, publish:
    string s;
    do {
     s = Console.ReadLine ();
     if (s.Length == 0)
      break;
     if (theFunc != null){
      System.Delegate [] funcs =theFunc.GetInvocationList();
      foreach (OnGetLine f in funcs) {
       try {
        f (s);
       } catch (Exception e) {
        Console.WriteLine
        ("Caught Exception: {0}", e.Message);
       }
      }
     }
    } while (true);
    }

      任何数目的listeners都可注册到producer,它们所要做的只是提供一个特定的函数:deletate。

      4. 注意初始化顺序

      C#中对于一些变量声明加入了initializer的概念。它们在构造函数之前被执行,实际上变量在基类的构造函数执行前之前被初始化。

      所以,在初始化变量的时候不要用基类中的数据,因为它们还没有被构造。

    10 September

    C# 2.0泛型类的创建和使用

    "一次编码,多次使用",这就是引入泛型的根源。在以前的C++中称为模板,C#泛型通过算法和数据结构支持独立编码。例如,泛型列表意味着,你不必再重 写一个强类型集合。在本文中,作者将向你展示定义和使用泛型是多么容易的事情-请注意,长期以来泛型一直被认为是最高级和最困难的术语。

      一、 简介

       泛型现在在任何一种语言中都被认为是一个高级的强有力的术语。当我在C++中第一次接触模板时,我对之有些疑惑。之后,我读了Bjarne Stroustrop的《The Design and Evolution of C++》,才发现模板的使用就象C中的宏和用之来取代的简单串替换模板一样容易。其实,模板和泛型是相同的东西-尽管它们的实现稍微不同。

       C#泛型支持在使用点处才定义算法及其数据类型。在C#的一些早期版本中,我们可以证明没有泛型也可以工作,因为每种类型都是派生于一个公共基类型- object。这意味着程序员可以基于object类型定义一个栈类并且把一切东西放到该栈上(因为一切都派生于object)。然而,一个object 栈意味着,Customer对象,Integer对象以及假想的对象都能被放置到同一个栈的实例上。结果是,开发者要子类化数据类型来把数据类型绑定到他 们要与之交互的东西上去。例如,在编写定制的商业对象时,我们就建议定义派生于System.Collections.CollectionBase的强 类型集合。原因很简单:基于object定义一切被认为是弱类型定义。

      业界的高手们在数十年前就确信强类型优于弱类型,所以.NET最终支持强类型,这看上去是很自然的事情。强类型算法当然建议类型化参数-这正是我们在泛型中所用的东西。

      十几年来,我们一直在使用字母T作为类型化参数的名字。这样,在任何泛型类使用者所提供的数据类型的地方,你都能够找到T。使用泛型的关键仅仅是提供这个T。定义泛型的关键在于实现一个方法或类,并且用特定数据类型来替换掉T。

       C#中的泛型支持另外一些提炼。例如,一个方法或类可以有多个参数化的类型并且C#泛型还支持WHERE约束-它用来具体要求类型化参数的类型。例如, 如果一个泛型类型必须实现接口IDisposable,那么C#泛型是支持实现这一限制的。在文章的最后我们还要看一下约束问题。
    闲话少说,让我们言归正传。

      二、 使用泛型集合

       有些人问我"面向对象编程(OOP)的承诺在哪里?",我的回答是应该从两个方面来看OOP:你所使用的OOP和你创建的OOP。如果我们简单地看一下 如果没有如例如Microsoft的.NET,Borland的VCL,以及所有的第三方组件这样的OO框架,那么很多高级的应用程序几乎就无法创建。所 以,我们可以说OOP已经实现了它的承诺。不错,生产好的OOP代码是困难的并且可能是极具挫败性的;但是记住,你不必须一定要通过OOP来实现你的目 标。因此,下面首先让我们看一下泛型的使用。

      当你用Visual Studio或C# Express等快速开发工具创建工程时,你会看到对于System.Collections.Generic命名空间的参考引用。在这个命名空间中,存 在若干泛型数据结构-它们都支持类型化的集合,散列,队列,栈,字典以及链表等。为了使用这些强有力的数据结构,你所要做的仅是提供数据类型。

      列表1显示出我们定义一个强类型集合的Customer对象是很容易的。

      列表1 这个控制台应用程序包含一个Customer类和一个基于List<T>的强类型集合Customers。

    using System;
    using System.Collections.Generic;
    using System.Text;
    namespace Generics{
     class Program{
      static void Main(string[] args){
       List<Customer> customers = new List<Customer>();
       customers.Add(new Customer("Motown-Jobs"));
       customers.Add(new Customer("Fatman's"));
       foreach (Customer c in customers)
       Console.WriteLine(c.CustomerName);
       Console.ReadLine();
      }
     }
     public class Customer{
      private string customerName = "";
      public string CustomerName{
       get { return customerName; }
       set { customerName = value; }
      }
      public Customer(string customerName){
       this.customerName = customerName;
      }
     }
    }

      注意,我们有一个强类型集合-List<Customer>-对这个集合类本身来说不需要写一句代码。如果我们想要扩展列表customer,我们可以通过从List<Customer>继承而派生一个新类。

    三、 实现一个泛型类

      一种合理的实现某种新功能的方法是在原有的事物上进一步构建。我们已经了解强类型集合,并知道一 种不错的用来构建泛型类的技术是使用一个特定类并删除数据类型。也就是说,让我们定义一个强类型集合CustomerList,并且来看一下它要把什么东 西转化成一个泛型类。

      列表2定义了一个类CustomerList。后面的部分把CustomerList转化成List<T>。

      列表2定义类CustomerList:

    using System;
    using System.Collections;
    using System.Text;
    namespace Generics{
     public class CustomerList : CollectionBase{
      public CustomerList() { }
      public Customer this[int index]{
       get { return (Customer)List[index]; }
       set { List[index] = value; }
      }
      public int Add(Customer value)
      {return List.Add(value);}
     }
    }

      四、 定义类头

      如果我们定义一个泛型类,我们需要把类头转化成一个泛型类。所有我们需要做的是命名参数并且把类名改成某种泛型。List<T>只有一个参数T,并且因为我们在以一种向后兼容的方式工作,所以我们知道类名是List。列表3显示出列表2中类的新类头。

      列表3 一个泛型类头显示出参数化的参数T。

    using System;
    using System.Collections;
    using System.Text;
    namespace Generics{
    public class List<T> : CollectionBase {}

      五、 实现泛型字段

      如果我们需要把任何字段转换成泛型字段,我们将只需简单地把它们的类型改变成T(或该字段所描述的任何参数)。泛型List不需要任何字段,但是假定存在一个私有的整型字段叫foo-我们将把它泛型化。我们将如下重新定义它:

    private T foo;

      当参数T被填充到类中时,List T也将因foo被填充。

      六、 定义泛型方法

      接下来,我们为所需要的参数化类型定义其它一些特性。这包括属性,方法,和事件。在我们的实例中,在Customer出现的每一处,我们都用参数T替换它。完成后的泛型列表类显示于列表4中。

      列表4 一个基于System.Collections.CollectionBase的轻量级的参数化泛型列表类。

    using System;
    using System.Collections;
    using System.Text;
    namespace Generics{
     public class List<T> : CollectionBase {
      public List(){ }
      public T this[int index] {
       get { return (T)List[index]; }
       set { List[index] = value; }
      }
      public int Add(T value) {
       return List.Add(value);
      }
     }
    }

      为了测试该定制列表,注释掉使用System.Collections.Generic命名空间一句并且把列表4中的List<T>使用在列表1的代码中;它将以同样的方式工作。

      全面地修改.NET的List<T>是不必要的而且它也包含远比我们的示例多得多的特性;但是列表4显示出这种机制对于定义定制泛型类是多么容易。
    七、 增加类型约束

      最后要讨论的是约束问题。约束被应用于类或其它特性上并且使用下面的语法:

    Where T : constraint_type

      例如,任何我们想要通过using语句所使用的,如一个SqlDataReader,必须实现Idisposable接口。这是因为如下方式使用的using语句:

    using(Form f = new Form()){...}

      就象一个 try..finally块一样工作-总是清除新创建的资源。其工作原理很简单,只需要CLR针对在该using语句中创建的对象发出一个到 IDisposable.Dispose的调用即可。例如,在上面这一句中,一个新的表单被创建,并且在using语句退出之前即调用 Form.Dispose。
    要对一个泛型类施加以确保该类实现了接口IDisposable,我们将添加先行词where T:Idisposable。在列表4中的泛型列表上施加约束后,我们将重新修改列表4如下面的列表5所示。

      列表5 增加一个约束到泛型类以确保我们的List<T>中的所有的值T实现接口Idisposable。

    using System;
    using System.Collections;
    using System.Text;
    namespace Generics{
     public class List<T> : CollectionBase where t : IDisposable{
      public List(){ }
      public T this[int index]{
       get { return (T)List[index]; }
       set { List[index] = value; }
      }
      public int Add(T value){return List.Add(value);}
     }
    }

      先行词where的值可以是类,接口,结构,实现一个无参的公共构造器或有一个特定的基类的类。详见有关帮助文档。

      八、 总结

       泛型的设计是用来减少你重复实现的代码的次数-只需改变数据类型即可。因为抽象数据结构,如队列,栈和列表皆是典型的数据结构,所以存在针对这些东西的 泛型类完全可以理解。你可以从.NET中派生大量的值-通过使用现有的泛型类,如在System.Collections.Generic命名空间中的那 些。

      可以肯定,在一段相当长的时间里,泛型将会象模式和重构等革新一样对开发带来越来越大的价值,而且新的数据结构能被转换成可重用的如泛型等的代码元素。
     

    采用C#泛型实现数据库之间的切换

     泛型变量太牛B了,哈哈

        数据库不是很复杂,因此采用了两个类:

    (1) DataProvider 泛型类

    public class DataProvider<ConnType, CmdType>
        where ConnType : IDbConnection, 
    new()
        where CmdType : IDbCommand,
    new ()
    {
        
    }

        提供数据库表的Insert,Update,Select,Delete操作。
        因为 IDbCommand能够由 IDbConnection 获取,其实只需要有一个泛型参数ConnType 的,不过这样以来,具体的代码改动比较大,偶就采用了两个泛型参数。

    (2)ConnectionPool 泛型类

    public class ConnectionPool<T>
            where T : IDbConnection,
    new ()
    {
        
    }

        最开始没在MySql 5.0 中找到对数据库连接池的支持,于是自己写了一个简单的数据库连接池。

        下面,就是采用 C# 的NB语法――using:

    (1)如果使用 MySql 数据库:

    using DataProvider = DataProvider<MySql.Data.MySqlClient.MySqlConnection, MySql.Data.MySqlClient.MySqlCommand>;

    (2)如果使用 SQLServer 数据库:

    using DataProvider = DataProvider<System.Data.SqlClient.SqlConnection, System.Data.SqlClient.SqlCommand>;

        这样一来,其它地方的代码一句都不用动了。

        懒惰是程序员的美德。我的一台电脑上装的是 SQLServer 2000数据库,一台电脑上装的是 MySql数据库,我经常一会在这台电脑上干活,一会在另外一台电脑上干活,两个电脑上的代码应该一致啊。因此,采用预编译指令:

    #if MSSQLSERVER
    using System.Data.SqlClient;
    using DataProvider = DataProvider<System.Data.SqlClient.SqlConnection, System.Data.SqlClient.SqlCommand>;
    #elif MYSQL
    using MySql.Data;
    using DataProvider = DataProvider<MySql.Data.MySqlClient.MySqlConnection, MySql.Data.MySqlClient.MySqlCommand>;
    #endif

        这样在编译时指定条件编译符号 " MYSQL " 得到的就是MySql版本的代码,指定条件编译符号" MSSQLSERVER ",得到的就是 SQLServer 版本的代码。

        其它:

        (1)理论上来说,采用反射得到的解决方案更完美,不过那样工期会更长,没必要啦啦啦啦啦啦。。。。。。。。。。。。。

        (2)数据库设计,偶采用的是免费软件 Toad Data Modeler 免费版,里面提供了数据库切换功能,切换过去,稍微改动改动。

        (3)这次开发工具采用的全是开源软件或者免费软件,都是超级好用的东东,感觉开发速度并不比庞大的收费软件慢。用到的工具如下:
        IDE:VS 2005 Express (C++版,C#版,Web开发版),.Net的aspnet命令行编译工具
        版本管理:SVN,小乌龟SVN Client
        Shell:Windows Power Shell(这玩意既然出来了,就要充分利用)
        UML建模:StarUML(功能强大的开源UML,比偶以前用过的JUDE,ArgoUML强大很多,支持C#)
        数据库建模:Toad Data Modeler 免费版
        数据库管理工具:EMS SQL Manager 2005 lite for MySQL
     
    06 September

    USING NOCOUNT

    SET   NOCOUNT  
      使返回的结果中不包含有关受   Transact-SQL   语句影响的行数的信息。  
       
      语法  
      SET   NOCOUNT   {   ON   |   OFF   }  
       
      注释  
      当   SET   NOCOUNT   为   ON   时,不返回计数(表示受   Transact-SQL   语句影响的行数)。当   SET   NOCOUNT   为   OFF   时,返回计数。  
       
      即使当   SET   NOCOUNT   为   ON   时,也更新   @@ROWCOUNT   函数。  
       
      当   SET   NOCOUNT   为   ON   时,将不给客户端发送存储过程中的每个语句的   DONE_IN_PROC   信息。当使用   Microsoft&reg;   SQL   Server&#8482;   提供的实用工具执行查询时,在   Transact-SQL   语句(如   SELECT、INSERT、UPDATE   和   DELETE)结束时将不会在查询结果中显示"nn   rows   affected"。  
       
      如果存储过程中包含的一些语句并不返回许多实际的数据,则该设置由于大量减少了网络流量,因此可显著提高性能。  
       
      SET   NOCOUNT   设置是在执行或运行时设置,而不是在分析时设置。  
       
      权限  
      SET   NOCOUNT   权限默认授予所有用户。  
       
      示例  
      下例在   osql   实用工具或   SQL   Server   查询分析器中执行时,可防止显示有关受影响的行数的信息。  
       
      USE   pubs  
      GO  
      --   Display   the   count   message.  
      SELECT   au_lname    
      FROM   authors  
      GO  
      USE   pubs  
      GO  
      --   SET   NOCOUNT   to   ON   and   no   longer   display   the   count   message.  
      SET   NOCOUNT   ON  
      GO  
      SELECT   au_lname    
      FROM   authors  
      GO  
      --   Reset   SET   NOCOUNT   to   OFF.  
      SET   NOCOUNT   OFF  
      GO