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

2.2 基本語法

語法是一種程序語言最基本的定義規范,只有按照語法給出的規則才能編寫出正確的程序。C#程序基本語法包括:數據類型的種類,變量與常量的聲明和使用以及語句的基本組成表達式和運算符。

2.2.1 數據類型

C#的數據類型包括值類型、引用類型和指針類型。指針類型是不安全類型,一般不推薦使用。

1.值類型

值類型包括簡單類型(如字符型、浮點型和整數類等)、枚舉類型和結構類型。所有的值類型都隱含地聲明了一個公共的無參數的構造函數,這個構造函數返回一個初始為零的值類型的實例。例如,對于字符型,默認值是“\x0000”;對于float,默認值是0.0F。

(1) 簡單類型:它是C#預先定義的結構類型,簡單類型用關鍵字定義,這些關鍵字僅僅是在System命名空間里預定義的結構類型的化名,比如關鍵字int對應System.Int32。簡單類型包括如表2-1所示數據類型。

表2-1 簡單數據類型

(2) 集合類型:它是C#中一種輕量級的值類型,用來表達一組特定的值的集合行為,以enum關鍵字進行聲明。

(3) 結構類型:它是用來封裝小型的相關變量組,把它們封裝成一個實體來統一使用,以struct關鍵字進行聲明。

2.引用類型

引用類型包括類類型、對象類型、字符串類型、接口類型、委托類型和數組類型等。引用類型與值類型的不同之處是值類型的變量值直接包含數據,而引用類型的變量把它們的引用存儲在對象中。類類型、對象類型和數組類型在后面的章節有詳細介紹。

(1) 字符串類型:直接從object中繼承而來的密封類。String類型的值可以寫成字符串文字的形式。例如:"123"、"hello world"是字符串類型。

(2) 接口類型:一個接口聲明一個只有抽象成員的引用類型,接口僅僅存在方法標志,但沒有執行代碼,以關鍵字interface進行聲明。

(3) 委托類型:委托引用一種靜態的方法或對象實例,引用該對象的實例方法,與C/C++中的指針類似,以關鍵字delegate進行聲明。

作者心得:

在C#中,所有數據類型都是基于基本對象Object來實現,因此它們之間在允許的范圍內是可以相互轉化的。這就是裝箱和拆箱,在2.2.4節會進行詳細介紹。

2.2.2 變量和常量

1.變量

所謂變量,就是在程序的運行過程中其值可以被改變的量,變量的類型可以是任何一種C#的數據類型。所有值類型的變量具有實際存在于內存中的值,也就是說當將一個值賦給變量時就是在執行對該值的拷貝。變量的定義格式為:

          變量數據類型 變量名(標識符);

或者

          變量數據類型 變量名(標識符) =變量值;

標識符就是變量名,變量的標志。

其中,第一個定義只是聲明了一個變量,并沒有對變量進行賦值,此時變量使用默認值。第二個聲明定義變量的同時對變量進行了初始化,變量值應該和變量數據類型一致。例如:

          int a=10; \\ 聲明了一個整數類型的變量a,并對其賦值為10
          double b,c; \\ 兩個double類型的變量
          int d=100,e=200; \\ 定義了兩個整數類型的變量,并對變量進行了賦值
          double f=a+b+c+d+e; \\ 把前面定義的變量相加,然后賦給一個double類型的變量

作者心得:

當幾個不同數值類型的變量進行運算時,低精度的變量會自動轉化為高精度的變量類型。

2.常量

所謂常量,就是在程序的運行過程中其值不能被改變的量。常量的類型也可以是任何一種C#的數據類型。常量的定義格式為:

          const 常量數據類型 常量名(標識符)=常量值;

其中,const關鍵字表示聲明一個常量,“常量名”就是標識符,用于唯一的標識該常量。常量名要有代表意義,不能過于簡練或者復雜。

“常量值”的類型要和常量數據類型一致,如果定義的是字符串型,“常量值”就應該是字符串類型,否則會發生錯誤。例如:

          const double PI=3.1415926; // 定義了一個double類型的常量
          const string VERSION = "Visual Studio 2008"; //定義了一個字符串型的常量

作者心得:

常量一旦定義,用戶在后面的代碼中如果試圖改變常量的值,編譯器會發現這個錯誤導致代碼無法編譯通過。

2.2.3 表達式和運算符

1.表達式

表達式是可以運算的代碼片段,表達式可以包括運算符、方法調用等,表達式是程序語句的基本組成部分,例如:

          int num = 5; //定義一個整型變量num,并對其賦值
          string str = “你好,世界!”; //定義一個字符串變量,并對其賦值

2.運算符

運算符是數據運算的術語和符號,它接受一個或多個稱為操作數的表達式作為輸入并返回值。C#中的運算符非常多,從操作數上劃分運算符大致分為以下3類。

● 一元運算符:處理一個操作數,只有幾個一元運算符。

● 二元運算符:處理兩個操作數,大多數運算符都是二元運算符。

● 三元運算符:處理三個操作數,只有一個三元運算符。

從功能上劃分,運算符主要分為:算術運算符,賦值運算符,關系運算符,條件運算符,位運算符和邏輯運算符。

例如:

          i ++; //一元運算,變量i自動加1
          num = 2 + 3; //二元運算,變量num等于2加3的和
          result = a > b ? 100 : -10//三元運算,條件運算符,根據條件的真假來決定運算的正確性

表達式中的運算符按照運算符優先級的特定順序計算,表2-2按優先順序列出常用運算符的優先級別,在表2-2中,上面的比下面的具有較高的優先級別。

表2-2 常用運算符

例如:

    int num = 4 + 5 * 2 + 6 / 2 //數學運算,先算乘除后算加減,結果是17

作者心得:

僅僅依靠優先級來安排數據的運算順序是可靠的,大部分情況下考慮使用()來進行強制優先級,凡是用()括起來的比其他運算符都有高的優先級。

范例2.2

Program.cs

在該程序中定義兩個雙精度變量,通過控制臺分別輸入他們的值,然后分別對這兩個變量做一系列數學運算。

代碼路徑:ShiLi2-2\Program.cs

    1  double firstNumber,secondNumber; //定義兩個雙精度變量
    2  Console.WriteLine("請輸入一個數字:");
    3  //將用戶輸入的第一個數字轉換成double類型,并存儲在firstNumber中
    4  firstNumber=Convert.ToDouble(Console.ReadLine());
    5  Console.WriteLine("請輸入第二個數字");
    6  //將用戶輸入的第二個數字轉換成double類型,并存儲在secondNumber中
    7  secondNumber=Convert.ToDouble(Console.ReadLine());
    8  Console.WriteLine("{0}+{1}={2}",firstNumber,secondNumber,firstNumber+
    9  secondNumber); //求和
    10 Console.WriteLine("{0}-{1}={2}", firstNumber, secondNumber, firstNumber -
    11 secondNumber); //求差
    12 Console.WriteLine("{0}*{1}={2}", firstNumber, secondNumber, firstNumber *
    13 secondNumber); //求積
    14 Console.WriteLine("{0}/{1}={2}", firstNumber, secondNumber, firstNumber /
    15 secondNumber); //求商
    16 Console.WriteLine("{0}%{1}={2}", firstNumber, secondNumber, firstNumber %
    17 secondNumber); //求余
    18 Console.ReadKey();

運行以上代碼輸出結果是:

請輸入一個數字:

10

請輸入第二個數字

6

10+6=16

10-6=4

10*6=60

10/6=1.66666666666667

10%6=4

2.2.4 裝箱和拆箱

裝箱和取消裝箱使值類型能夠被視為對象。對值類型裝箱將把該值類型打包到Object引用類型的一個實例中。這使得值類型可以存儲于垃圾回收堆中。取消裝箱將從對象中提取值類型,取消裝箱又經常被稱作“拆箱”。例如:

          int i = 123; //定義一個值類型變量
          object o=(object)i;  // 裝箱
          o = 123; //對裝箱后的對象操作
          i=(int)o;  //取消裝箱

從裝箱和取消裝箱的定義可以看出,這兩種行為主要用于進行值類型和引用類型之間的相互轉化的情況。

作者心得:

相對于簡單的賦值而言,裝箱和拆箱過程需要進行大量的計算。對值類型進行裝箱時,必須分配并構造一個全新的對象。此外,拆箱所需的強制轉換也需要進行大量的計算。因此,讀者在進行裝箱和拆箱操作時應該考慮到該操作對性能的影響。

2.2.5 泛型

C#中的泛型類似C++的模板,它在一定程度上能夠提高應用程序的效率。使用泛型可以定義類型安全的數據結構,而無需使用具體實際的數據類型。通過使用泛型,能夠將數據類型參數化,以此完成代碼重用的目標。

2.2.5.1 使用系統的泛型類

通常情況下,泛型常見于集合應用中。在System.Collections.Generic名稱空間中,包含了一些基于泛型的容器類,例如System.Collections.Generic.Stack、System. Collections.Generic. Dictionary、System.Collections.Generic.List和System.Collections. Generic.Queue等,這些類庫可以在集合中實現泛型。

創建泛型的格式:

          1)   類名 <Type>  變量名 =new 類名 <Type>();
          2)   List<String>l2=new ArrayList();

第1行是泛型創建的格式,使用泛型類必須指定實際的類型,并在<>角括號中指定實際的類型。第2行根據格式創建了String類型的泛型類的集合類型l2,這意味著l2支能存儲String類型的數據。

泛型的的使用如下面的代碼:

          1)   List<String>l=new ArrayList();
          2)   l.add("小明");
          3)   l.add("小王");
          4)   String s=l.get(0)

第1行,使用泛型List<String> l=new ArrayList()創建ArrayList的對象l,然后第2、3行使用ArrayList類的add方法,傳入二個String類型的參數:小明和小王。這里只能傳泛型中定義的類型,否則報錯。所以第四行使用ArrayList類的方法get取得元素時就不需要類型轉換了,因為所有的元素類型只能是String類型。

C#泛型類在編譯時,先生成中間代碼IL,通用類型T只是一個占位符。在實例化類時,根據用戶指定的數據類型代替T并由即時編譯器(JIT)生成本地代碼,這個本地代碼中已經使用了實際的數據類型,等同于用實際類型寫的類。我們把為所有類型參數提供參數的泛型類型稱為封閉構造泛型類型,簡稱封閉類。不同的封閉類的本地代碼是不一樣的。按照這個原理,可以這樣認為:泛型類的不同封閉類是不同的數據類型。

2.2.5.2 創建泛型

除了使用系統的泛型類之外,讀者可以編寫自己的泛型類。下面我們來介紹泛型類和普通類的區別。

1.靜態構造函數

靜態構造函數的規則:只能有一個,且不能有參數,它只能被.NET運行時自動調用,而不能人工調用。

泛型中的靜態構造函數的原理和非泛型類是一樣的,只需把泛型中的不同的封閉類理解為不同的類即可。以下兩種情況可激發靜態的構造函數:

● 特定的封閉類第一次被實例化。

● 特定封閉類中任一靜態成員變量被調用。

2.靜態成員變量

在C#1.0中,類的靜態成員變量在不同的類實例間是共享的,并且它是通過類名訪問的。C#2.0中由于引進了泛型,導致靜態成員變量的機制出現了一些變化:靜態成員變量在相同封閉類間共享,不同的封閉類間不共享。

這也非常容易理解,因為不同的封閉類雖然有相同的類名稱,但由于分別傳入了不同的數據類型,他們是完全不同的類,比如:

          Stack<int> a = new Stack<int>();
          Stack<int> b = new Stack<int>();
          Stack<long> c = new Stack<long>();

類實例a和b是同一類型,它們之間共享靜態成員變量,但類實例c卻是和a、b完全不同的類型,所以不能和a、b共享靜態成員變量。

3.數據類型的約束

在編寫泛型類時,一般情況下,通用數據類型T是不能適應所有類型的。但如何才能限制調用者傳入的數據類型呢?這就需要對傳入的數據類型進行約束,約束的方式是指定T的祖先,即繼承的接口或類。因為C#的單根繼承性,所以約束可以有多個接口,但最多只能有一個類,并且類必須在接口之前。

由于通用類型T是從object繼承來的,所以他在類Node的編寫中只能調用object類的方法,這給程序的編寫造成了困難。比如你的類設計只需要支持兩種數據類型int和string,并且在類中需要對T類型的變量比較大小,但這些卻無法實現,因為object是沒有比較大小的方法的。為了解決這個問題,只需對T進行IComparable約束,這時在類Node里就可以對T的實例執行CompareTo方法了。這個問題可以擴展到其他用戶自定義的數據類型。

如果在類Node里需要對T重新進行實例化該怎么辦呢?因為類Node中不知道類T到底有哪些構造函數。為了解決這個問題,需要用到new約束,需要注意的是,new約束只能是無參數的,所以也要求相應的類Stack必須有一個無參構造函數,否則編譯失敗。

4.泛型的使用

上面我們了解了泛型的基本概念和創建泛型的方法,下面我們通過一個具體的例子來演示如何使用泛型。

范例2.3

ShiLi2-10\Program.cs

本實例使用泛型類List實現學生信息錄入的功能,在控制臺根據提示輸入學生的學號、姓名和年齡,確認錄入信息后,顯示該學生的信息。

代碼路徑:ShiLi2-10\Program.cs

          1.        class Program
          2.        {
          3.          public class Student
          4.          {
          5.              private int id;
          6.              private String name;
          7.              private int age;
          8.              public Student(){}
          9.              public Student(int id, String name, int age)
          10.             {
          11.                this.id=id;
          12.                this.name=name;
          13.                this.age=age;
          14.             }
          15.             public void print(List<Student>stud)
          16.             {
          17.                foreach(Student s in stud)
          18.                {
          19.                    Console.WriteLine("學生的學號是:"+s.id);
          20.                    Console.WriteLine("學生的姓名是:"+s.name);
          21.                    Console.WriteLine("學生的年齡是:"+s.age);
          22.                }
          23.             }
          24.             static void Main(string[]args)
          25.             {
          26.                List<Student>stud=new List<Student>();
          27.                do
          28.                {
          29.                   Console.WriteLine("請輸入學生學號:");
          30.                    int id=Convert.ToInt32(Console.ReadLine());
          31.                    Console.WriteLine("請輸入學生姓名:");
          32.                    string name=Console.ReadLine();
          33.                    Console.WriteLine("請輸入學生年齡:");
          34.                    int age=Convert.ToInt32(Console.ReadLine());
          35.                    Console.WriteLine("請輸入學生家庭住址:");
          36.                    Student s=new Student(id, name, age);
          37.                    stud.Add(s);
          38.                    Console.WriteLine("是否錄入學生信息?Y/N");
          39.                }while(Console.ReadLine().ToLower()=="y");
          40.                   Student studnet=new Student();
          41.                   studnet.print(stud);
          42.                }
          43.             }
          44.         }

程序說明:第3行自定義封裝了學生類,第5行到第7行定義了3個私有字段表示學生的學號、姓名和年齡。第8行定義不帶參數的構造函數,第9行到第13行在構造函數內對三個字段進行初始化。第10行,編寫一種方法print通過foreach語句循環打印學生的信息。

第26行使用泛型初始化泛型集合對象stud,對象類型規定為我們定義的Student類型。第29行到第35行獲得輸入的數據。第37行調用stud對象的Add方法將學生信息添加到泛型集合中。第41行調用Student對象的print方法打印學生信息。

以上代碼運行后會出現如圖2-1所示的結果。

圖2-1 運行程序后的結果

作者心得:

泛型的編程模式可以大大提高代碼的開發效率,雖然其概念比較復雜且難以理解,但讀者只要按照上面的示例代碼編寫自己通用的類型即可。泛型其實就是提供一種模板,而在實際應用中,只要按照這個模板編碼即可。

主站蜘蛛池模板: 阿瓦提县| 东台市| 绿春县| 大邑县| 苍梧县| 苍山县| 亳州市| 金沙县| 行唐县| 三河市| 确山县| 道真| 化德县| 霍山县| 博罗县| 延边| 泗阳县| 成都市| 阳春市| 资中县| 文昌市| 永兴县| 克山县| 奈曼旗| 天柱县| 克山县| 堆龙德庆县| 西丰县| 寿阳县| 东平县| 庆城县| 辽中县| 咸宁市| 开江县| 股票| 天气| 通渭县| 凉城县| 阿尔山市| 隆回县| 桂阳县|