◆本章內容
(1)了解變量的基本概念
(2)掌握變量的聲明及賦值
(3)熟悉變量的作用域
(4)掌握數值類型的概念及用法
(5)掌握引用類型的概念及用法
(6)熟悉枚舉類型的概念及用法
(7)掌握常用的類型轉換方式
(8)了解常量的概念及用法
◆本章簡述
應用程序的開發離不開變量與常量的應用。變量本身被用來存儲特定類型的數據,而常量則存儲不變的數據值。本章詳細地介紹變量的類型和基本操作,同時對常量也做了詳細的講解,講解過程中為了便於讀者理解結合了大量的舉例。
◆知識框架
3.1 變量的基本概念
變量本身被用來存儲特定類型的數據,可以根據需要隨時改變變量中所存儲的數據值。變量具有名稱、類型和值。變量名是變量在程序源代碼中的標識。變量類型確定它所代表的內存的大小和類型,變量值是指它所代表的內存塊中的數據。在程序的執行過程中,變量的值可以發生變化。使用變量之前必須先聲明變量,即指定變量的類型和名稱。
3.2 變量的聲明及賦值
變量在使用之前,必須進行聲明並賦值,本節將對變量的聲明及賦值,以及變量的作用域進行詳細講解。
3.2.1 聲明變量
變量的使用是程序設計中一個十分重要的環節。為什么要定義變量呢?簡單地說,就是要告訴編譯器(compiler)這個變量是屬於哪一種數據類型,這樣編譯器才知道需要配置多少空間給它,以及它能存放什么樣的數據。在程序運行過程中,空間內的值是變化的,這個內存空間就稱為變量。聲明變量就是指定變量的名稱和類型,變量的聲明非常重要,未經聲明的變量本身並不合法,也因此沒有辦法在程序當中使用。在C#中,聲明一個變量是由一個類型和跟在后面的一個或多個變量名組成的,多個變量之間用逗號分開,聲明變量以分號結束。
[例3.1]聲明一個整型變量num, 然后再同時聲明3個整型變量sum1、sum2和sum3,代碼如下:
int num;//聲明一個整型變量 int sum1, sum2, sum3;//同時聲明3個整型變量
在第1行代碼中,聲明了一個名稱為num的整型變量。在第2行代碼中,聲明了3個整型變量,分別為sum1、sum2和sum3。
聲明變量時,還可以初始化變量,即在每個變量名后面加上給變量賦初始值的指令。
[例3.2]聲明一個整型變量 a,並且賦值為927。然后,再同時聲明3個整型變量,並初始化,代碼如下:
int a = 927;//初始化整型變量a int x = 1, y = 2, z = 3;//初始化整型變量x,y和z
在聲明變量時,要注意變量名的命名規則。C#的變量名是一種標識符,應該符合標識符的命名規則。變量名是區分大小寫的,下而列出變量的命名規則。
◆變量名只能由數字、字母和下畫線組成。
◆變量名的第一個符號只能是字母和下畫線,不能是數字。
◆不能使用關鍵字作為變量名。
◆一旦在一個語句塊中定義了一個變量名,那么在變量的作用域內都不能再定義同名的變量。
說明:在C#語言中允許使用漢字或其他語言文字作為變量名,如“int年齡=21”,在程序運行時並不出現什么錯誤,但建議讀者盡量不要使用這些語言文字作為變量名。
3.2.2 變量的賦值
在C#中,使用賦值運算符“=”(等號)來給變量賦值,將等號右邊的值賦給左邊的變量。
[例3.3]聲明一個變量,並給變量賦值,代碼如下:
int sum;//聲明一個變量 sum = 2019;//使用賦值運算符“=”給變量賦值
在3.2.1節介紹的初始化變量,其實是一種特殊的賦值方式,它在聲明變量的同時給變量賦值。在給變量賦值時,等號右邊也可以是一個已經被賦值的變量。
[例3.4]首先聲明兩個變量sum和num,然后將變量sum賦值為927,最后將變量sum賦值給變量num,代碼如下:
int sum, num;//聲明兩個變量 sum = 927;//給變量sum賦值為927 num = sum;//將變量sum賦值給變量num
注意:在對多個同類型的變量賦同一個值時,為了節省代碼的行數,可以同時對多個變量進行初始化:
int a, b, c, d, e; a = b = c = d = e = 0;
3.2.3 變量的作用域
由於變量被定義出來后只是暫存在內存中,等到程序執行到某一個點后,該變量會被釋放掉,也就是說變量有它的生命周期。因此,變量的作用域是指程序代碼能夠訪問該變量的區域,若超出該區域,則在編譯時會出現錯誤。在程序中,一般會根據變量的“有效范圍”將變量分為“成員變量"和“局部變量”。
1. 成員變量
在類體中定義的變量被稱為成員變量,成員變量在整個類中都有效。類的成員變量又可分為兩種,即靜態變量和實例變量。
[例3.5]聲明靜態變量和實例變量,實例代碼如下:
class Test { int x = 45; static int y = 90; }
其中,x為實例變量,y為靜態變量(也稱類變量)。如果在成員變量的類型前面加上關鍵字static,這樣的成員變量稱為靜態變量。靜態變量的有效范圍可以跨類,甚至可達到整個應用程序之內。對於靜態變量,除了能在定義它的類內存取,還能直接以“類名.靜態變量”的方式在其他類內使用。
2. 局部變量
在類的方法體中定義的變量(方法內部定義,“{”與“}”之間的代碼中聲明的變量)稱為局部變量。局部變量只在當前代碼塊中有效。
在類的方法中聲明的變量,包括方法的參數,都屬於局部變量。局部變量只有在當前定義的方法內有效,不能用於類的其他方法中。局部變量的生命周期取決於方法,當方法被調用時,C#編譯器為方法中的局部變量分配內存空間,當該方法的調用結束后,則會釋放方法中局部變量占用的內存空間,局部變量也將會銷毀。
變量的有效范圍如圖3.1所示。
[例3.6]創建一個控制台應用程序,使用for循環將從0~20的數字顯示出來。然后在for語句中聲明變量i,此時i就是局部變量,其作用域只限於for循環體內,代碼如下:
static void Main(string[] args) { //調用for語句循環輸出數字 for(int i=0;i<=20;i++)//for循環內的局部變量i { Console.WriteLine(i.ToString());//輸出0-20的數字 } Console.ReadLine(); }
程序運行結果如圖。
3.3 數據類型
C#中的變量類型根據其定義可以分為兩種:一種是值類型,另一種是引用類型。這兩種類型的差異在於數據的存儲方式,值類型的變量本身直接存儲數據。而引用類型則存儲實際數據的引用,程序通過此引用找到真正的數據,在以下內容中將會對這些類型進行詳細講解。C#中的數據類型結構如圖3.2所示。
3.3.1 值類型
值類型變量直接存儲其數據值,主要包含整數類型、浮點類型以及布爾類型等。值類型變量在棧中進行分配,因此效率很高,使用值類型主要目的是為了提高性能。值類型具有如下特性。
◆值類型變量都存儲在棧中。
◆訪問值類型變量時,一般都是直接訪問其實例。
◆每個值類型變量都有自己的數據副本,因此對一個值類型變量的操作不會影響其他變量。
◆復制值類型變量時,復制的是變量的值,而不是變量的地址。
◆值類型變量不能為 null,必須具有一個確定的值。
值類型是從System.ValueType類繼承而來的類型,下面詳細介紹值類型中包含的幾種數據類型。
1. 整數類型
整數類型用來存儲整數數值,即沒有小數部分的數值。可以是正數,也可以是負數。整型數據在C#程序中有3種表示形式,分別為十進制、八進制和十六進制。
十進制: 十進制的表現形式大家都很熟悉,如120、0、-127。
注意:不能以0作為十進制數的開頭(0除外)。
八進制: 如0123(轉換成十進制數為83)、-0123(轉換成十進制數為-83)。
注意:八進制必須以0開頭。
十六進制: 如0x25(轉換成十進制數為37)、0Xb0le(轉換成十進制數為45086)。
注意:十六進制必須以0X或0x開頭。
在C#中內置的整數類型如表3.1所示。
表3.1 C#內置的整數類型 |
|||
類型 |
別名 |
說明(8位等於1字節) |
范圍 |
sbyte |
System.SByte |
8位有符號整數 |
-128~127 |
short |
System.Int16 |
16位有符號整數 |
-32768~32767 |
int |
System.Int32 |
32位有符號整數 |
-2147483648~2147483647 |
long |
System.Int64 |
64位有符號整數 |
-9223372036854775808~9223372036854775807 |
byte |
System.Byte |
8位無符號整數 |
0~255 |
ushort |
System.UInt16 |
16位無符號整數 |
0~65535 |
uint |
System.UInt32 |
32位無符號整數 |
0~4294967295 |
ulong |
System.UInt64 |
64位無符號整數 |
0~18446744073709551615 |
byte類型以及short類型是范圍比較小的整數,如果正整數的范圍沒有超過65535,聲明為ushort類型即可,當然更小的數值直接以byte類型作處理即可。只是使用這種類型時必須特別注意數值的大小,否則可能會導致運算溢出的錯誤。
[例3.7]創建一個控制台應用程序,在其中聲明一個int類型的變量a並初始化為927、一個byte類型的變量b並初始化為255,最后輸出,代碼如下:
static void Main(string[] args) { int a = 927;//聲明一個int類型的變量a byte b = 255;//聲明一個byte類型的變量b Console.WriteLine("a={0}", a);//輸出int類型的變量a Console.WriteLine("b={0}", b);//輸出byte類型的變量b Console.ReadLine(); }
程序運行結果為:
此時,如果將byte類型的變量b賦值為266,重新編譯程序,就會出現錯誤提示。
主要原因是byte類型的變量是8位無符號整數,它的范圍為0-255, 266已經超出了byte類型的范圍,所以編譯程序會出現錯誤提示。
注意:1、在定義局部變量時,要對其進行初始化。
2、在定義全局變量時,如果沒有特定的要求不用對其進行初始化,整數類型默認初始化為0。
2. 浮點類型
浮點類型變量主要用於處理含有小數的數值數據,浮點類型主要包含float和double兩種數值類型。
表3.2列出了這兩種數值類型的描述信息。
表3.2浮點類型及描述 |
|||
類型 |
別名 |
說明 |
范圍 |
float |
System.Single |
精確到7位數 |
±1.5×10-45~±3.4×1038 |
double |
System.Double |
精確到15~16位數 |
±5.0×10-324~±3.4×10308 |
如果不做任何設置,包含小數點的數值都被認為是double類型,例如9.27,沒有特別指定的情況下,這個數值是double類型。如果要將數值以float類型來處理,就應該通過強制使用f或F將其指定為float類型。
[例3.8]下面的代碼就是將數值強制指定為float類型。
float theMySum = 9.27f;//使用f強制指定為float類型 float theMuSums = 1.12F;//使用F強制指定為float類型
如果要將數值強制指定為double類型,則應該使用d或D進行設置,但加不加d或D沒有硬性規定,可以加也可以不加。
[例3.9]下面的代碼就是將數值強制指定為double類型。
double myDou = 927d;//使用d強制指定為double類型 double mudou = 112D;//使用D強制指定為double類型
注意:1、如果需要使用float類型變量時,必須在數值的后面跟隨f或F,否則編譯器會直接將其作為double類型處理。也可以在double類型的值前面加上(float), 對其進行強制轉換。
2、在定義局部變量時,要對其進行初始化。
3、在定義全局變量時,如果沒有特定的要求不用對其進行初始化,浮點類型默認初始化為0而不是0.0。
3. decimal類型
decimal類型表示128位數據類型,它是一種精度更高的浮點類型,其精度可以達到28位,取范圍為±1.0×1028~±7.9×1028。
說明:由於decimal類型的高精度特性,它更合適於財務和貨幣計算。
如果希望一個小數被當成decimal類型,需要使用后綴m或M,例如:
decimal myMoney = 1.12m;
如果小數沒有后綴m或M,數值將被視為double類型,從而導致編譯器錯誤,例如,在開發環境中運行下面代碼:
static void Main(string[] args) { decimal d = 3.14; Console.WriteLine(d); }
將會出現如圖所示的錯誤提示。
從上圖可以看出,3.14這個數如果沒有后綴,直接被視為double類型,所以賦值給decimal類型的變量時,就會出現錯誤提示,應該將3.14改為3.14m或3.14M。
4. 布爾類型
布爾類型主要用來表示true和false值,C#中定義布爾類型時,需要使用bool關鍵字。例如:
bool x;
布爾類型通常被用在流程控制中作為判斷條件。
一個布爾類型的變量,其值只能是true或者false,不能將其他的值指定給布爾類型變量,布爾類型變量不能與其他類型之間進行轉換。
[例3.10]將927賦值給布爾類型變量x,代碼如下。
bool x = 927;
這樣賦值顯然是錯誤的,編譯器會返回錯誤提示,如圖
布爾類型變量大多數被應用到流程控制語句當中,例如,循環語句或者if語句等。
注意:1、在定義局部變量時,要對其進行初始化。
2、在定義全局變量時,如果沒有特定的要求不用對其進行初始化,布爾類型的默認初始化為false。
5. 闖關訓練
開發財務系統時,通過值類型創建存儲流動資金金額的臨時性變量。
3.3.2 引用類型
引用類型是構建C#應用程序的主要對象類型數據。在應用程序執行的過程中,預先定義的對象類型以new創建對象實例,並且存儲在堆中。堆是一種由系統彈性配置的內存空間,沒有特定大小及存活時間,因此可以被彈性地運用於對象的訪問。引用類型就類似於生活中的代理商,代理商沒有自己的產品,而是代理廠家的產品,使其就好像是自己的產品一樣。
引用類型具有如下特征。
◆必須在托管堆中為引用類型變量分配內存。
◆使用new關鍵字來創建引用類型變量。
◆在托管堆中分配的每個對象都有與之相關聯的附加成員,這些成員必須被初始化。
◆引用類型變量是由垃圾回收機制來管理的。
◆多個引用類型變量可以引用同一對象,這種情形下,對一個變量的操作會影響另一個變量所引用的同一對象。
◆引用類型被賦值前的值都是null。
所有被稱為“類”的都是引用類型,主要包括類、接口、數組和委托。下面通過一個實例來演示如何使用引用類型。
[例3.11]創建一個控制台應用程序,在其中創建一個類C,在此類中建立一個字段 Value,並初始化為0,然后在程序的其他位置通過new關鍵字創建對此類的引用類型變量,最后輸出,代碼如下:
class Program { class C //創建一個類C { public int Value = 0; //聲明一個公共int類型的變量Value } static void Main(string[] args) { int v1 = 0;//聲明一個int類型的變量v1,並初始化為0 int v2 = v1;//聲明一個int類型的變量v2,並將v1賦值給v2 v2 = 927;//重新將變量v2賦值為927 C r1 = new C();//使用new關鍵字創建引用對象 C r2 = r1;//使r2=r1 r2.Value = 112;//設置變量r2的Value值 Console.WriteLine("值類型變量的值:{0},{1}", v1, v2);//輸出變量v1和v2 Console.WriteLine("引用類型對象的值:{0},{1}", r1.Value, r2.Value);//輸出引用類型對象的Value值 } }
程序運行結果如下。
3.3.3 值類型與引用類型的區別
從概念上看,值類型直接存儲其值,而引用類型存儲對其值的引用。這兩種類型存儲在內存的不同地方。在C#中,必須在設計類型時就決定類型實例的行為。如果在編寫代碼時不能理解引用類型和值類型的區別,那么將會給代碼帶來不必要的異常。
從內存空間上看,值類型是在棧中操作,而引用類型則在堆中分配存儲單元。棧在編譯時就分配好內存空間,在代碼中有棧的明確定義,而堆是程序運行中動態分配的內存空間,可以根據程序的運行情況動態地分配內存的大小。因此,值類型總是在內存中占用一個預定義的字節數。而引用類型的變量則在堆中分配一個內存空間,這個內存空間包含的是對另一個內存位置的引用,這個位置是托管堆中的一個地址,即存放此變量實際值的地方。
也就是說值類型相當於現金,要用就直接用,而引類型相當於存折,要用得先去銀行取。
說明:C#的所有值類型均隱式派生自System.ValueType,而System.ValueType 直接派生於System.Object.即System.ValueType本身是一個類類型,而不是值類型。其關鍵在於ValueType重寫了Eouals()方法,從而對值類型按照實例的值來比較,而不是引用地址來比較。
下面以一段代碼來詳細講解一下值類型與引用類型的區別,代碼如下。
namespace ConsoleApp1 { class Program { static void Main(string[] args) { ReferenceAndValue.Demonstration();//調用ReferenceAndValue類中的Demonstation方法 Console.ReadLine(); } } public class Stamp//定義一個類 { public string Name { get; set; }//定義引用類型 public int Age { get; set; }//定義值類型 } public static class ReferenceAndValue//定義一個靜態類 { public static void Demonstration()//定義一個靜態方法 { Stamp Stamp_1 = new Stamp { Name = "Premiere", Age = 25 };//實例化 Stamp Stamp_2 = new Stamp { Name = "Again", Age = 47 };//實例化 int age = Stamp_1.Age; //獲取值類型Age的值 Stamp_1.Age = 22;//修改值類型的值 Stamp guru = Stamp_2;//獲取Stamp_2中的值 Stamp_2.Name = "Again Amend";//修改引用的Name值 Console.WriteLine("Stamp_1 中的 age 值:{0}", Stamp_1.Age);//顯示Stamp_1中的Age值 Console.WriteLine("age 的值:{0}", age);//顯示age值 Console.WriteLine("Stamp_2 中的 name 值:{0}", Stamp_2.Name);//顯示Stamp_2中的Name值 Console.WriteLine("guru 中的 name 值:{0}", guru.Name);//顯示guru中的Name值 } } }
運行結果如圖。
從上圖中可以看出,當改變了Stamp_1.Age的值時,age沒跟着變,而在改變了Stamp_2.Name的值后,guru.Name卻跟着變了,這就是值類型和引用類型的區別。在聲明age值類型變量時,將Stamp_1.Age的值賦給它,這時,編譯器在棧上分配了一塊空間,然后把Stamp_1.Age的值填進去,二者沒有任何關聯,就像在計算機中復制文件一樣,只是把Stamp_1.Age的值復制給age了。而引用類型則不同,在聲明guru時把Stamp_2賦給它。前面說過,引用類型包含的只是堆上數據區域地址的引用,其實就是把Stamp_2的引用也賦給guru,因此它們指向了同一塊內存區域。既然是指向同一塊區域,不管修改誰,另一個的值都會跟着改變。就像信用卡跟親情卡一樣,用親情卡取了錢,與之關聯的信用卡賬上也會跟着發生變化。
3.3.4 枚舉類型
枚舉類型是一種獨特的值類型,它用於聲明一組具有相同性質的常量,編寫與日期相關的應用程序時,經常需要使用年、月、日、星期等日期數據,可以將這些數據組織成多個不同名稱的枚舉類型。使用枚舉可以增加程序的可讀性和可維護性。同時,枚舉類型可以避免類型錯誤。
說明:在定義枚舉類型時,如果不對其進行賦值,默認情況下,第一個枝舉數的值為0,后面每個枚舉數的值依次遞增1。
在C#中使用關鍵字enum類聲明枚舉,其形式如下。
enum 枚舉明 { list1 = value1, list2 = value2, list3 = value3, ... listN = valueN }
其中,大括號“{}”中的內容為枚舉值列表,每個枚舉值均對應一個枚舉值名稱,value1-valueN為整數數據類型,list1-listN則為枚舉值的標識名稱。下而通過一個實例來演示 如何使用枚舉類型。
[例3.12]創建一個控制台應用程序,通過使用枚舉米判斷當前系統日期是星期幾,代碼如下。
class Program { enum MyDate //使用enum創建枚舉 { Sun = 0, //設置枚舉值名稱Sun,枚舉值為0 Mon = 1, //設置枚舉值名稱Mon,枚舉值為1 Tue = 2, //設置枚舉值名稱Tue,枚舉值為2 Wed = 3, //設置枚舉值名稱Wed,枚舉值為3 Thu = 4, //設置枚舉值名稱Thu,枚舉值為4 Fri = 5, //設置枚舉值名稱Fri,枚舉值為5 Sat = 6 //設置枚舉值名稱Sat,枚舉值為6 } static void Main(string[] args) { int k = (int)DateTime.Now.DayOfWeek;//獲取代表星期幾的返回值 switch (k) { //如果k等於枚舉變量MyDate中的Sun的枚舉值,則輸出今天是星期日 case (int)MyDate.Sun: Console.WriteLine("今天是星期日"); break; //如果k等於枚舉變量MyDate中的Mon的枚舉值,則輸出今天是星期一 case (int)MyDate.Mon: Console.WriteLine("今天是星期一"); break; //如果k等於枚舉變量MyDate中的Tue的枚舉值,則輸出今天是星期二 case (int)MyDate.Tue: Console.WriteLine("今天是星期二"); break; //如果k等於枚舉變量MyDate中的Wed的枚舉值,則輸出今天是星期三 case (int)MyDate.Wed: Console.WriteLine("今天是星期三"); break; //如果k等於枚舉變量MyDate中的Thu的枚舉值,則輸出今天是星期四 case (int)MyDate.Thu: Console.WriteLine("今天是星期四"); break; //如果k等於枚舉變量MyDate中的Fri的枚舉值,則輸出今天是星期五 case (int)MyDate.Fri: Console.WriteLine("今天是星期五"); break; //如果k等於枚舉變量MyDate中的Sat的枚舉值,則輸出今天是星期六 case (int)MyDate.Sat: Console.WriteLine("今天是星期六"); break; } Console.ReadLine(); } }
程序運行的結果如圖
查看程序運行的結果,因為當前日期是2021年5月3日星期一,所以輸出的結果顯示當天是星期一。程序首先通過enum關鍵字建立一個枚舉,枚舉值名稱分別代表一周的七天,如果枚舉值名稱是Sun,說明其代表的是一周中的星期日,其枚舉值為0,以此類推。然后,聲明一個int類型的變量k,用於獲取當前表示的日期是星期幾。最后,調用switch語句,輸出當天是星期幾。
3.3.5 類型轉換
類型轉換就是將一種類型轉換成另一種類型,轉換可以是隱式轉換,也可以是顯式轉換,本節將詳細介紹這兩種轉換方式,並講解有關裝箱和拆箱的內容。
說明:要理解類型轉換,讀者可以這么想象,大腦前面是一片內存,源和目標分別是兩個大小不同的內存塊(由變量及數據的類型來決定),將源數據賦值給目標內存的過程,就是用目標內存塊去套取源內存中的數據,能套多少算多少。
1. 隱式轉換
所謂隱式轉換就是不需要聲明就能進行的轉換。進行隱式轉換時,編譯器不需要進行檢查就能自動進行轉換。表3.3列出了可以進行隱式轉換的數據類型。
表3.3隱式類型轉換表 |
|
源類型 |
目標類型 |
sbyte |
short、int、long、float、double、decimal |
byte |
short、ushort、int、uint、long、ulong、float、double或decimal |
short |
int、long、float、double或decimal |
ushort |
int、uint、long、ulong、float、double或decimal |
int |
long、float、double或decimal |
uint |
long、ulong、float、double或decimal |
char |
ushort、int、uint、long、ulong、float、double或decimal |
float |
double |
ulong |
float、double或decimal |
long |
float、double或decimal |
從int、uint、long或ulong到float,以及從long或ulong到double的轉換可能導致精度損失,但是不會影響其數量級。其他的隱式轉換不會丟失任何信息。
這些類型按精度從“低”到“高”排列的順序為byte>short>int>long>float> double,可對照圖3.3,其中char類型比較特殊,它可以與部分int型數字兼容,且不會發生精度變化。
說明:當一種類型的值轉換為大小相等或更大的另一類型時,則發生擴大轉換;當一種類型的值轉換為較小的另一種類型時,則發生收縮轉換。
[例3.13]將int類型的值隱式轉換成long類型,代碼如下。
int i = 927;//聲明一個整型變量i並初始化為927 long j = i;//隱式轉換成long類型
2. 顯式轉換
顯式轉換也可以稱為強制轉換,需要在代碼中明確地聲明要轉換的類型。如果要把高精度的變量的值賦給低精度的變量,就需要使用顯式轉換。表3.4列出了需要進行顯式轉換的數據類型。
表3.4顯式類型轉換表 |
|
源類型 |
目標類型 |
sbyte |
byte、ushort、uint、ulong或char |
byte |
sbyte和char |
short |
sbyte、byte、ushort、uint、ulong或char |
ushort |
sbyte、byte、short或char |
int |
sbyte、byte、short、ushort、uint、ulong或char |
uint |
sbyte、byte、short、ushort、int或char |
char |
sbyte、byte或short |
float |
sbyte、byte、short、ushort、int、uint、long、ulong、char或decimal |
ulong |
sbyte、byte、short、ushort、int、uint、long或char |
long |
sbyte、byte、short、ushort、int、uint、ulong或char |
double |
sbyte、byte、short、ushort、int、uint、ulong、long、float、char或decimal |
decimal |
sbyte、byte、short、ushort、int、uint、ulong、long、float、char或double |
由於顯式轉換包括所有隱式轉換和顯式轉換,因此總是可以使用強制轉換表達式從任何數值類型轉換為任何其他的數值類型。
顯式類型轉換的一般形式為:
(類型說明符)表達式
其功能是把表達式的運算結果強制轉換成類型說明符所表示的類型。
例如,下面的代碼用來把x轉換為float類型:
(float)x;
[例3.14]創建一個控制台應用程序,將double類型的x進行顯式類型轉換,代碼如下。
static void Main(string[] args) { double x = 19810927.0112;//聲明double類型變量x int y = (int)x;//顯示轉換成整型變量y Console.WriteLine(y); //輸出整型變量y Console.ReadLine(); }
程序運行結果為
3. 使用Convert類進行轉換
上節中講解了可以使用“(類型說明符)表達式”進行顯式類型轉換,使用這種方式實現將long型數據轉換成int型數據:
long l = 3000000000;//聲明long類型變量l int i = (int)l;//顯示轉換為int變量i Console.WriteLine(i);//輸出變量i
程序運行結果為:
按照代碼的本意,i的值應該是3000000000,但在運行這兩行代碼時,卻發現i的值是-1294967296,這主要是由於int類型的最大值為2147483647,很明顯,3000000000要比2147483647大,所以在使用上面代碼進行顯式類型轉換時,出現了與預期不符的結果,但是程序並沒有報告錯誤。如果在實際開發中遇到這種情況,可能會引起大的BUG,那么,在遇到這種錯誤時,有沒有一種方式能夠向開發人員報告錯誤呢?答案是肯定的。C#中提供了Convert類,該類也可以進行顯式類型轉換,它的主要作用是將一個基本數據類型轉換為另一個基本數據類型。Convert類的常用方法及說明如表3.3所示。
表3.3 Convert類的常用方法 |
|
方法 |
說明 |
ToBoolean |
將指定的值轉換為等效的布爾值 |
ToByte |
將指定的值轉換為8位無符號整數 |
ToChar |
將指定的值轉換為Unicode字符 |
ToDateTime |
將指定的值轉換為DateTime |
ToDecimal |
將指定值轉換為Decimal數字 |
ToDouble |
將指定的值轉換為雙精度浮點數字 |
Tolnt32 |
將指定的值轉換為32位有符號整數 |
Tolnt64 |
將指定的值轉換為64位有符號整數 |
ToSByte |
將指定的值轉換為8位有符號整數 |
ToSingle |
將指定的值轉換為單精度浮點數字 |
ToString |
將指定值轉換為其等效的String表示形式 |
ToUInt32 |
將指定的值轉換為32位無符號整數 |
ToUInt64 |
將指定的值轉換為64位無符號整數 |
[例3.15]創建一個控制台應用程序,定義一個double類型的變量x,並賦值為198.99,使用Convert類將其顯式轉換為int類型,代碼如下:
double x = 198.99;//聲明double類型變量並初始化 int y = Convert.ToInt32(x);//使用Convert類的方法進行顯示類型轉換 Console.WriteLine(y);//輸出變量y
程序運行結果為:
下面使用Convert類的ToInt32對本節開始的兩行代碼進行修改,修改后的代碼如下:
long l = 3000000000;//聲明long類型變量l int i = Convert.ToInt32(l);//顯示轉換為int變量i Console.WriteLine(i);//輸出變量i
再次運行這兩行代碼,則會出現如圖3.38所示的錯誤提示。
這樣,開發人員即可根據圖中的錯誤提示對程序代碼進行修改,避免程序出現邏輯錯誤。
4. 裝箱和拆箱
將值類型轉換為引用類型的過程叫作裝箱,相反,將引用類型轉換為值類型的過程叫作拆箱,下面將通過例子詳細介紹裝箱與拆箱的過程。
(1)裝箱。裝箱允許將值類型隱式轉換成引用類型,下而通過一個實例演示如何進行裝箱操作。
[例3.16]創建一個控制台應用程序,聲明一個整型變量i並賦值為2019,然后將其復制到裝箱對象obj中,最后再改變變量i的值,化碼如下。
static void Main(string[] args) { int i = 2019;//聲明一個int類型變量i,井初始化為2019 object obj = i;//聲明-個object類型obj,其初始化值為i Console.WriteLine("1、i的值為{0},裝箱之后的對象為{1}", i, obj); i = 927;//重新將i賦值為927 Console.WriteLine("2、i的值為{0},裝箱之后的對象為{1}", i, obj); Console.ReadLine(); }
程序的運行結果為:
從程序運行結果可以看出,值類型變量的值復制到裝箱得到的對象中,裝箱后改變值類型變量的值,並不會影響裝箱對象的值。
(2)拆箱。拆箱允許將引用類型顯式轉換為值類型,下面通過一個示例演示拆箱的過程。
[例3.17]創建一個控制台應用程序,聲明一個整型變量i並賦值為112,然后將其復制到裝箱對象obj中,最后,進行拆箱操作將裝箱對象obj賦值給整型變量j,代碼如下。
static void Main(string[] args) { int i = 112;//聲明-個int類型的變量i,並初始化為112 object obj = i;//執行裝箱操作 Console.WriteLine("裝箱操作: 值為{0},裝箱之后對象為{1}", i, obj); int j = (int)obj;//執行拆箱操作 Console.WriteLine("拆箱操作:裝箱對象為{0},值為{1}", obj, i); Console.ReadLine(); }
程序運行結果為:
查看程序運行結果,不難看出,拆箱后得到的值類型數據的值與裝箱對象相等。需要讀者注意的是,在執行拆箱操作時要符合類型一致的原則,否則會出現異常。
說明:裝箱是將一個值類型轉換為一個對象類型(object),而拆箱則是將一個對象類型顯式轉換為一個值類型。對於裝箱而言,它是將被裝箱的值類型復制一個副本來轉換;而對於拆箱而言,需要注意類型的兼容性,例如,不能將一個值為 “string"的object類型轉換為int類型。
3.4 聲明隱式類型的局部變量
本章前面通過指定數據類型和標識符來聲明變量,如下所示:
int myInt;
前面說過,變量使用前必須賦值。可在同一個語句中聲明並初始化變量,如下所示:
int myInt = 99;
還可像下面這樣做(假定myOtherInt是已初始化的整數變量):
int myInt = myOtherInt * 99;
賦給變量的值必須具有和變量相同的類型。例如,只能將int值賦給int變量。C#編譯器可迅速判斷變量初始化表達式的類型,如果和變量類型不符,就會明確告訴你。除此之外,還可要求C#編譯器根據表達式推斷變量類型,並在聲明變量時自動使用該類型。為此,只需用var關鍵字代替類型名稱,如下所示:
var myVariable = 99; var myOtherVariable = "Hello";
兩個變量myVariable和myOtherVariable稱為隱式類型變量。var關鍵字告訴編譯器根據變量的初始化表達式推斷變量類型。在本例中,myVariable 是int類型,而myOtherVariable是string類型。必須注意,var只是在聲明變量時提供一些方便。但變量一經聲明,就只能將編譯器推斷的那種類型的值賦給它。例如,不能再將float、double、string值賦給myVariable。還要注意,只有提供了初始化表達式,才能使用關鍵字var。
以下聲明非法,會導致編譯錯誤:
var yetAnotherVariable; //錯誤-編譯器不能推斷類型 }
3.5 常量
常量就是其值固定不變的量,而且常量的值在編譯時就已經確定了。常量的類型只能為下列類型之一: sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal、bool、string等。C#中使用關鍵字const和readonly定義常量,下面分別對這兩種常量進行講解。
3.5.1 const常量
在C#中提到常量,通常指的是const常量。const常量也叫靜態常量,它在編譯時就已經確定了值。const常量的值必須在聲明時就進行初始化,而且之后不可以再進行更改。
[例3.18]聲明一個正確的常量,同時再聲明一個錯誤的常量,以便讀者對比參考,代碼如下。
const double PI = 3.1415926;//正確的聲明方法 const int MyInt;///錯誤:定義常量時沒有初始化
3.5.2 readonly常量
readonly常量是一種特殊的常量,也稱為動態常量,從字面理解上看,readonly常量可以進行動態賦值,但需要注意的是,這里的動態賦值是有條件的,它只能在構造函數中進行賦值,例如,下面的代碼:
class Program { readonly int Price;//定義一個reanonly常量 Program()//構造函數 { Price = 368;//在構造函數中修改reanonly常量的值 } static void Main(string[] args) { } }
如果要在構造函數以外的位置修改readonly常量的值,比如,在Main方法中進行修改,代碼如下:
class Program { readonly int Price;//定義一個reanonly常量 Program()//構造函數 { Price = 368;//在構造函數中修改reanonly常量的值 } static void Main(string[] args) { Program p = new Program();//創建類的對象 p.Price = 365;//試圖對readonly常量值修改 } }
這時再運行程序,將會出現如圖所示的錯誤提示。
3.5.3 const常量與readonly常量的區別
const常量與readonly常量的主要區別如下:
◆const常量必須在聲明時初始化,而readonly常量則可以延遲到構造函數中初始化。
◆ const常量在編譯時就被解析,即將常量的值替換成了初始化的值,而readonly常量的值需要在運行時確定。
◆ const常量可以在類中或者方法體中定義,而readonly常量只能在類中定義。
與變量不同,常量在整個程序中只能被賦值一次。在為所有的對象共享值時,常量是非常有用的。下面通過一個例子演示常量與變量的差異。
[例3.19]創建一個控制台應用程序,首先聲明一個變量MyInt並且賦值為927,然后再聲明一個常量MyWInt並賦值為112,最后將變量MyInt賦值為1039,關鍵代碼如下。
static void Main(string[] args) { int MyInt = 927;//聲明一個整型變量 const int MyWInt = 112;//聲明一個整型常量 Console.WriteLine("變量MyInt={0}", MyInt);//輸出 Console.WriteLine("常量MyWInt={0}", MyWInt);//輸出 MyInt = 1039;//重新將變量賦值為1039 Console.WriteLine("變量MyInt={0}", MyInt);//輸出 Console.ReadLine(); }
執行程序,輸出的結果為:
變量MyInt的初始化值為927,而常量MyWnt的值等於112,由於變量的值是可以修改的,所以變量MyInt可以重新被賦值為1039后輸出。通過直看輸出結果,可以看到變量Mylnt的值已經被修改,如果嘗試修改常量MyWInt的值,編譯器會出現錯誤信息,阻止進行這樣的操作。
3.6 小結
本章重點講解了變量和常量,通過大量的舉例說明,使讀者更好地理解所學知識的用法。在閱讀本章時,要重點掌握值類型、引用類型和枚舉類型的概念及用法,並且要了解如何進行類型轉換。了解變量的基本知識后,要掌握如何對變量進行操作,了解變量的作用域以及如何為變量賦值。本章最后對常量進行了詳細的敘述,包括常量的概念及常量的基本類型。
3.7 實踐與練習
(1)嘗試開發一個程序,在該程序中建立一個靜態方法,在靜態方法中聲明一個局部變量,並對其賦值,然后輸出。
(2)嘗試開發一個程序,要求聲明一個常量,然后試着更改這個常量的值,看會引發什么錯誤。