- C#從入門到精通(第2版)
- 龍馬高新教育策劃 國家863中部軟件孵化器編著
- 73字
- 2019-01-02 05:29:03
第3章 數據類型
本章視頻教學錄像:53分鐘
通過第2章的學習,我們對編寫C#程序有了一個簡單的了解。要想真正編出自己想要的程序,實現需要的功能,必須熟練掌握C#語法知識,這樣在以后的練習中才能順利進階,達到高手水平。本章將介紹數據類型相關的內容。
本章要點(已掌握的在方框中打鉤)
□ 值類型
□ 引用數據類型
□ 類型轉換
□ 裝箱和拆箱
3.1 數據類型概述
本節視頻教學錄像:3分鐘
數據類型就是指數據的種類。在應用程序中,要使數據能被計算機識別并處理,需要將數據分為不同的類型,這樣做的好處是方便存儲和計算。比如對姓名和地址的處理需要使用字符型,在對貨幣和數量的處理中又需要使用數值類型,這些數據都是不同類型的數據。如姓名“張三”為字符型,年齡“25”為整型等。
提示
為什么要定義數據類型?
因為計算機是沒有思維的,你只有告訴它,它才知道這是什么。
比如你定義“int a”,計算機才知道a是一個整數,否則就因識別不出來它是什么而出錯。
C#的數據類型分為值類型、引用類型和指針類型3大類。值類型包括簡單類型、結構類型和枚舉類型等。引用類型包括類類型、接口類型、委托類型和數組類型等。指針類型只能用于安全模式。下圖展示了各種數據類型及其之間的關系。

值類型的數據存儲在內存的堆棧中,可以提供快速訪問。如果變量是值類型的,這個變量就包含實際數據,在一個獨立的內存區域保存自己的值;如果在代碼中修改其值,在內存中會保存修改后的值。C#中的大多數基本數據類型,如整型、字符型、浮點型、布爾型等都是值類型,結構、枚舉也屬于值類型。
引用類型是指向存儲在內存堆中的數據的指針或引用。與純粹的地址不同,引用總是指向一個對象,而且這個對象具有指定的類型,并且在堆上分配了存儲空間。字符串、數組、接口、類等都屬于引用類型。引用類型很抽象,就像是一個門牌號碼,可以根據門牌號碼找到辦公室所在位置。值類型和引用類型的基本區別是內存中的存儲方式。
所有的值類型均隱式地派生自System.ValueType,并且值類型不能派生出新的類。引用類型的變量又稱為對象,可存儲對實際數據的引用。常見的引用類型有class、interface、delegate、object和string等。多個引用變量可以附加于一個對象,而且某些引用可以不附加于任何對象,如果聲明了一個引用類型的變量,卻不將它賦給任何對象,那么它的默認值就是null。相比之下,值類型的值不能是null。
3.2 值類型
本節視頻教學錄像:23分鐘
C#語言的值類型包括整數類型、浮點數類型、布爾類型、字符類型等簡單類型及枚舉類型和結構類型。本小節介紹簡單類型,枚舉類型和結構類型將在后面章節中介紹。
1. 整數類型
整數類型的變量值為整數。計算機語言提供的整數類型的值總是在一定的范圍之內。根據數據在計算機內存中所占的位數來劃分,C#有8種整數類型的數據,這些數據及其在計算機中表示的整數的范圍如下表所示。

例如,定義整型變量如下。
01 int s; //定義int類型的變量s 02 s=50; //將50的值賦給s
上述兩行代碼也可合并為:
int s=50; //定義int類型的變量s,將50的值賦給s
如果是同類型的多個變量,也可將其聲明語句簡寫入同一行代碼中,例如,
int a=10,b=20,c; //c還未被賦值
對于一個整數沒有進行任何明確的聲明,則該變量默認為int類型,一般情況下,應根據程序的需要選擇合適的數據類型。為了把鍵入的值指定為其他的數據類型,可以在數字后面加上如下字符。
01 uint ui=20 U; //uint類型可以在數字后加上字符U 02 long l=20 L; //long類型可以在數字后加上字符L 03 ulong ul=20 UL; //ulong類型可以在數字后加上字符UL
提示
這些大寫字符也可以用相應的小寫字符代替,但字符“l”易與整數“1”混淆,所以推薦使用大寫字符。
【范例3-1】 創建一個控制臺程序,聲明一個int類型的變量x并初始化為1000,一個byte類型的變量y并初始化為245,最后輸出。
⑴ 在Visual Studio 2013中新建C#控制臺程序,項目名為“Text”。
⑵ 在Program.cs的Main方法中輸入以下代碼(代碼3-1.txt)。
01 int x=1000; //聲明一個int類型的變量x并初始化值 02 byte y=245; //聲明一個int類型的變量y并初始化值 03 Console.WriteLine("x={0}",x); //在控制臺輸出x結果 04 Console.WriteLine("y={0}",y); //在控制臺輸出y結果 05 Console.ReadLine();
【運行結果】
單擊工具欄中的按鈕,即可在控制臺中輸出如下圖所示的結果。

此時,如果將byte類型的變量y賦值為260,重新編譯程序,則會出現錯誤。主要原因是byte類型的變量是8位無符號整數,它的范圍是0~255,260超過了byte類型的范圍,因此編譯程序時會出現錯誤。
2. 浮點數類型
浮點數類型又稱為實數類型,是指帶有小數部分的數值。C#支持兩種浮點數類型:單精度(float)和雙精度(double)。它們的差別在于取值范圍和精度不同。浮點數類型數據的特征如下表所示。

若對于浮點數沒有進行任何明確的聲明,則該變量默認為double類型。如果想強制將其指定為float類型,則在其后面加上字符“F”或者“f”。
float f=10.5 F; //float類型在數字后加上字符F
如果想將數值強制指定為double類型,則在其后面加上字符“D”或者“d”。
double d=112 D; //double類型在數字后加上字符D
技巧
浮點數有一定的取值范圍和有效數字限制,超出規定范圍的數據是無法表示的。float類型精度為7位有效數字,因此float的值經常會有些誤差。例如,10減去9.90得到的結果不是0.10,而是一個接近0.099999999的值。
3. 字符類型
除了數字外,計算機處理的信息主要是字符。C#字符類型采用Unicode字符集,一個Unicode標準字符長度為16位,允許用單個編碼方案表示世界上所有的字符。計算機中對字符型數據的存儲并不是把該字符本身放到內存單元中去,而是將該字符相應的Unicode代碼放到存儲單元中,即一個字符占兩個字節的存儲單元,存儲單元存放的是該字符相應的Unicode碼值。
在C#中,字符常量是用單引號(即撇號)括起來的一個字符,如'a'、'x'、'D'、'?'、'$'等都是字符常量。將字符放在雙引號中,編譯器會把它看做字符串,從而產生錯誤。注意,'a'和'A'是不同的字符常量。如下所示。
char c1='X'; //將字符X賦給字符型變量c1
除了以上形式的字符常量外,C#還允許使用一種特殊形式的字符常量,即以“\”開頭的字符序列。它們一般用來實現一定的控制功能,并沒有一定的字型,這種非顯示字符難以用一般形式的字符表示,故規定用這種特殊的形式來表示,這種形式的字符也稱為“轉義字符”。在C#中,轉義字符及其含義如下表所示。

轉義字符的使用如下所示。
char c2="\"; //c2表示單引號
char c3="\0"; //c3表示空字符
C#中用string類型表示字符串,所以不需要使用char數組表示字符串。
4. decimal類型
為了適應高精度的財務和貨幣計算的需要,C#提供了十進制decimal類型。decimal類型數據的特征如下表所示。

要把數字指定為decimal類型,而不是double、float或者整型,可以在數字后面加上字符“M”或者“m”,例如,
decimal d=2.718 M; //定義decimal類型的變量d,將2.718的值賦給d
提示
如果計算的結果對精度要求非常高,如財務金融計算,就應該使用decimal類型,而不是浮點數類型。這是因為decimal類型有比浮點數類型數據較高的精度和較小的值域,decimal類型不受舍入錯誤的影響。
【范例3-2】 已知圓的半徑為12cm,編程計算圓的面積。
⑴ 在Visual Studio 2013中新建C#控制臺程序,項目名為“Circle”。
⑵ 在Program.cs的Main方法中輸入以下代碼(代碼3-2.txt)。
01 decimal pi=3.14159M; //字母M表示數據是decimal類型 02 int r=12; //定義int型變量r表示圓的半徑 03 decimal s=0; //用來存放圓的面積 04 s=pi*r*r; //計算圓的面積 05 Console.WriteLine("圓的半徑是{0},\n圓的面積是:{1}",r,s); //在控制臺輸出結果 06 Console.ReadKey(); //暫停運行,按任意鍵繼續 07 Console.ReadLine();
【運行結果】
單擊工具欄中的按鈕,即可在控制臺中輸出如下圖所示的結果。

【范例分析】
在這個實例中定義了decimal類型的變量pi和s來代表圓周率和圓的面積,int型的變量r表示圓的半徑。第1行代碼中數據的后面加M表示數據是Decimal類型;第4行是計算圓的面積;第5行是在控制臺輸出結果,“\n”是轉義字符,起換行作用,{0}和{1}中的0和1是占位符號,分別將r和s的結果顯示在{0}和{1}所在的位置;第6行是暫停程序的運行,以便能看清運行的結果。
【拓展訓練】
把【范例3-2】的輸出結果以消息框的形式顯示(拓展代碼3-2.txt)。
⑴ 在【范例3-2】中添加對“System.Windows.Forms”的引用。在【解決方案資源管理器】中的【引用】上單擊鼠標右鍵,單擊彈出的【添加引用】項,在出現的【添加引用】窗口中選擇【.NET】選項卡,找到“System.Windows.Forms”,然后單擊【確定】按鈕即可。

⑵ 在Program.cs中添加如下的導入命名空間語句。
using System.Windows.Forms; //使用創建基于Windows的應用程序的類
⑶ 將【范例3-2】步驟⑵中的第5行語句改為使用MessageBox.Show輸出運行結果。
MessageBox.Show("圓的半徑是:"+r+"\n圓的面積是:"+s); //以消息框輸出圓的面積
【運行結果】
運行結果如下圖所示。

MessageBox.Show能以消息框的形式顯示結果,要求以字符串形式表示輸出的結果。“\n”是轉義字符,起換行作用。
5. 布爾類型
布爾類型(bool)是一種用來表示“真”或“假”的邏輯數據類型,布爾類型占用一個字節的內存。在C#中,布爾類型變量只有兩種取值:true(代表“真”)和false(代表“假”)。并且true值不能被其他任何非0值所代替。如下所示。
01 bool flag=true; //正確 02 bool flag=1; //錯誤,不能將一個整型數據賦給布爾類型的變量
3.3 引用類型
本節視頻教學錄像:2分鐘
C#中的值類型比較簡單,但對更加復雜的數據處理效率很低。C#的引用類型主要用來描述結構復雜、抽象能力比較強的數據,它與值類型數據是相并列的。同為引用類型的兩個變量,可以指向同一個對象,也可以針對同一個變量產生作用,或者被其他同為引用類型的變量所影響。字符串、類、接口、委托和數組等均屬于引用類型。下面介紹字符串類型。
字符串是一種引用類型,是由放在一對雙引號中的多個字符組成的一個串,可以將其看做一個由字符組成的數組。通常使用string來聲明字符串變量,例如,
string name="Tom" //定義一個值為Tom的字符串變量name
關于字符串的詳細操作,將在8.1節詳細介紹。
3.4 數據類型之間的轉換
本節視頻教學錄像:20分鐘
在輸出結果時經常要把整型、浮點型等類型轉換為字符串。不同類型的數據需要轉換為同一類型才能正常計算,所有的操作過程中經常會涉及數據類型之間的轉換。C#中數據類型的轉換可以分為兩類:隱式轉換和顯式轉換。
3.4.1 隱式轉換
隱式轉換就是系統默認的、不需要加以聲明就可以進行的轉換。在該過程中,編譯器不需要對轉換進行詳細檢查,就能安全地進行轉換。例如,
01 short st=250; 02 int i=st; //將短整型隱式轉換成整型
在C#引入var類型的變量之前,隱式轉換僅存在于數值類型的數據之間。引入var類型之后,用var定義的變量可以實現隱式數據轉換。
1. 數值類型數據間的隱式轉換
隱式數據類型轉換適用于數值類型的數據之間,如整型數據(int)可以隱式轉換為浮點型(float)和雙精度型(double)數據,浮點型(float)可以隱式轉換為雙精度型(double)數據。隱式數據類型轉換只有遵循如下表所示的規則才能實現。

從int、uint、long或ulong到float,以及從long或ulong到double的轉換可能導致精度損失,但不會影響數量級。其他的隱式轉換不會丟失任何信息。
2. var類型數據隱式轉換
用var定義的變量的數據類型是由賦值的數據決定的。如var Name="Johnson",此時變量Name就是字符串類型進行了隱式轉換。如下所示。
01 var intNum=250; 02 int i=intNum; //var型變量intNum隱式轉換成整型 03 var Name="Johnson"; 04 string strName=Name; //var型變量Name隱式轉換成string型
3.4.2 顯式轉換
顯式轉換又叫做強制類型轉換,需要用戶明確地指定轉換的類型。通過顯式數據轉換,可以把取值范圍大的數據轉換為取值范圍小的數據。顯式轉換可以發生在表達式的計算過程中,但可能引起信息的丟失。例如,下面的代碼把float類型的變量pi強制轉換為int,小數部分的信息就丟失了。
01 float pi=3.14f; //定義一個單精度的實數
02 int i=(int)pi; //將單精度強制轉換為整型來計算,i的值是3,不是3.14,造成信息丟失
3.4.3 使用Convert類轉換
.Net Framework提供了很多類庫,其中System.Convert類就是專門進行類型轉換的類,通過Convert類提供的方法可以實現各種基本數據類型間的轉換。Convert類的常用方法如下表所示。

例如,
01 String MyString="true"; 02 Bool MyBool=Convert.ToBoolean(MyString); //將String轉換為Boolean型,MyBool=true 03 String newString="123456789"; 04 Int MyInt=Convert.ToInt32(newString); //將字符串轉換為數字值,MyInt=123456789
【范例3-3】 隱式轉換、顯式轉換使用舉例。
⑴ 在Visual Studio 2013中新建C#控制臺程序,項目名為“TypeConvert”。
⑵ 在Program.cs的Main方法中輸入以下代碼(代碼3-3.txt)。
01 Console.WriteLine("隱式、顯式轉換例子:"); 02 short r=25; //表示圓的半徑 03 int i=r; //將短整型r隱式轉換成整型 04 float pi=3.14f; //定義一個單精度的實數 05 double s1=pi*i*i; //s1為double型,表示圓的面積 06 int s2; 07 s2=(int)pi*i*i; //s2為int型,表示圓的面積 08 var Name="Johnson"; 09 string strName=Name; //var型變量Name隱式轉換為string型 10 Console.WriteLine("r=25,圓的面積={0},{1}",s1,s2);
11 Console.WriteLine("轉換成功!"); 12 Console.ReadLine();
【代碼詳解】
第3行表示將short類型隱式轉換為int;第5行表示將int和float類型隱式轉換為double類型;第7行表示顯式把float轉換為int;第8~9行表示var類型隱式轉換為string類型。
【運行結果】
單擊工具欄中的按鈕,即可在控制臺中輸出如下圖所示的結果。

技巧
第7行代碼如果寫成s2=pi*i*i,隱式轉換將失敗,因為float不能隱式轉換為int類型。
【范例分析】
這個范例主要演示了類型的轉換。從運行結果看,強制轉換存在信息丟失的現象,如第7行將pi強制轉換為int,原來是3.14,轉換后變成了3。隱式類型轉換要遵循前面給出的規則,否則轉換就會失敗。
【拓展訓練】
擴展【范例3-3】,使用Convert類的方法來實現類型的轉換。代碼如下(拓展代碼3-3.txt)。
01 Console.WriteLine("隱式、顯式轉換例子:"); 02 short r=25; //r表示圓的半徑 03 int i=Convert.ToInt32(r); //將短整型r隱式轉換成整型 04 float pi=3.14f; //定義一個單精度的實數 05 double s1=pi*i*i; //s1為double型,表示圓的面積 06 int s2; 07 s2= Convert.ToInt32(pi)*i*i; //s2為int型,表示圓的面積 08 var memberName="Johnson"; 09 string strName= Convert.ToString(memberName); //var型變量隱式轉換為string型 10 Console.WriteLine("r=25,圓的面積={0},{1}",s1,s2); 11 Console.WriteLine("memberName={0}",memberName); 12 Console.WriteLine("轉換成功!"); 13 Console.ReadLine();
【運行結果】
運行結果如下圖所示。

3.4.4 數值和字符串之間的轉換
在C#中字符串和數值經常需要互相轉換,下面介紹二者之間的轉換方法。
ToString()方法:數值類型的ToString()方法可以將數值型數據轉換為字符串。
Parse()方法:數值類型的Parse()方法可以將字符串轉換為數值型,例如,字符串轉換為整型使用int.Parse(string),字符串轉換為雙精度浮點型使用double.Parse(string)等。
例如,
01 int num1=25; 02 string str1=num1.ToString(); //num1的ToString()方法將num1轉換為string賦給str1 03 string str2="38"; 04 int num2=int.Parse(str2); //int.Parse()方法將字符串str2轉換為int類型 05 string str3="21"; 06 double num3=double.Parse(str3); //double.Parse()將字符串轉換為雙精度浮點型 07 string str4="56"; 08 float num4=float.Parse(str4); //float.Parse()將字符串轉換為單精度浮點型
3.4.5 裝箱和拆箱
拆箱是把“引用”類型轉換成“值”類型,裝箱是把“值”類型轉換成“引用”類型,這是數據類型轉換的一種特殊應用。有時某些方法的參數要求使用“引用”類型,而想把“值”類型的變量通過這個參數傳入,就需要使用這個操作。例如,
01 int n=4; //n是值類型 02 object obj=n; //封箱,把任何值類型隱式地轉換為object類型,其中object為引用類型 03 Console.WriteLine("n的初始值為:{0},裝箱后的值為{1}",n,obj.ToString()); 04 int m=(int)obj; //拆箱,把一個object類型隱式地轉換為值類型 05 Console.WriteLine("引用類型的值為:{0},拆箱后的值為{1}",obj.ToString(),m)
3.5 高手點撥
本節視頻教學錄像:4分鐘
引用類型和值類型變量的使用是C#中的高級技巧之一。值類型變量中保存的是自己的實際數據,在賦值的時候會把源變量的數據復制一份,然后賦給目的變量;引用類型變量中保存的是“指向實際數據的指針”,即實際對象數據的內存地址,在進行賦值操作的時候,它和值類型一樣,也是先有一個復制的操作,不過它復制的不是實際的數據,而是引用(真實數據的內存地址)。
1. 怎么區分值類型與引用類型
以struct關鍵字定義的數據類型就是值類型,另外,枚舉類型也是值類型;以class關鍵字定義的數據類型就是引用類型。
2. 值類型變量與引用類型變量的使用區別
值類型變量不需要使用new關鍵字來分配內存。相信大家使用int型變量時,就沒有使用new來為其分配內存,定義完之后,就可以直接使用;引用類型變量則需要為其賦值后,才能使用。下面舉例說明值類型與引用類型在使用上的區別。
首先定義兩種類型的員工:結構體SEmployee和類CEmployee。
01 struct SEmployee 02 { 03 public int Age; 04 } 05 class CEmployee 06 { 07 public int Age; 08 }
在main函數中,按照如下編寫:
01 { 02 SEmployee se; 03 se.Age=4; 04 Console.WriteLine("年齡:{0}",se.Age); 05 Console.ReadKey(); 06 }
程序編譯沒有任何問題,運行后輸出【年齡:4】。現在按照如下的代碼編寫main函數:
01 { 02 CEmployee ce; 03 ce=new CEmployee(); 04 ce.Age=5; 05 Console.WriteLine("年齡:{0}",ce.Age); 06 Console.ReadKey(); 07 }
這時程序就不能通過編譯,提示使用了未賦值的變量“ce”,把注釋掉的那一行恢復就可以順利通過編譯,運行后輸出【年齡:5】。
通過這個小例子可以看出,值類型變量在定義后,就直接可以使用,而引用類型必須使用new關鍵字后才能使用。對于引用類型會有“未將對象引用設置到對象的實例”錯誤,而值類型不會發生這種錯誤。
3.值類型與引用類型的內存分配區別
內存分配時間:值類型變量在定義后就完成內存分配,引用類型必須顯式用new關鍵字來分配內存。
內存分配區域:值類型的實例一般在線程棧上分配(也可以作為字段嵌入引用類型的對象中),在代表值類型的變量中,并不包含一個指向實例的指針,變量已經包含了實例本身的字段;引用類型總是從托管堆分配的,C#的new操作符會返回對象的內存地址,也就是指向對象數據的內存地址。
4.值類型與引用類型的優缺點
值類型變量在定義后,不論使用與否都會進行內存分配,因此當定義值類型而又沒有使用的情況下,這些無用的變量就白白占用了內存。
值類型是按值傳遞的,也就是當用一個變量給另外一個變量賦值時,會把源變量的值賦值一遍,傳給目的變量,當值類型(結構體)定義的比較大時,值類型的傳遞效率就非常低。
每個值類型的變量都有自己的內存空間,很難做到幾個值類型的變量共享一個內存空間。
引用類型變量在定義后,不會自動分配內存,因此我們可以在用到引用類型變量的時候,用new來分配空間,不會造成內存的浪費;但是,引用類型是在托管堆上分配的,也就是其內存回收通過垃圾回收器來回收,引用類型的使用會增加應用程序在其生存期內需要進行的垃圾回收次數。
引用類型是按引用傳遞的,也就是內存地址在引用類型變量之間傳遞,這種傳遞方式效率非常高,當對象非常大時,可以優先考慮引用類型。
每個引用變量存儲的是內存地址,可以實現多個變量共享一個內存空間。
當數據傳遞后,要求對目的變量的修改不能影響源變量的值,可以考慮將數據類型設計為值類型;當數據可以傳遞后,要求其后的操作必須影響到源數據,可以考慮將數據類型設計為引用類型。值類型與引用類型各有優缺點,使用時應根據實際的需要,選擇合適的類型。
3.6 實戰練習
操作題
創建一個控制臺應用程序,聲明一個整型變量x并賦值為100,然后將其復制到裝箱對象obj中。最后進行拆箱操作,將裝箱對象obj賦值給整型變量y。