官术网_书友最值得收藏!

第二篇 C#語法及面向對象基礎

2章 C#語法基礎

每種計算機語言都有其基本的語法規則,用來管理內存和處理數據。任何計算機程序都必須遵循其編寫語言的語法規則。C#語言繼承了許多C++的語法特性,簡化了C++語法的諸多復雜性。很多關鍵字和C++中的是一樣的。這對使用過C、C++、Java等語言的讀者來說是個好消息,但是C#語法并不是C++語法的簡單復制,如它對數據類型的管理更為嚴格。

本章主要內容:

● 變量的定義和使用

● 數據類型及其轉換方法

● 常用表達式

● C#基本流程控制語句

● 命名空間介紹

2.1 C#的基本語法

C#在保持C系列語言的優美表示形式的同時,實現了應用程序的快速開發。各語句之間采用分號相隔,各代碼塊使用“{”作為開始,使用“}”作為結束。代碼注釋分兩種情況,一種是行注釋,使用“//”可以實現;另一種是多行注釋,使用“/*”作為注釋行的開始,使用“*/”作為注釋行的結束。不管是行注釋,還是多行注釋,“C#”編譯器都會忽略被注釋的行。

在C#中沒有單獨的頭文件,聲明方法和類型不要求按照特定的順序。在C#中引入了namespace(叫做命名空間,詳細說明請參考后面章節中的介紹)代替C++中的包含文件,可以使用using namespace包含命名空間,這樣程序中便可以使用空間中定義的類、方法等。

下面是對第1章中的程序實例稍加改進之后的代碼。

        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Data;
        using System.Drawing;
        using System.Text;
        using System.Windows.Forms;
        namespace FormsTest
        {
            public partial class Form1 : Form
            {
              public Form1()
              {
                  InitializeComponent();                              //初始化控件
              }
              private void button1_Click(object sender, EventArgs e)  // button1的
                                                                        //單擊事件
              {
                  //MessageBox.Show("你單擊了左邊的button1按鈕。");
              }
              private void button2_Click(object sender, EventArgs e)  // button2的
                                                                        //單擊事件
              {
                  MessageBox.Show("你單擊了右邊的button2按鈕。");       //彈出消息框
              }
          }
        }

可以看到語句MessageBox.Show("你單擊了左邊的button1按鈕。")被“//”注釋了。但是這并不會產生語法錯誤,因為C#中的方法可以沒有命令語句,該段代碼展示了C#的大部分基本語法。為方便開發和維護,代碼的編寫應該遵循縮進原則,具體格式如上面代碼所示,當然VS2008工具具備自動縮進功能。

2.2 變量

變量,對應著內存中的一塊存儲位置。簡單地說,變量是計算機存儲于內存中其值可改變的量。這里的“可改變”是指程序運行期間的可改變。

2.2.1 變量的聲明

在C#中變量也是對象,變量也有其方法和屬性等特征。在程序中需要首先聲明變量,然后才可以使用。可以按照如下方式命名變量。

        <變量類型> <變量名>;

其中,<變量類型>有int、float、string、byte、long等。變量名的命名需要遵循以下原則。

● 第一個字符只能是字母(包括漢字)或下畫線。其余字符可以為字母(包括漢字)、數字和下畫線的組合。

● 變量名不能和C#的關鍵字或庫函數名相同。如Main、public、int、class等。

下面的變量命名是合法的。

        int x;
        string p2p;
        bool _start;
        float p_p;

而下面的代碼則不合法。

        int Main;           //與庫函數同名
        string 2pp;         //以數字開始
        bool ! start;        //有標點符號
        float _;            //只有下畫線

另外,聲明變量還需要有一定的意義,最好名字能表示其用途。如需要定義一個bool型變量,表示是否啟動了線程,則_start是一個很好的變量名。

2.2.2 變量的賦值

在C#中對變量的賦值是非常容易的事。只需要在要賦值的變量名后邊加上“=”和所賦的值即可。如下所示代碼是給本節中定義的幾個變量賦值。

        X=20;
        p2p=”Hello, C#! ”;
        _start=true;
        p_p=3.14159;

當然,也可以在定義變量時賦值。同時也可以利用已賦值變量給變量賦值,具體方法如下所示。

        int x=20;
        string p2p=”Hello, C#! ”;
        bool _start=true;
        float p_p=3.14159;
        float r_r=p_p                   //將已賦值變量p_p的值賦給r_r
        string p3p= p_p.ToString()     //將已賦值變量的p_p.ToString()方法返回值賦給p3p
        double Sum = 0, a, b, c;

上面代碼的最后一行同時聲明了4個double型變量。其中在聲明的同時Sum被賦初始值0,這在C#中是允許的。

程序中有時需要將變量的值輸出。其方法與在C++中略有不同,下面是有關變量定義、賦值和輸出的程序VarTest,代碼如下所示。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace VarTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  string str;
                  double Sum = 0, a, b, c;
                  a = 20;
                  b = 30;
                  c = 40;
                  Sum = a + b + c;                //求和
                  str="a+b+c=";                     //設置字符串值
                  Console.Write(str);               //輸出字符串
                  Console.WriteLine("{0}", Sum);    //輸出a、b、c的和
                  Console.ReadKey();
              }
            }
        }

運行程序,得到如圖2-1所示的結果。

圖2-1 程序運行結果

其中定義了4個double型變量Sum、a、b、c,并分別賦值,求和后再賦值給Sum變量。注意,str為字符串變量,只能存儲字符串。所有字符串都要用雙引號括起來。上述“a+b+c”實際上是一串字符,后面將詳細介紹。

        Console.Write(str);
        Console.WriteLine("{0}", Sum);

這兩行代碼實現了寫入標準輸出流,簡單地說就是在控制臺窗口中顯示。Console.WriteLine用到了兩個參數的方法,第一個參數“{0}”表示輸出其后邊變量列表的第0個變量值。之所以叫變量列表,是因為該方法還可以同時輸出多個變量。如將上面兩行代碼用下面一行代碼代替,形式如下所示。

        Console.WriteLine("{0}+{1}+{2}={3}", a, b, c, Sum);

其輸出結果如圖2-2所示。

圖2-2 程序運行結果

2.2.3 簡單數據類型

細心的讀者可能會發現,上面所介紹的變量有多種類型。其實在計算機中,為了便于管理和有效利用內存,把變量分為了多種類型,如整數型、浮點數類型、字符類型等。在C#中,所有數據類型都是從System.Object繼承而來的,也即是任何數據類型的值都可以賦值給Object類型變量。在C#中可以將數據類型分為值類型和引用類型。其中值類型又可以分為簡單類型和struct類型。前面介紹的數據類型如int、double等數據類型就叫簡單數據類型。本節只介紹簡單類型,表2-1列出了C#中的簡單數據類型。

表2-1 C#中的簡單數據類型

從表2-1中可以看出,簡單數據類型又可以分為整數類型、布爾類型、浮點類型、字符類型、字符串類型和decimal類型。

表2-1中前8種數據類型就是就是整數類型,即byte、sbyte、ushort、short、uint、int、ulong、long。

表2-1中的bool類型用來表示邏輯“真”和“假”,這在程序的邏輯判斷中經常使用。在程序中用“true”和“false”表示邏輯“真”和“假”。

2.2.4 使用簡單數據類型

程序BoolTest演示了布爾類型的用法,代碼如下所示(鑒于篇幅原因,只給出了部分代碼)。

        static void Main(string[] args)
        {
            bool flagT, flagF;
            flagT = true;                                 //初始化變量為true
            flagF = false;                                //初始化變量為false
            Console.WriteLine("The value of flagT is {0}", flagT);   //輸出變量值
            Console.WriteLine("The value of flagF is {0}", flagF);   //輸出變量值
            Console.ReadKey();
        }

運行程序,效果如圖2-3所示。

圖2-3 程序運行結果

代碼第一行是變量聲明,聲明了兩個bool型變量。第二行、第三行分別對其賦值。隨后的代碼輸出兩個變量的值,bool型變量只能取“true”或“false”,若將整型數據或字符串等數據賦給它則會出錯。

說明:定義了一種數據類型變量后,該變量的表示范圍就已經確定了。如果出現表示范圍以外的數據則會產生所謂的“溢出”,這樣會導致程序發生錯誤。實際編程中應該謹慎選取數據類型。

表2-1中的double和float稱為浮點類型。浮點類型和整數類型不同,它可以表示小數位。其中double型數據可以有15到16位小數,而float類型數據有7位小數。它們都有一個缺點,就是無法精確表示很多數(一般情況的精度要求足夠了)。decimal可以彌補這個缺點,它可以表示到小數點后28位以后。decimal(十進制)類型數據常用于對數據精度要求很高的場合,如銀行、復雜的科學計算等。

程序DecimalTest對比了decimal和double類型的精度,代碼如下所示。

        static void Main(string[] args)
        {
            //定義變量
            decimal x;
            double y;
            x = 3.14159265358979323m;
            x = x / 3;
            y = (double)x;
            //輸出變量
            Console.WriteLine("x={0}", x);
            Console.WriteLine("y={0}", y);
            Console.ReadKey();
        }

運行程序,輸出結果如圖2-4所示。

圖2-4 程序運行結果

程序第一、二行分別定義了decimal型變量x和double型變量y。第三行給x賦值圓周率小數點后18位的數值。x對自身除以3之后,第5行代碼將decimal強制轉換后賦值給double型變量y,最后輸出兩變量的值。輸出結果中,decimal類型變量x的精度達到27位。而double類型變量y的精度只達到了12位,便將后面的數據舍棄了。

表2-1中的char表示字符類型,用于存儲字符,長度為一個字節;string是字符串類型,用于存儲字符串。字符串的長度可以為0,也可以無限長。

下面程序代碼演示了它們的部分屬性,程序名為StrChTest。

        static void Main(string[] args)
        {
        char c;
            string str, s;
            c=' I' ;
            str="you and ";
          s = " are friends";
          str = str + c;                         //字符串相加
          Console.WriteLine("str={0}", str);      //輸出字符串
          str = str + s;                          //字符串相加
          Console.WriteLine("str={0}", str);      //輸出字符串
          str = Convert.ToString(c);              //將字符轉換為字符串
          Console.WriteLine("str={0}", str);      //輸出字符串
          Console.ReadKey();
      }

運行程序,結果如圖2-5所示。

圖2-5 程序運行結果

第一、二行分別定義了一個字符變量c和兩個字符串變量str和s。字符需用單引號括起來,而字符串用雙引號括起來。對它們賦值完畢后是如下語句。

        str = str + c;

該語句將字符和字符串相加后的結果賦值給原來字符串。這在C#這是允許的。從圖2-5中可以看出輸出第一行str的值為“you and I”。接下來是將兩字符串相加,代碼如下所示。

        str = str + s;

從輸出結果知道,后一字符串“are friends”接到了前一個字符串的末尾。如圖2-5中第二行輸出。

最后是類型轉換,代碼如下所示。

        str = Convert.ToString(c);

字符不可以隱式轉換為字符串,所以有必要使用類型轉換語句。如圖2-5中第三行輸出。變量str只有一個字符,但是它仍然被當做字符串。最后,字符和字符串類型還支持包括漢字在內的所有Unicode編碼字符。

2.2.5 使用struct創建結構類型

前面講到,struct(結構)類型是一種復雜數據類型,是因為它可以包含簡單數據類型。不但如此,它還可以包含包括自身在內的其他結構類型,以及方法、屬性、索引器等。struct類型也是一種值類型,它通過值而不是通過引用方式傳遞參數。這是因為struct是值類型,而類是引用類型。這也是它與類最重要的區別。struct類型常用來封裝小型變量組。如矩形的坐標、大地方位信息等。

2.2.6 結構類型例程

下面的代碼顯示了一個結構類型的一般定義方法。

        <保護級別> struct <結構名>
        {
          <保護級別> <類型> <結構成員名稱>
        }

此處<保護級別>有private和public。結構可以實現接口,卻無法繼承另外一個結構。正因為如此,結構成員不能被聲明為protected。

下面是一段聲明結構類型的示例代碼。

        public struct point
        {
            public int x;
            public int y;
        }

上面代碼定義了一個點的坐標,其中結構名為point。成員變量是兩個整形變量x和y。其<保護級別>設置為public,讀者只需知道這是為了讓其他代碼可以訪問。

定義了結構類型就可以在程序中使用該類型了,這些類型就像簡單數據類型如int、double等,可以用來聲明具體的變量。可以用如下方法聲明。

        <結構名> <結構變量名>;

如“point P; ”,聲明了一個point類型的結構變量P。該結構變量即包含了一個整型變量x和一個整型變量y。可以通過P.x和P.y訪問。在C#中用“.”表示對象的層次關系。

同樣結構類型還可以包含結構類型,代碼如下所示。

        public struct Rectangle
        {
            //包含point結構
            public point topleft;
            public point topright;
            public point buttomleft;
            public point buttomright;
            //包含變量
            public bool used;
        }

該結構定義包含了4個point結構類型,和一個bool類型。其結構變量的聲明和上述聲明方法一致。如“Rectangle rec; ”,同樣可以使用如rec.tpoleft.x、rec.tpoleft.y訪問所包含結構中的對象。

需要說明的是,在結構類型中不能包含其自身。下面的代碼將會導致循環,從而產生錯誤。

        public struct Rectangle
        {
      public Rectangle rec;
      public bool used;
  }

下面的代碼演示了使用struct定義結構類型的具體方法。

    //程序StructTest
    using System;
    using System.Collections.Generic;
    using System.Text;
    namespace StructTest
    {
        //定義point結構
        public struct point
        {
          public int x;
          public int y;
    }
        //定義Rectangle結構
        public struct Rectangle
    {
          //包含point結構
          public point topleft;
          public point topright;
          public point buttomleft;
          public point buttomright;
          public bool used;
        }
        class Program
        {
          static void Main(string[] args)
          {
              //初始化Rectangle結構變量
              Rectangle rec, rc;
              rec.topleft.x= 200;
              rec.topleft.y = 200;
              rec.topright.x = 300;
              rec.topright.y = 200;
              rec.buttomleft.x = 200;
              rec.buttomleft.y = 300;
              rec.buttomright.x = 300;
              rec.buttomright.y = 300;
              rec.used = true;
              rc = rec;
              //輸出結構實例中的變量值
              Console.WriteLine("rc.topleft.x={0}", rc.topleft.x);
              Console.WriteLine("rc.topleft.y={0}", rc.topleft.y);
              Console.WriteLine("rc.topright.x={0}", rc.topright.x);
              Console.WriteLine("rc.topright.y={0}", rc.topright.y);
              Console.WriteLine("rc.topright.x={0}", rc.buttomleft.x);
                  Console.WriteLine("rc.topright.y={0}", rc.buttomleft.y);
                  Console.WriteLine("rc.buttomright.x={0}", rc.buttomright.x);
                  Console.WriteLine("rc.buttomright.y={0}", rc.buttomright.y);
                  Console.WriteLine("rc.used={0}", rc.used);
                  Console.ReadKey();
              }
            }
        }

程序首先定義了兩個struct結構類型,其中一個是point類型,另一個是Rectangle類型。Point中定義了兩個整型成員變量x和y。Rectangle中定義了矩形的4個point結構類型的頂點,以及一個bool類型。完成類型定義之后就可以在程序中使用所定義的類型了。代碼首先定義兩個Rectangle結構類型rec和rc,接下來對其中的rec中的結構成員進行初始化,代碼如下。

        rc = rec;

表示將結構變量rec的值賦給rc。因為它們具有相同的類型結構,在內存中相當于將rec的成員值復制給rc的對應成員。從圖2-6的顯示結構也可以看出,它們的成員值完全相同。

圖2-6 程序運行結果

復雜變量的類型中,還有數組類型,詳細介紹請參考第5章的數組處理。接下來將介紹有關類型轉換問題。

2.2.7 定義結構的構造函數

構造函數是一種特殊的成員函數,主要用來初始化對象,在對象生成時該函數就自動執行。構造函數要求與類型名稱相同且沒有返回值。

下面定義了一個結構的構造函數實例,程序名為ConstructTest。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace ConstructTest
        {
            class Program
            {
              public struct point
              {
                  public int x;
                  public int y;
                  public point(int x, int y)       //定義構造函數
                  {
                    this.x = x;                   //對成員賦值
                    this.y = y;
                  }
              }
              static void Main(string[] args)
              {
                  point P1 = new point();          //使用默認值初始化
                  point P2 = new point(32, 45);       //使用自定義構造函數初始化
                  Console.WriteLine("P1.x={0}", P1.x);
                  Console.WriteLine("P1.y={0}", P1.y);
                  Console.WriteLine("P2.x={0}", P2.x);
                  Console.WriteLine("P2.y={0}", P2.y);
                  Console.ReadKey();
              }
          }
        }

程序中的如下代碼行是用來定義結構point,包含兩個整型成員變量和一個構造函數,該函數帶有兩個參數,用來初始化前面定義的兩個成員變量。

        public struct point
        {
            public int x;
            public int y;
            public point(int x, int y)      //定義構造函數
            {
              this.x = x;                  //對成員賦值
              this.y = y;
            }
        }

其中如下的代碼,

        this.x = x;
        this.y = y;

使用了this指針,此處讀者只需知道它表示當前類。上述兩行語句中的this.x和this.y表示了獲取當前結構中的成員變量x和y。并將參數列表中的x和y值分別賦給它們。

接下來在Main函數中的開始兩行語句,代碼如下。

        point P1 = new point();        //使用默認值初始化
        point P2 = new point(32, 45);  //使用自定義構造函數初始化

用來實例化類,得到兩個相應對象P1和P2。此處的“new”就是用來進行類的實例化。其中第一個對象采用了默認值初始化,第二個對象采用了自定義的構造函數初始化。運行程序,得到如圖2-7所示運行結果。

圖2-7 程序運行結果

從結果可以看出,第一個對象P1的成員全部為0。第二給對象P2的成員變量值正是所預設的值,分別是32和45。

2.2.8 類型轉換

程序中經常會遇到需要將某個類變量的值轉換為另外一個類型。如需要顯示某個整型變量,則需要將其轉換為字符串。類型轉換分為以下幾種情況。

● 根據轉換方式的不同,可以分為隱式(Implicit)轉換和顯式(Explicit)轉換。

● 根據源類型和目標類型之間的關系進行劃分,又可分為變換(Conversion)、投射(Cast)和裝箱/拆箱(Boxing/Unboxing)。

2.2.9 隱式轉換

隱式轉換,通常將低類型向高類型轉換。如將int型變量轉換為float型變量。這類轉換可以保證轉換前后的值不發生變化。一般情況下,系統可以自動實現隱式轉換。如下面的程序IntToDoubleTest所示。

        static void Main(string[] args)
        {
            double k;
            int i = 5;
            k = i;
            Console.WriteLine("k={0}", k);  //實現隱式轉換,并輸出轉換結果
            Console.ReadKey();
        }

上述代碼中定義了一個double類型(高類型)變量k和一個int類型(低類型)變量i。如下代碼行將賦值給k,由于類型不同,系統自動實現了隱式轉換。

        k = i;

代碼運行結果如圖2-8所示。表2-2列出了可以實現隱式轉換的類型。

圖2-8 程序運行結果

表2-2 可以實現隱式轉換的類型列表

從表2-2中可以看出,不存在向char類型的隱式轉換。如整型不可以自動轉換為char類型。但是在顯示轉換中是存在的,這將在后面章節中作介紹。另外,也不支持浮點類型向decimal類型轉換。將上述程序改為如下形式。

        static void Main(string[] args)
        {
            double k=5;
            int i;
            i = k;
            Console.WriteLine("i={0}", i);  //不能實現隱式轉換
            Console.ReadKey();
        }

運行程序,錯誤提示如圖2-9所示。

圖2-9 類型轉換錯誤提示

錯誤提示需要進行強制轉換或顯式轉換。將上述代碼修改如下。

        static void Main(string[] args)
        {
            double k=5;
            int i;
            i =(int)k;          //強制轉換類型(可能丟失數據)
            Console.WriteLine("i={0}", i);
            Console.ReadKey();
        }

則可編譯通過,該方法強迫數據從一種類型轉換到另一種類型,這即是顯示轉換。

2.2.10 顯式轉換

兩種類型之間沒有任何關系的數據類型,是不能相互轉換的。表2-3列出了可以進行顯式轉換數值類型。

表2-3 可以實現顯式轉換的數值類型

上述轉換也可使用System.Convert方法進行顯式轉換,方法如下。

        static void Main(string[] args)
        {
            double k=5;
            int i;
            i =Convert. ToInt32(k); //使用函數進行類型轉換
            Console.WriteLine("i={0}", i);
            Console.ReadKey();
        }

Convert.ToInt32(k)的作用是將變量k的值轉換為等效的32位有符號整數。System.Convert類為類型轉換提供了一整套方法,前提是這些轉換能夠被編譯器所支持。它提供一種與語言無關的方法進行類型轉換,并且可用于公共語言運行庫支持的所有語言。Convert用于執行收縮轉換和不相關數據類型之間的轉換。例如,支持從DateTime類型轉換為string類型、從string類型轉換為數字類型,以及從string類型轉換為bool類型等。

顯式轉換需要明確制定需要轉換的類型。轉換前后可能有誤差,所以有的地方又叫強制轉換。表2-4列出了有關Convert的一些類型轉換方法說明。

表2-4 Convert的一些轉換方法

由于顯式轉換是從高類型向低類型轉換,轉換前后將不可避免存在誤差,甚至出現不能成功轉換(編譯器會提示)。將上述代碼修改如下。

        static void Main(string[] args)
        {
            double k = -3.1415926535;
            uint ui;
            ui = Convert.ToUInt32(k);   //轉換為Uint32類型
            Console.WriteLine("ui={0}", ui);
        }

系統將出現如圖2-10所示OverflowException異常。然而,將上述代碼改為如下形式。

圖2-10 類型轉換異常

        double k = 3.1415926535;
        uint ui;
        ui = Convert.ToUInt32(k);
        Console.WriteLine("ui={0}", ui);

則系統的編譯通過,同時得到如圖2-11所示的運行結果。

圖2.11 程序運行結果

轉換的結果是double變量k的所有小數位被四舍五入掉了。上述兩段代碼說明,雖然有的類型之間可以實現轉換,卻是需要一定的條件的。如其中第一段發生異常的代碼,就是因為Convert.ToUInt32(k)方法只能將變量k轉換為無符號整型;而k是有符號的(負數),所以發生了異常。

2.2.11 根據參與類型轉換的劃分

根據參與類型轉換的源類型和目標類型的關系不同進行劃分,又可以將類型轉換分為變換、投射和裝箱/拆箱。

變換是最常見的一種類型轉換,通常用于簡單的值類型之間的轉換。前面介紹的隱式轉換和顯式轉換都是屬于這種類型的轉換。

當源類型和目標類型之間具有直接或間接的繼承關系時,進行的類型轉換屬于投射。它分為兩種情況。第一種是將子孫類型的對象轉換為祖先類型,叫做向上投射(Upcast);第二種是將祖先類型的對象轉換為子孫類型,叫做向下投射(Downcast)。

其中,向上投射可以使用隱式轉換,而向下投射須使用顯式轉換。因為具有繼承關系的兩個類中,子類將擁有父類中的所有成員。因此當子類對象向父類轉換時,可以確保不會產生成員丟失的情況。而當父類對象向子類轉換時,不一定能確保父類對象擁有子類中定義的成員。

進行類型轉換時,在源類型和目標類型中,如果一個是值類型而另一個是引用類型時,則會發生裝箱/拆箱轉換。其中,從值類型到引用類型的轉換稱為裝箱,而從引用類型到值類型轉換則稱為拆箱。執行裝箱操作時,程序會在堆中新創建一個對象(視為“箱子”)。然后將值類型的值復制到這個新創建的對象中。拆箱時,則是將對象中的值復制到棧上。

2.3 常量

有時候需要在程序中將某個量固定下來,使之保持不變。程序中的圓周率π,它是不需要改變的,同時也不希望程序運行期間被誤改。這是就用到了常量這個概念。常量在程序運行期間,其值是不能改變的。在C#語言中,常量可以分為兩種類型。一種是靜態常量(Compile-time constant),另一種是動態常量(Runtime constant)。其中,前者使用“const”關鍵字來定義,后者使用“readonly”關鍵字來定義。

2.3.1 靜態常量

可以使用如下方式定義靜態常量。

        <訪問權限 > const <變量類型>

其中“訪問權限”有public、private、protected等(具體含有將在第3章有關章節介紹)。使用“const”修飾的“變量類型”通常是值類型。如果是引用類型,則只能在初始化時為其賦予null。下面是一個名為ConstTest的用于定義常量的示例。

        public const double pi = 3.14159265;

上面代碼定義了一個公有整型常量pi。應該注意的是,靜態常量在定義時需要對其初始化。

        public const double piL = 3.14;            //定義靜態常量
        static void Main(string[] args)
        {
            const double piH = 3.14159265;          //定義靜態常量
            piH = 3.15;
            Console.WriteLine("piL={0}", piL);      //輸出靜態常量
            Console.WriteLine("piH={0}", piH);      //輸出靜態常量
            Console.ReadKey();
        }

程序中定義了一個const成員變量piL和一個const變量piH。運行結果如圖2-12所示。

圖2-12 程序運行結果

將上述代碼改為如下所示的代碼。

        const double piH = 3.14159265;
        piH = 3.15;
        Console.WriteLine("piL={0}", piL);
        Console.WriteLine("piH={0}", piH);
        Console.ReadKey();

則系統出現錯誤提示:“賦值號左邊必須是變量、屬性或索引器。”

這說明const常量在程序運行過程中,其值是不允許改變的。其次,const常量還有一個優點,就是在定義const常量時是不占用內存的。因為在編譯程序時產生的中間預言代碼中,將所有用到這種常量的地方,都會使用其實際值給予替換。

2.3.2 動態常量

動態常量(也叫只讀常量)使用“readonly”關鍵字來定義。相對于靜態常量,動態常量要靈活得多,它的定義方式如下。

        <訪問權限 > readonly <變量類型>

“訪問權限”同樣有public、private、和protected。只讀常量的“變量類型”可以是值類型,也可以是引用類型。

下面是兩個只讀常量的聲明示例。

        public readonly System.Text.StringBuilder Sb = new StringBuilder();
        private readonly double pi = 3.14159265;

之所以稱為動態變量,是因為系統要為“readonly”所定義的動態常量分配內存空間。它和類中其他成員一樣擁有獨立的內存空間,這和靜態常量是不同的。此外,動態常量除了可以在定義的時候設定常量值外,也可以在類的構造函數中設定。由于動態常量相當于類中的成員,因此使用靜態常量所受到的類型限制,在動態常量的情況下就不存在了。也可以使用“readonly”去定義任何類型的常量。

2.3.3 使用動態常量

下面代碼程序名為ReadOnlyTest,演示動態常量的一些設置。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace ReadOnlyTest
        {
            //定義結構point
            public struct point
            {
              public int x;
              public int y;
              //定義結構point的構造函數
              public point(int x, int y)
              {
                  this.x = x;
                  this.y = y;
              }
            }
            class Program
            {
              //定義動態常量
              private static readonly double pi = 3.14159265;
              //使用構造函數定義動態常量
              public static readonly point P=new point (12,26);
              static void Main(string[] args)
              {
                  //輸出對比結果
                  Console.WriteLine("pi={0}", pi);
                  Console.WriteLine("P.x={0}", P.x);
                  Console.WriteLine("P.y={0}", P.y);
                  Console.ReadKey();
              }
            }
        }

程序中首先定義了帶有自定義構造函數的結構point(結構的構造函數的定義方法,請參考前面的章節)。

        public struct point
        {
            public int x;
            public int y;
            public point(int x, int y)
          {
              this.x = x;
              this.y = y;
          }
      }

該構造函數帶有兩個整型變量x和y。用于將參數列表中傳下來的參數,分別賦值給結構的成員變量x和y。后面實例化該對象時便是調用了該函數。代碼如下所示。

        private static readonly double pi = 3.14159265;
        public static readonly point P=new point (12,26);

這兩行中用到了“static”關鍵字,此關鍵字是一個靜態方法,用來定義的對象可以直接調用。需要注意的是,動態常量不能也沒有必要使用“static”關鍵字修飾。

其中,第一行代碼定義了一個靜態雙精度浮點型常量pi。第二行使用自定義的構造函數,實例化一個point類型的結構。整個程序的最后幾行是常量值的輸出。運行程序,結果如圖2-13所示。

圖2-13 程序運行結果

上述程序實現了在結構的構造函數種進行初始化,達到了代碼重用的效果。

表2-5總結了靜態常量和動態常量的異同點。

表2-5 靜態常量和動態常量對比

2.4 表達式

C#語言中的表達式與數學中的表達式基本類似,都有嚴格的格式。在C#中表達式用于幫助數據處理,如加、減、取反和邏輯判斷等。任何程序中基本都離不開表達式的使用。通常給一個變量賦值用的符號“=”是一個賦值運算符,它與變量和值構成一個表達式。表達式描述了對數據進行處理的順序和操作。由運算符和運算量組成,其中運算量(又叫操作數)通常指的是變量。下面內容主要介紹運算符。

2.4.1 數學運算符

和數學中的運算符對應,在C#中有加、減、乘、除和求余5種基本的數學運算符。除此之外還有兩個符號運算符,它們是正號“+”和負號“-”。表2-6列出了它們的基本用法和意義。

表2-6 數學運算符

2.4.2 普通數學運算符

下面程序MathOperatorTest演示了上述運算符的使用和意義。

        static void Main(string[] args)
        {
            double a;
            int b, c;
            a = 0;
            b = 25;
            c = 2;
            a = b + c;
            Console.WriteLine("a={0}", a);
            a = b - c;
            Console.WriteLine("a={0}", a);
            a = b * c;
            Console.WriteLine("a={0}", a);
            a =(double)b / (double)c;
            Console.WriteLine("a={0}", a);
            a = b % c;                 //求余
            Console.WriteLine("a={0}", a);
            a = +a;                   //取符號為“+”
            Console.WriteLine("a={0}", a);
            a = -a;                   //取符號為“-”
            Console.WriteLine("a={0}", a);
            Console.ReadKey();
        }

上面代碼運用了所有的數學運算符,需要說明的是下面的代碼。

        a =(double)b / (double)c;

這里用到了類型轉換,是由于C#中的整數相除,仍然是整數。也就是說,如果將上述代碼修改如下。

        a =b / c;

則a的值為12,而不是12.5,所以在進行相除時一定要注意變量類型。運行程序,得到如圖2-14所示的結果。

圖2-14 程序運行結果

可以看出,一元運算符“-”實現了變量的符號取反運算;而運算符“+”對變量的數值并沒有影響。但是,這并不表示“+”運算符沒有用處,示例代碼如下所示。

        string str1 , str2 , str3;
        str1=”abcdefg”;
        str2=”higklmn”;
        str1=str2 + str3;     //字符串相加
        Console.WriteLine("str1={0}”, str1);

上面代碼運行后將會輸出“abcdefghigklmn”。

也就是“+”運算符實現了兩個字符串的連接,而其他數學運算符則不能實現對字符串的操作。后面的章節還會介紹“+”運算符的其他應用,如運算符重載等。

2.4.3 自加和自減運算符

自加運算符用“++”表示,自減運算符用“--”表示。它們在C#中分別都有兩種使用方式,同時這兩種方式的意義也是不一樣的。表2-7列出了它們的用法和意義。

表2-7 自加和自減運算符

自加和自減運算符采用“自右至左”的結合方向,它們只能對整型變量進行運算,而不能是表達式或常數,如下面語句是錯誤的。

        6++;
      (a+b)--;

程序AutoTest演示了自加和自減運算符的使用和意義,代碼如下。

        static void Main(string[] args)
        {
            int a, b;
            a = 0;
            b = 5;
            a = b++;        //自加運算,先賦值加后
            Console.WriteLine("a={0}", a);
            Console.WriteLine("b={0}", b);
            a = b--;        //自減運算,先賦值加減
            Console.WriteLine("a={0}", a);
            Console.WriteLine("b={0}", b);
            a = ++b;        //自加運算,先加后賦值
            Console.WriteLine("a={0}", a);
            Console.WriteLine("b={0}", b);
            a = --b;        //自減運算,先減后賦值
            Console.WriteLine("a={0}", a);
            Console.WriteLine("b={0}", b);
            Console.ReadKey();
        }

程序運行結果如圖2-15所示。

圖2-15 程序運行結果

2.4.4 賦值運算符

前面已經多次使用到了“=”賦值運算符。其實,在C#中還有很多類似的運算符,正確地使用它們可以提高程序開發效率。表2-8列出了C#中的賦值運算符及其解釋。

表2-8 C#中的賦值運算符

下面程序AssignmentTest演示了上述賦值運算符的使用。

        static void Main(string[] args)
        {
            byte a , b;
            a = 9;
            b = 3;
            a += b;     //相加
            Console.WriteLine("a={0}", a);
            a -= b;     //相減
            Console.WriteLine("a={0}", a);
            a *= b;     //相乘
            Console.WriteLine("a={0}", a);
            a /= b;     //相除
            Console.WriteLine("a={0}", a);
            a <<= b;    //左移
            Console.WriteLine("a={0}", a);
            a >>= b;    //右移
            Console.WriteLine("a={0}", a);
            a &= b;     //求位與運算
            Console.WriteLine("a={0}", a);
            a ^= b;     //求位異或運算
            Console.WriteLine("a={0}", a);
            a —= b;     //求位或運算
            Console.WriteLine("a={0}", a);
            Console.ReadKey();
        }

代碼首先定義了兩個字節型(無符號8位整數)變量數a和b,其中a=9,二進制表示為a=00001001。b=3,其二進制表示為b=00000011。運行程序,結果如圖2-16所示。

圖2-16 程序運行結果

前4行分別是加、減、乘、除的結果,第5行中a=72。語法如下所示。

        a <<= b;

將a左移了3位(b=3),此時的a=9,即a=00001001。將其向左移后變成a=01001000也即a=72。在移動時將右邊空出的位填0。

同理,下面的代碼。

        a >>= b;

將a的值右移3位,等于將a的值還原。

接下來的代碼。

        a &= b;

用于將a和b按位求與運算。此時a=00001001, b=00000011。所以得到最后的a=00000001即a=1。其余兩個運算符道理相同,在此不再贅述。

2.4.5 比較運算符

比較運算符通常用于確定變量之間的關系,如大于、小于等。它是程序作出邏輯判斷的基礎。表2-9列出了C#中的一些常用的比較運算符及其解釋。

表2-9 C#中的比較運算符

              static void Main(string[] args)
              {
                  int a, b;
                  bool bt, bf;
                  bt = true;
                  bf = false;
                  a = 5;
                  b = 8;
                  if (a == b)     //判斷是否相等
                  {
                    Console.WriteLine("a等于b");

下面的程序CompareTest說明了上述比較運算符的使用和意義。

          }
          else
          {
              Console.WriteLine("a不等于b");
      }
      //輸出類型判斷結果
      Console.WriteLine(a is int);
      //輸出邏輯與運算結果
      Console.WriteLine(bt && bf);
      Console.ReadKey();
      }

程序中用到了“if else”分支語句,該語句的意義正如其英文意義。當“if”的括號中的表達式值為“true”時,程序進入該代碼塊,否則進入“else”代碼塊運行。程序運行結果如圖2-17所示。

圖2-17 程序運行結果

2.4.6 運算符的優先級

上面介紹的很多運算符都可以相互組合為表達式,如(a=b)+c*d、x%y+z等。這為復雜問題的解決提供了方便,但同時帶來了需要考慮的運算符的優先級問題。表2-10列出了C#中各種運算符的優先級順序。

表2-10 C#中的比較運算符

其中的“? :”為條件運算符,該運算符使用形式如下。

        c? e1:e2

當“c”為真時程序執行e1語句,反之則執行e2語句。該語句也是C#中唯一的三元運算符。

注意:編程應該以“可靠性高于效率”的原則進行。如將費解的地方分解處理,或者使用“()”將沒有把握的地方括起來,或者對代碼加注釋等。這樣使程序容易讓人讀懂,便于后期的檢查和維護。

2.4.7 命名空間

在.NET Framework中包含了一個由4000多個類組成的基礎類庫,這就是.NET Framewok基礎類庫。基礎類庫中的這些類被組織成為命名空間。為從字節流的輸入和輸出到數據庫操作、到文件操作、到Windows窗體控件和ASP.NET控件,所涉及的所有內容提供多種有用的功能。通常的C#應用程序都離不開.NET Framework類庫支持。

其實,在前面的每個程序中都用到了命名空間。有System.、System.IO、System.Text等。以第2.3.2節的程序為例,它們的使用方式如下。

        using System;
        using System.Collections.Generic;
        using System.Text;

包含命名空間用到了“using”關鍵字,告知系統“本程序需要用到這個命名空間,請提供該命名空間中的所有需要的服務”。這樣便可以在自己的程序(命名空間)中使用上述命名空間中的類。

當然,也可以自定義命名空間。實際上C#中的幾乎所有類都放在特定的命名空間中,包括用戶自己編寫的程序。

下面的示例程序,整個命名空間以“namespace ReadOnlyTest”開始。“ReadOnlyTest”便是自定義的程序名,也是自定義的命名空間名。整個代碼塊包含在“namespace ReadOnlyTest”所在的大括號內,其中是系統和用戶定義的類、結構及其成員,包括point結構和program類。

        namespace ReadOnlyTest
        {
            //定義結構
            public struct point
            {
              public int x;
              public int y;
              //定義結構的構造函數
              public point(int x, int y)
              {
                  this.x = x;
                  this.y = y;
              }
            }
            class Program
        {
            //定義動態常量
              private static readonly double pi = 3.14159265;
              //使用構造函數定義動態常量
              public static readonly point P=new point (12,26);
              static void Main(string[] args)
              {
                  //輸出結果
                  Console.WriteLine("pi={0}", pi);
                  Console.WriteLine("P.x={0}", P.x);
                  Console.WriteLine("P.y={0}", P.y);
                  Console.ReadKey();
              }
            }
        }

之所以在程序中引入命名空間,是由于該方式可以有效解決類的重名問題。比如在開發大型軟件時A開發組定義了classA類,然而B開發小組也定義了一個classA類。這樣在最后組織到一起進行編譯時編譯器便會提示兩個類重名。而引入命名空間之后,可以將A開發組的命名空間命名為namespaceA,將B開發組的命名空間命名為namespaceB。這樣由于命名空間的不同便可以使用相同的類名了,這在C#中是完全可行的。只是如果需要訪問classA,需要使用完全的路徑名,如namespaceA.classA、namespaceB.classA,這樣編譯器才知道到底訪問哪個空間中的類。當然也可以使用“using”關鍵字將它們包含進程序,但是仍然需要限定命名空間。

細心的讀者可能會發現有的命名空間會非常長,如System.Security.Cryptography.X509 Certificates。如果每次都要限定命名空間,將會是一件非常煩瑣的事情。讓人欣慰的是,C#提供了一種“別名”機制,可以使用該機制定義一個需要引用的命名空間別名,定義方法如下。

        using <別名> =<命名空間>

定義了別名之后就可以利用該別名訪問對于命名空間中的類了。這與訪問實際的命名空間是一樣的,代碼如下。

        using SSCX= System.Security.Cryptography.X509Certificates;
        ......
        SSCX.X509Store.Equals=true;
        ......

2.4.8 嵌套命名空間

從命名空間的引用可以看出,命名空間中可以繼續定義命名空間。這就是命名空間的嵌套,定義方式如下。

        namespace NameA
        {
            public class classA
        {
            ......
        funA()
        {
            ......
        }
        ......
        }
        namespace NameB
        {
            public class classB
            {
              ......
              funB()
              {
                  ......
              }
              ......
            }
          }
          ......
        }

上面代碼定義了一個命名空間NameA,其中又定義了一個類classA和一個命名空間NameB。其中類classA中又定義了一個方法funA()。命名空間NameB中又定義了一個方法funB()。如果該名稱空間的外部空間程序要訪問funA(),則可以通過全名NameA.classA.funA()訪問;相應的訪問funB()可以通過全名NameA.NameB.classB.funB()訪問。

上面介紹的是在一個文件中定義命名空間,C#中還有可以分開定義同一個命名空間,并不要求一定在同一個文件中。

2.5 流程控制

從功能上劃分,語句可以分為兩類。一類是用于描述計算的操作運算語句,如數學運算、賦值運算、方法調用語句等;另一類是用于控制這些語句執行順序的語句,如選擇語句、循環控制語句等。這類語句叫做流程控制語句。

可以將流程控制語句分為分支語句、循環語句和跳轉語句等幾個大類。本節主要介紹這幾類語句。

2.5.1 分支語句

程序中經常會出現很多的運算結果,這些結果又對應著多種可能的執行分支語句。分支語句便是用于選擇和執行程序中的這些分支語句。C#分支語句主要包括三元運算符、if語句和switch語句。

2.5.2 三元運算符

三元運算符在前面已經有所介紹,其格式如下。

        c? e1:e2

其分支方法是,當“c”為真時程序執行e1語句,反之則執行e2語句。下面的程序TriTest演示了三元運算符的具體應用。

        static void Main(string[] args)
        {
            const double noon=12;
            string resultStr;
            //獲取系統當前時間的小時部分
            double now = System.DateTime.Now.Hour;
            Console.WriteLine("time={0}", now);
            //判斷變量大小,用以確定是上午或下午
            resultStr=(now< noon)? "現在是上午":"現在是下午";
            Console.WriteLine("{0}", resultStr);
            Console.ReadKey();
        }

程序首先定義了三個變量,其中兩個double型變量用于存儲時間,另一個string型變量用于顯示結果。

        double now = System.DateTime.Now.Hour;

用來獲取系統當前時間的小時部分,并把結果賦值給變量now。

        resultStr=(now<time)? "現在是上午":"現在是下午";

該語句是關鍵,判斷現在時間和設定的時間的大小,用以確定是上午還是下午。結果返回給變量resultStr,最后是顯示結果。運行程序得到如圖2-18所示結果。

圖2-18 程序運行結果

三元運算符,也可以實現嵌套使用。嵌套格式如下所示。

        expr0? expr1:expr2;

其中表達式expr1和expr2都可以嵌套。如下面的代碼。

        expr0? (expr00? expr11: expr22):expr2;

為了便于區分使用了括號,但實際中可以不使用。

上面程序中只能判斷是上午還是下午,如果還需要判斷是下午還是晚上,則可以將上述代碼改為如下形式。

        static void Main(string[] args)
        {
            const double noon=12;
            const double eveningTime = 19;
          string resultStr;
          //獲取系統當前時間的小時部分
          double now = System.DateTime.Now.Hour;
          Console.WriteLine("time={0}", now);
          //判斷變量大小,用以確定是上午或下午或晚上
          resultStr=(now<noon)? "現在是上午":(now<eveningTime)? "現在是下午":"現在是晚上";
          Console.WriteLine("{0}", resultStr);
          Console.ReadKey();
      }

將系統設置為12點以后,19點以前的時間。然后運行程序,結果如圖2-19所示。

圖2-19 程序運行結果

2.5.3 if語句

與三元運算符具有同樣功能的還有if語句。所不同的是if語句沒有返回值。if語句有多種形式,前面用到的有if…else是其中比較簡單的一種。除此之外還有if…else if…else,或者僅僅一個單獨的if語句,但是沒有單獨的else語句。

對if…else語句,其表達形式如下。

        if(expr)
        {
        ......
        }
        else
        {
        ......
        }

expr可以是任何表達式或方法的返回結果,結果的類型必須是bool型。這和三元運算符是一致的。當返回為“true”時,執行if代碼塊中的語句。否則,執行else代碼塊中的語句。

if…else屬于二選一執行。也有多選一執行的用法,這就是if…else if…else。其表達的形式如下。

        if(expr1)
        {
        ......
        }
        else if(expr2)
        {
        ......
        }
        else
        {
        }

expr1和expr2的意義和上面是一樣的。當expr1返回“true”時,執行if代碼塊;否則判斷expr2的返回,如果是“true”則執行expr2代碼塊中語句;否則執行else代碼塊中的語句。當然這種格式并不限于三選一,還可以有更多的選擇分支。只需要多加“else if”語句便可。

2.5.4 使用if語句

下面的程序IfElseTest演示了如何使用if語句。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace IfElseTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  Console.WriteLine("請選擇你目前的開發工作:");
                  Console.WriteLine("1.Windows桌面應用程序");
                  Console.WriteLine("2.Web應用程序");
                  Console.WriteLine("3.Web服務");
                  Console.Write("請輸入你的選擇:");
                  //讀取用戶的輸入字符
                  string choice=Console.ReadLine();
                  //判斷用戶的輸入
                  if (choice=="1")
                  {
                      Console.WriteLine("你目前的開發工作是:1.Windows桌面應用程序");
                  }
                  else if (choice == "2")
                  {
                      Console.WriteLine("你目前的開發工作是:2.Web應用程序");
                  }
                  else if (choice == "3")
                  {
                      Console.WriteLine("你目前的開發工作是:3.Web服務");
                  }
                  else
                  {
                      Console.WriteLine("對不起你選擇錯誤,下次請輸入1-3之間的整數");
                  }
                  Console.ReadKey();
              }
            }
        }

整個程序主要是完成對用戶輸入的判斷。其中下面的代碼在控制臺上列出用戶選項,并提示用戶輸入。

        Console.WriteLine("請選擇你目前的開發工作:");
        Console.WriteLine("1.Windows桌面應用程序");
        Console.WriteLine("2.Web應用程序");
        Console.WriteLine("3.Web服務");
        Console.Write("請輸入你的選擇:");

下面定義了一個字符串變量,用于接收用戶輸入的字符。

        string choice=Console.ReadLine();

接下來判斷用戶的輸入是1~3中的哪一個數字字符,并輸出對應項。如果輸入不在1~3之間,則輸出錯誤提示。運行代碼,輸入一個數字,如“1”,運行結果如圖2-20所示。

圖2-20 程序運行結果

2.5.5 程序流程

如圖2-21所示為上節示例整個程序的流程圖。

圖2-21 程序流程圖

相比而言,三元運算符的開發效率更高,if語句更為直觀,其代碼易于理解和維護。但是從開發效率的角度來看,if語句稍遜一籌。

2.5.6 switch語句

if語句在解決程序分支問題上提供了一個很好的解決方案。但是,如果選項很多,則需要很多的else if語句,這樣會造成代碼的不易理解和維護。在C#中還提供了一種專門解決多分支問題的語句,這就是switch語句。

“switch”在英語中的意思是“開關”,所以又叫開關語句。該語句可以一次將測試變量與多個可能值進行比較,而不是一次只能測試一個可能值。該語句的基本結構如下所示。

        switch(expr)
        {
            case value1:
              statement1;
              break;
            case value2:
              statement2;
              break;
              ......
            case valueN:
              statementN;
              break;
            default:
              defaultStatement;
              break;
        }

expr可以是任何整數類型或字符串,或者返回整數類型、字符串類型的表達式、方法等。value1~valueN是expr的可能取值。如果expr的值為其中之一則執行其對應的statement。如果所有value都不滿足,則執行default語句對應的defaultStatement語句。break語句表示跳出整個switch語句。

2.5.7 使用switch語句

將第2.5.4節中的例子改為使用switch語句控制分支,程序名為SwitchTest,代碼如下所示。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace SwitchTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  Console.WriteLine("請選擇你目前的開發工作:");
                  Console.WriteLine("1.Windows桌面應用程序");
                  Console.WriteLine("2.Web應用程序");
                  Console.WriteLine("3.Web服務");
                  Console.Write("請輸入你的選擇:");
                  //讀取用戶的輸入字符
                  string choice=Console.ReadLine();
                  //判斷用戶的輸入
                  switch(choice)
                  {
                      case "1":
                        Console.WriteLine("你目前的開發工作是:1.Windows桌面應用程序");
                        break;  //跳出循環
                      case "2":
                        Console.WriteLine("你目前的開發工作是:2.Web應用程序");
                        break;  //跳出循環
                      case "3":
                        Console.WriteLine("你目前的開發工作是:3.Web服務");
                        break;  //跳出循環
                      default:
                        Console.WriteLine("對不起你選擇錯誤,下次請輸入1-3之間的整數");
                        break;  //跳出循環
                  }
                  Console.ReadKey();
              }
            }
        }

程序使用switch語句將if語句部分替換了。需要注意的是每個case部分的可能變量值類型應使用雙引號括起來。因為choice是一個字符串變量。運行程序并輸入一個數字,如“2”,運行結果如圖2-22所示。

圖2-22 程序運行結果

2.5.8 goto語句

和C、C++一樣,C#也支持goto語句。該語句是一種用于流程無條件轉移的語句。使用該語句的前提是需要在程序中加入標簽。下面是goto語句的使用格式。

        ......
            goto <LabelName>;
        ......
        <LabelName>:
        ......

其中<LabelName>是標簽名。程序執行到goto語句處便會忽略goto和<LabelName>之間的語句,而轉向<LabelName>后面執行。

前面的程序SwitchTest中有一個需要改進的地方,就是當用戶輸入的不是1~3之間的數字時,程序便自動退出了。使用goto語句可以做以下改進,改進后的程序名為GotoTest,代碼如下所示。

        static void Main(string[] args)
        {
            Console.WriteLine("請選擇你目前的開發工作:");
            Console.WriteLine("1.Windows桌面應用程序");
            Console.WriteLine("2.Web應用程序");
            Console.WriteLine("3.Web服務");
            //設置標簽
        Label:
            Console.Write("請輸入你的選擇:");
            //讀取用戶的輸入字符
            string choice=Console.ReadLine();
            //判斷用戶的輸入
            switch(choice)
            {
              case "1":
                  Console.WriteLine("你目前的開發工作是:1.Windows桌面應用程序");
                  break;    //跳出循環
              case "2":
                  Console.WriteLine("你目前的開發工作是:2.Web應用程序");
                  break;    //跳出循環
              case "3":
                  Console.WriteLine("你目前的開發工作是:3.Web服務");
                  break;    //跳出循環
              default:
                  Console.WriteLine("對不起,你選擇錯誤,下次請輸入1-3之間的整數");
                  goto Label; //轉向Label處執行
                  break;    //跳出循環
            }
            Console.ReadKey();
        }

在程序中設置了標簽和goto語句,當用戶輸入非法字符時,程序將執行default語句。輸出錯誤信息提示后,便遇到goto語句轉向Label后邊的語句,提示用戶重新輸入。

運行程序,先輸入一個字符,如“y”,程序提示錯誤信息;再輸入一個數字,如“3”,運行結果如圖2-23所示。

圖2-23 程序運行結果

說明:雖然goto語句能夠使程序員很方便地控制程序的轉向,但是過多地使用goto語句會造成程序結構的混亂。這會讓人難以讀懂代碼,容易導致后期的軟件維護相當困難,所以建議C#語言的初學者不使用goto語句。

2.5.9 循環語句

循環語句用于解決多次重復性的計算問題,如窮舉問題和迭代問題。該語句充分發揮了計算機的快速計算能力。

在C#中循環語句有do-while循環、while循環、for循環和foreach循環。下面將分別作詳細介紹。

2.5.10 do-while語句

do-while是兩個關鍵字的組合循環,其結構如下。

        do
        {
            statement;
        }while(expr);

其中do代碼塊中的statement是要循環處理的語句。這些語句按照順序執行。當執行完畢程序跳出代碼塊執行while語句。該語句首先判斷expr返回值是true還是false,如果是true,則繼續執行do代碼塊中的代碼。一般情況下statement都和expr有一定的關聯。如圖2-24所示為do-while語句執行的流程圖。

圖2-24 do-while語句執行流程

2.5.11 使用do-while語句

下面的程序DoWhileTest中用到了do-while語句,該代碼是對前面例程的改進。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace DoWhileTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  bool flag;
                  Console.WriteLine("請選擇你目前的開發工作:");
                  Console.WriteLine("1.Windows桌面應用程序");
                  Console.WriteLine("2.Web應用程序");
                  Console.WriteLine("3.Web服務");
                  do
                  {
                      flag = false;
                      Console.Write("請輸入你的選擇:");
                      //讀取用戶的輸入字符
                      string choice = Console.ReadLine();
                      //判斷用戶的輸入
                      switch (choice)
                      {
                          case "1":
                            Console.WriteLine("你目前的開發工作是:1.Windows桌面應用程序");
                            break;
                          case "2":
                            Console.WriteLine("你目前的開發工作是:2.Web應用程序");
                            break;
                          case "3":
                            Console.WriteLine("你目前的開發工作是:3.Web服務");
                            break;
                          default:
                            Console.WriteLine("對不起,你選擇錯誤,下次請輸入1-3之間的整數");
                            flag = true;
                            break;
                      }
                  }while (flag);
                  Console.ReadKey();
              }
            }
        }

上面的程序使用do-while語句替換了goto語句。實現了使用goto語句同樣的功能。其中,

        bool flag;

定義了一個布爾變量,用于測試循環是否結束。do代碼塊中的如下語句,

        flag = false;

將flag設置為“false”。這樣當代碼沒有執行到default代碼塊時,程序跳出switch語句便執行while判斷,此時flag為“false”則跳出do-while循環。如果程序執行到default代碼塊,程序將會執行到下面的語句。

        flag = true;

該語句將flag的值改變為“true”。這樣在做while判斷時就會繼續返回do代碼塊繼續執行下一輪循環。

運行程序并向控制臺輸入一個字符,如“a”,程序便會提示錯誤信息;再輸入一個數字字符,如“1”,運行結果如圖2-25所示。

圖2-25 程序運行結果

2.5.12 while語句

while循環語句的結構如下。

        while(expr)
        {
            statement;
        }

無論從關鍵字看,還是從功能上看,它和do-while語句都非常相似。唯一和do-while語句不同的是do-while語句先執行“statement”語句,然后判斷是否繼續循環;而while循環語句首先對expr的返回值進行判斷,如果為“true”才執行代碼塊中的語句statement。反之,則一次也不執行。如圖2-26所示為while語句執行的流程圖。

圖2-26 while語句執行流程圖

2.5.13 使用while語句

下面的程序WhileTest使用while語句,替換了上面例程中的do-while語句。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace WhileTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  bool flag;
                  Console.WriteLine("請選擇你目前的開發工作:");
                  Console.WriteLine("1.Windows桌面應用程序");
                  Console.WriteLine("2.Web應用程序");
                  Console.WriteLine("3.Web服務");
                  flag = true;
                  while (flag)
                  {
                      flag = false;
                      Console.Write("請輸入你的選擇:");
                      //讀取用戶的輸入字符
                      string choice = Console.ReadLine();
                      //判斷用戶的輸入
                      switch (choice)
                      {
                          case "1":
                            Console.WriteLine("你目前的開發工作是:1.Windows桌面應用程序");
                            break;
                          case "2":
                            Console.WriteLine("你目前的開發工作是:2.Web應用程序");
                            break;
                          case "3":
                            Console.WriteLine("你目前的開發工作是:3.Web服務");
                            break;
                          default:
                            Console.WriteLine("對不起你選擇錯誤,下次請輸入1-3之間的整數");
                            flag = true;
                            break;
                      }
                  }
                  Console.ReadKey();
              }
            }
        }

與程序DoWhileTest不同的是,上面代碼首先需要將flag設為“true”。這樣才能通過判斷,執行while代碼塊中的語句。代碼如下所示。

        flag = false;

將flag設置為“false”,這樣代碼沒有執行到“default”語句便會跳出循環。如果執行到“default”語句,代碼如下所示。

        flag = true;

將flag設置為“true”,從而繼續循環。

運行程序,首先輸入錯誤字符串,如“abc”,程序提示輸入錯誤信息;然后輸入一個數字字符,如“3”。得到如圖2-27所示運行結果。

圖2-27 程序運行結果

2.5.14 for語句

for循環語句與前面介紹的while等語句最大的不同就是,for語句可以指定循環的次數。并且主要是通過循環次數判斷是否要繼續循環。

下面是for語句的使用格式。

        for(<初始化計數器>; <繼續循環的條件>; <操作計數器>)
        {
            statement;
        }

<初始化計數器>在整個循環過程中只執行一次。如果<繼續循環的條件>返回的是“true”,那么執行statement語句,并且執行完之后再執行<操作計數器>。否則,如果<繼續循環的條件>返回的是false那么跳出循環。如圖2-28所示為“for”語句流程圖。

圖2-28 for語句執行流程圖

2.5.15 使用for語句

程序ForTest演示了for語句的使用,代碼如下所示。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace ForTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  int i, j, k;
                  //前5行輸出
                  for (i = 0; i <=4; i++)              //控制行
                  {
                      for (k = 0; k < i; k++)           //輸出空格
                      {
                          Console.Write(" ");
                      }
                      for (j = 1; j <= 9-2 * i; j++) //控制列
                      {
                          Console.Write("*");
                      }
                      Console.WriteLine();
                  }
                  //后5行輸出
                  for (i = 4; i >= 0; i--)             //控制行
                  {
                      for (k = 0; k < i; k++)           //輸出空格
                      {
                          Console.Write(" ");
                      }
                      for (j = 1; j <= 9-2 * i; j++) //控制列
                      {
                          Console.Write("*");
                      }
                      Console.WriteLine();
                  }
                  Console.ReadKey();
              }
            }
        }

運行程序,結果如圖2-29所示。

圖2-29 程序運行結果

運行結果得到一個“沙漏”圖形。在程序中將該圖形分為兩部分輸出,第一部分畫“沙漏”的上半部,代碼如下所示。

                  //前5行輸出
                  for (i = 0; i <=4; i++)              //控制行
                  {
                      for (k = 0; k < i; k++)           //輸出空格
                      {
                        Console.Write(" ");
                      }
                      for (j = 1; j <= 9-2 * i; j++) //控制列
                      {
                        Console.Write("*");
                      }
                      Console.WriteLine();
                  }

其中,for語句中又嵌套了兩個for循環。這在C#中是允許的。上半部總共有5行,所以控制行的變量i從0取到4。嵌套的第一個for用于控制輸出空格,第二個for語句用于控制第i行輸出“*”的個數。有興趣的讀者可以自己研究一下,此處不再做細致分析。

接下來的語句是畫“沙漏”下半部分。由于只是將上半部分倒過來,所以直接將第一部分的如下語句,

        for (i = 0; i <=4; i++)

換成如下語句即可。

        for (i = 4; i >= 0; i--)

2.5.16 foreach循環語句

foreach循環與for循環較為相似。主要用于循環訪問各種存儲數據對象的數據內容,如數組、集合等。下面的程序實例ForeachTest,演示了foreach語句循環訪問數組的方法。

        using System;
        using System.Collections.Generic;
        using System.Text;
        namespace ForeachTest
        {
            class Program
            {
              static void Main(string[] args)
              {
                  int Val=1;
                  //定義一個二維數組
                  int[, ] MyArry = new int[2,3] ;
                  //初始化二維數組
                  for (int i = 0; i < MyArry.GetLength(0); i++)
                  {
                      for (int j = 0; j < MyArry.GetLength(1); j++)
                      {
                          //給數組元素賦值
                          MyArry[i, j] = Val;
                          Val = Val + Val;
                      }
                  }
                  //訪問并輸出數組內容
                  foreach (int ind in MyArry)
                  {
                      Console.WriteLine(ind);
                  }
                  Console.ReadKey();
              }
            }
        }

程序使用到了數組,有關數組的具體介紹請參考第5章相關內容。程序首先定義一個二維數組MyArry,并對其進行初始化。初始化代碼如下。

        for (int i = 0; i < MyArry.GetLength(0); i++)
        {
            for (int j = 0; j < MyArry.GetLength(1); j++)
            {
              MyArry[i, j] = Val;
              Val = Val + Val;
            }
        }

接著使用foreach循環訪問數組中的各元素,代碼如下。

        foreach (int ind in MyArry)
        {
            Console.WriteLine(ind);
        }

其中的ind是獲取到的數組元素值。運行程序,結果如圖2-30所示。

圖2-30 程序運行結果

說明:foreach循環語句只能讀取數據,而不能寫入數據。同時不能以索引方式隨機訪問數據。但是foreach循環語句的執行效率比for語句略高。

在循環語句中有時并不需要按照特定的方式循環,而需要在某個情況發生時就跳出循環。這時就可以使用循環中斷語句了。

2.5.17 循環中斷語句

循環中斷語句包括break、continue、return。

其中,break語句前面已經用到過。循環執行過程中遇到該語句,就會跳出循環,不再執行下面的語句。

2.5.18 使用break語句

下面的程序BreakTest說明了break語句的用法。

        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
        {
            //如果i == 3
              if (i == 3)
              {
                  //輸出i的值
                  Console.WriteLine("i={0}", i);
        //跳出整個循環
                  break;
                  Console.WriteLine("Excuted! ");
              }
              if (i == 4)
              {
                  //輸出i的值
                  Console.WriteLine("i={0}", i);
              }
            }
            Console.ReadKey();
        }

輸出結果為i=3。這表明下面的語句,

        if (i == 3)
        {
            Console.WriteLine("i={0}", i);
            break;
            Console.WriteLine("Excuted! ");
        }

中break后面的如下語句,

        Console.WriteLine("Excuted! ");

和如下語句,

        if (i == 4)
        {
            Console.WriteLine("i={0}", i);
        }

并沒有被執行到。因為循環遇到break語句便跳出了循環。

2.5.19 使用continue語句

continue語句與break語句略有差異。它用于循環中的某次執行,而繼續下次循環。將上述程序改為如下形式。

        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
            {
              if (i == 3)
              {
                  Console.WriteLine("i={0}", i);
                  continue;  //繼續下一輪循環(如果滿足循環條件)
                  Console.WriteLine("Excuted! ");
              }
              if (i == 4)
              {
                  Console.WriteLine("i={0}", i);
              }
            }
            Console.ReadKey();
        }

運行程序,得到如圖2-31所示運行結果。

圖2-31 程序運行結果

結果說明下面的代碼段,

        if (i == 3)
        {
            Console.WriteLine("i={0}", i);
            continue;
            Console.WriteLine("Excuted! ");
        }

中的如下代碼,

        Console.WriteLine("i={0}", i);

和如下代碼,

        if (i == 4)
        {
            Console.WriteLine("i={0}", i);
        }

被執行了,而continue語句后的如下代碼,

        Console.WriteLine("Excuted! ");

并沒有執行。也就是說,continue語句用來實現終止本次循環,而接著執行下次的循環。

2.5.20 使用return語句

最后來說明return語句,該語句主要用于結束函數的執行,并可以將需要的參量返回。可以用于包括循環語句在內的函數中的任何地方。

將一節的代碼中的continue改為return,代碼如下。

        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
            {
              if (i == 3)
              {
                  Console.WriteLine("i={0}", i);
                  return;   //函數返回,退出當前函數
                  Console.WriteLine("Excuted! ");
              }
              if (i == 4)
              {
                  Console.WriteLine("i={0}", i);
              }
            }
            Console.ReadKey();
        }

程序將在遇到return語句后立即退出整個Main函數,而返回一個void類型。表現在控制臺是程序輸出“i=3”便立即關閉控制臺,這表明整個程序已退出。

2.6 小結

本章介紹了C#中經常用到的變量、常量、表達式,以及流程控制,其中包括它們的定義和使用,并配合了大量實例。

變量部分主要介紹了變量的聲明和賦值,以及C#中簡單數據類型。還對結構做了較詳細的介紹,同時也介紹了程序中常遇到的類型轉換。常量部分主要介紹了靜態常量和動態常量及其區別。表達式部分介紹了常用的運算符,以及其使用方法。同時對命名空間作了大致的介紹。

本章的最后介紹的是流程控制語句,這也是重點介紹之一。包括了分支語句、goto語句、循環語句及循環中斷語句。

主站蜘蛛池模板: 科技| 卢湾区| 英超| 柘城县| 丁青县| 淮北市| 习水县| 黎城县| 烟台市| 彭山县| 久治县| 通辽市| 临清市| 化隆| 本溪市| 孝昌县| 云南省| 牡丹江市| 平顶山市| 呼图壁县| 广灵县| 安平县| 巍山| 金堂县| 邹城市| 徐州市| 浦县| 巴东县| 偏关县| 涿州市| 繁峙县| 奉节县| 凤山市| 九龙县| 抚顺县| 平安县| 临高县| 石狮市| 临漳县| 舟山市| 庆阳市|