C#變數型別(1):引用型別和值型別
C#是一種型別安全的語言。每一個變數都要求定義為一個特定的型別,並且要求儲存在變數中的值只能是這種型別的值。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
變數既能儲存值型別,也可以儲存引用型別,還可以是指標。這一課將講述前兩種型別,關於指標的討論我們將在下一課中進行。
下面是關於值型別和引用型別不同點的概論:
如果一個變數v儲存的是值型別,則它直接儲存包含資料的物件。任何其他的變數v’都不能直接儲存已經由v儲存了的物件,雖然v’可以儲存一個和v儲存的物件有著相同值的物件。(譯註:這意味著,v和v’儲存的物件毫不相干,任意改變其中一個儲存的物件都不會影響到另一個變數儲存的物件。)
如果變數v儲存的是一個引用型別,它直接儲存一個指向物件的引用,同時可能存在另一個變數v’,它也儲存著指向這個物件的引用。(譯註:這意味著,改變v’引用的物件同時也會影響到v引用的物件,反之亦然。)
值型別
在C#中你可以通過宣告列舉型別或是結構型別來定義你自己的值型別。C#以同樣的方式處理使用者自定義的型別和C#預定義的值型別,不過C#編譯器可能更優於處理後者。下面的表列出了C#中預定義的值型別的一些資訊。因為在C#中所有的基本值型別都是從object型別(最終基類)發展而來,所以下表中還顯示了與這些預定義型別相對應的.Net框架中的System型別。
C# 型別 |
.Net 框架型別 |
有無符號 |
佔據位數 |
取值範圍 |
sbyte |
System.Sbyte |
是 |
1 |
-128 到 127 |
short |
System.Int16 |
是 |
2 |
-32768 到32767 |
int |
System.Int32 |
是 |
4 |
-2147483648 到 2147483647 |
long |
System.Int64 |
是 |
8 |
-9223372036854775808 到 9223372036854775807 |
byte |
System.Byte |
否 |
1 |
0 到 255 |
ushort |
System.Uint16 |
否 |
2 |
0 到 65535 |
uint |
System.UInt32 |
否 |
4 |
0 到 4294967295 |
ulong |
System.Uint64 |
否 |
8 |
0 到18446744073709551615 |
float |
System.Single |
是 |
4 |
可能值從 ±1.5 x 10-45 到 ±3.4 x 1038 ,小數點後7位有效數字 |
double |
System.Double |
是 |
8 |
可能值從 ±5.0 x 10-324 to ±1.7 x 10308 小數點後15到16位有效數字 |
decimal |
System.Decimal |
是 |
12 |
可能值從 ±1.0 x 10-28 到±7.9 x 1028 小數點後28到29位有效數字 |
char |
System.Char |
N/A |
2 |
任何16位Unicode字元 |
bool |
System.Boolean |
N/A |
1 / 2 |
true 或者false |
在下面的程式碼中,兩個變數都宣告為整形,並得到賦值:
int x = 10;
int y = x;
y = 20; // 這條語句執行後x的值為10,y的值為20;
引用型別
C#預定義的引用型別包括object和string型別。正如我們在上面提到的,object型別是所有其他型別的最終基類。使用者定義的引用型別可以是介面型別、類型別和委託型別(第12課會有具體介紹)。
引用型別事實上儲存一個指向它引用的物件的記憶體地址。下面的程式碼段中有兩個變數引用了同一個物件(本例中,假設這個物件有一個數據成員’myValue’):
object x = new object();
x.myValue = 10;
object y = x;
y.myValue = 20; // 這條語句執行後,x.myValue和y.myValue的值都為20。
上面的這段程式碼演示了引用型別的一個特點:改變某一個引用指向的物件的屬性同時也會影響到所有其他指向這個物件的引用。不過,strings型別雖然也是引用型別,但它的工作方式更象值型別。當一個字串被指定了另一個字串的值時,例如:
string s1 = "hello";
string s2 = s1;
s2和s1都引用了同一個字串型別,但是當s1的值發生改變時,例如s1=”goodbye”;s2的值仍然是”hello”。之所以會這樣,是因為當改變s1的值是,新建立了一個string物件,s1引用這個新的string物件;s2仍然引用原來string物件。產生這種行為的原因是string物件是恆定的,也就是說,一旦一個string物件被建立,它的值就不能再修改,所以當改變一個字串變數的值的時候,僅僅是新建立了一個包含修改內容的新的string物件。
轉義序列和逐字字串(verbatim strings)
當宣告一個字串變數時有一些字元是不能以平常的方式包含在變數中的。為了解決這個問題,C#提供了兩種不同的方法。
第一種方法是使用’轉義序列’。例如,我們想得到如下的字串
“Hello World
How are you”
我們可以使用下面的語句宣告字串:string a = "/"Hello World/nHow are you/""。這條語句中使用了”和換行符的轉義序列。更多字元的轉義序列可以參見下表:
Character |
Escape Sequence |
' |
/' |
" |
/" |
/ |
// |
警報 |
/a |
退格符 |
/b |
換頁符 |
/f |
換行符 |
/n |
回車符 |
/r |
Tab 符 |
/t |
垂直 Tab 符 |
/v |
使用數字指定的Unicode 字元,如/u2000 |
/u |
使用十六進位制數指定的Unicode 字元,如/xc8 |
/x |
空值 |
/0 (zero |
第二種方法是使用’逐字字串’文字。這種方法將想要得到的字串放在@”和”之間。假如我們需要將C:/My Documents/賦值給’path’,我們可以使用轉義序列方法:string path = "C://My Documents//";也可以使用如下的語句:string path = @"C:/MyDocuments/"。
通過使用後一種方法得到的字串還可以橫跨多行而不需要使用’/n’。使用這種方法唯一需要使用到轉義序列的字串是”,其轉義字元為””(兩個連在一起的雙引號)。例如想將the word "big" contains three letters.賦值給’text’,我們就可以使用如下的語句:string text = @"the word ""big"" contains three letters."。
裝箱
C#允許你將任何的值型別轉換為對應的引用型別,也可以將得到的引用型別再轉換為值型別。下面的程式碼演示了這種轉換――裝箱:
int i = 123;
object box = i;
if (box is int)
{Console.Write("Box contains an int");} // 這一行將會打印出來。
當第2行的程式碼執行後,物件’box’將被初始化,i儲存的值將被複制給這個物件。值得注意的一件趣事是box執行時的型別將是被裝箱的值型別(本例中即為int)。’is’操作符將返回box的型別為int。