Visual C# 語言概念資料類型(C# 與 Java)
本主題討論 Java 和 C# 在資料的表示、分配和記憶體回收等方面的一些主要相同點和不同點。
複合資料型別
在 Java 和 C# 中,類作為有欄位、方法和事件的複合資料型別這一概念是相似的。(有關類繼承的概念在名為繼承與衍生類別(C# 與 Java)的主題中單獨討論。)C# 引入結構的概念,結構是一種堆棧分配的複合資料型別,它不支援繼承。在其他許多方面,結構與類非常相似。結構提供一種將相關欄位和方法組合在一起的輕量方法,以便在緊湊迴圈和其他效能關鍵的方案中使用。
C# 使您能夠建立一個在對類的執行個體進行記憶體回收前調用的解構函式方法。在 Java 中,可以使用 finalize 方法來包含代碼,用於在將對象作為記憶體回收前清理資源。在 C# 中,此功能由類解構函式執行。解構函式就像是沒有參數和前面不帶顎化符 (~) 的建構函式。
內建資料類型
C# 提供 Java 中可用的所有資料類型,並增加了對無符號數字和新的 128 位高精度浮點類型的支援。
核心類庫為 Java 中的每個基中繼資料類型提供了一個封裝類,此封裝類將基中繼資料類型表示為 Java 對象。例如,Int32 類封裝 int 資料類型,Double 類封裝 double 資料類型。
另一方面,C# 中的所有基中繼資料類型都是 System 命名空間中的對象。對於每個資料類型,提供了一個簡稱(或別名)。例如,int 是 System.Int32 的簡稱,而 double 是 System.Double 的簡寫。
下表提供了 C# 資料類型列表及其別名。如表所示,前八個資料類型對應於 Java 中可用的基元類型。但請注意,Java 的 boolean 在 C# 中稱為 bool。
簡稱 |
.NET 類 |
類型 |
寬度 |
範圍(位) |
byte |
Byte |
不帶正負號的整數 |
8 |
0 到 255 |
sbyte |
SByte |
有符號整數 |
8 |
-128 到 127 |
int |
Int32 |
有符號整數 |
32 |
-2,147,483,648 到 2,147,483,647 |
uint |
UInt32 |
不帶正負號的整數 |
32 |
0 到 4294967295 |
short |
Int16 |
有符號整數 |
16 |
-32,768 到 32,767 |
ushort |
UInt16 |
不帶正負號的整數 |
16 |
0 到 65535 |
long |
Int64 |
有符號整數 |
64 |
-922337203685477508 到 922337203685477507 |
ulong |
UInt64 |
不帶正負號的整數 |
64 |
0 到 18446744073709551615 |
float |
Single |
單精確度浮點型 |
32 |
-3.402823e38 至 3.402823e38 |
double |
Double |
雙精確度浮點型 |
64 |
-1.79769313486232e308 至 1.79769313486232e308 |
char |
Char |
單 Unicode 字元 |
16 |
文本中使用的 Unicode 符號 |
bool |
Boolean |
邏輯布爾實值型別 |
8 |
True 或 False |
object |
Object |
所有其他類型的基底類型 |
|
|
string |
String |
字元序列 |
|
|
decimal |
Decimal |
精確小數類型或整型,可以表示帶有 29 個有效位的十進位數 |
128 |
±1.0 × 10e−28 至 ±7.9 × 10e28 |
因為 C# 將所有基中繼資料類型當作對象表示,所以可以在基中繼資料類型上調用對象方法。例如:
C# 複製代碼
static void Main()
{
int i = 10;
object o = i;
System.Console.WriteLine(o.ToString());
}
藉助自動裝箱和unboxing完成此操作。有關更多資訊,請參見裝箱和unboxing(C# 編程指南)。
常數
Java 和 C# 均能夠聲明這樣一個變數:它的值在編譯時間指定,在運行時不能更改。Java 使用 final 欄位修飾符聲明此類變數,而 C# 則使用 const 關鍵字。除了 const 以外,C# 還提供 readonly 關鍵字以聲明可以在運行時進行一次賦值(在聲明語句中或在建構函式中)的變數。初始化以後,readonly 變數的值不能更改。當已單獨編譯的模組需要共用版本號碼等資料時,可以使用 readonly 變數。如果模組 A 更新了,並使用一個新的版本號碼進行了重新編譯,則模組 B 可以用這個新的常數值進行初始化,而無需重新編譯。
枚舉
枚舉用於對已命名常數進行分組,與在 C 和 C++ 中的用法相似,但不可用於 Java。下面的樣本定義一個簡單的 Color 枚舉。
C# 複製代碼
public enum Color
{
Green, //defaults to 0
Orange, //defaults to 1
Red, //defaults to 2
Blue //defaults to 3
}
也可以將整數值分配給枚舉,如下面的枚舉聲明所示:
C# 複製代碼
public enum Color2
{
Green = 10,
Orange = 20,
Red = 30,
Blue = 40
}
下面的程式碼範例調用 Enum 類型的 GetNames 方法來顯示枚舉的可用常數。然後,將值分配給枚舉,並顯示此值。
C# 複製代碼
class TestEnums
{
static void Main()
{
System.Console.WriteLine("Possible color choices: ");
//Enum.GetNames returns a string array of named constants for the enum.
foreach(string s in System.Enum.GetNames(typeof(Color)))
{
System.Console.WriteLine(s);
}
Color favorite = Color.Blue;
System.Console.WriteLine("Favorite Color is {0}", favorite);
System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
}
}
輸出
Possible color choices:
Green
Orange
Red
Blue
Favorite Color is Blue
Favorite Color value is 3
字串
Java
和 C#
中的字串類型的行為相似,只有細微的差異。兩種字串類型都是不可變的,意味著一旦建立了字串,字串的值就無法更改。兩個執行個體中的方法看上去修改了
字串的實際內容,實際上建立並返回了一個新字串,而原始字串保持不變。C# 和 Java 中比較字串值的過程有所不同。若要在 Java
中比較字串值,則開發人員需要在字串類型上調用 equals 方法,原因是預設情況下 == 運算子會比較參考型別。在 C# 中,開發人員可以直接使用 == 或 != 運算子來比較字串值。儘管在 C# 中字串是參考型別,但在預設情況下 == 和 != 運算子將比較字串值而不是引用。
和在 Java 中一樣,C# 開發人員不應使用字串類型來串聯字串,以避免在每次串聯字串時建立新字串類所產生的開銷。相反,開發人員可以使用 StringBuilder 類,它與 Java 的 StringBuffer 類在功能上等效。
字串文本
C# 能夠避免在字串常數中使用逸出序列,如用於定位字元的 "\t" 或用於反斜線字元的 "\"。若要達到此目的,只需在分配字串值之前使用 @ 標記法宣告原義字串。下面的樣本示範如何使用逸出字元以及如何分配字串文本:
C# 複製代碼
static void Main()
{
//Using escaped characters:
string path1 = "\\\\FileShare\\Directory\\file.txt";
System.Console.WriteLine(path1);
//Using String Literals:
string path2 = @"\\FileShare\Directory\file.txt";
System.Console.WriteLine(path2);
}
轉換和強制轉換
Java 和 C# 對資料類型的自動轉換和強制轉換遵循相似的規則。
和 Java 一樣,C# 支援隱式和顯式類型轉換。如果是擴大轉換,則為隱式轉換。例如,下面從 int 到 long 的轉換為隱式轉換,與在 Java 中相同:
C# 複製代碼
int int1 = 5;
long long1 = int1; //implicit conversion
下面是 .NET Framework 資料類型之間的隱式轉換列表:
源類型 |
目標類型 |
Byte |
short、ushort、int、uint、long、ulong、float、double 或 decimal |
Sbyte |
short、int、long、float、double 或 decimal |
Int |
long、float、double 或 decimal |
Uint |
long、ulong、float、double 或 decimal |
Short |
int、long、float、double 或 decimal |
Ushort |
int、uint、long、ulong、float、double 或 decimal |
Long |
float、double 或 decimal |
Ulong |
float、double 或 decimal |
Float |
double |
Char |
ushort、int、uint、long、ulong、float、double 或 decimal |
使用與 Java 相同的文法強制轉換要顯式轉換的運算式:
C# 複製代碼
long long2 = 5483;
int int2 = (int)long2; //explicit conversion
下表列出了顯式轉換。
源類型 |
目標類型 |
Byte |
sbyte 或 char |
Sbyte |
byte、ushort、uint、ulong 或 char |
Int |
sbyte、byte、short、ushort、uint、ulong 或 char |
Uint |
sbyte、byte、short、ushort、int 或 char |
Short |
sbyte、byte、ushort、uint、ulong 或 char |
Ushort |
sbyte、byte、short 或 char |
Long |
sbyte、byte、short、ushort、int、uint、ulong 或 char |
Ulong |
sbyte、byte、short、ushort、int、uint、long 或 char |
Float |
sbyte、byte、short、ushort、int、uint、long、ulong、char 或 decimal |
Double |
sbyte、byte、short、ushort、int、uint、long、ulong、char、float 或 decimal |
Char |
sbyte、byte 或 short |
Decimal |
sbyte、byte、short、ushort、int、uint、long、ulong、char、float 或 double |
值與參考型別
C# 支援兩種變數類型:
如果建立兩個實值型別變數 i 和 j(如下所示),則 i 和 j 完全相互獨立:
C# 複製代碼
int i = 10;
int j = 20;
為它們指定了獨立的記憶體位置:
如果更改這兩個變數中其中一個的值,另一個變數當然不受影響。例如,如果具有如下形式的運算式,則這兩個變數之間仍然沒有關聯:
C# 複製代碼
int k = i;
也就是說,如果更改 i 的值,則 k 將保留賦值時 i 具有的值。
C# 複製代碼
i = 30;
System.Console.WriteLine(i.ToString()); // 30
System.Console.WriteLine(k.ToString()); // 10
但是,參考型別的行為則不同。例如,可以聲明如下所示的兩個變數:
C# 複製代碼
Employee ee1 = new Employee();
Employee ee2 = ee1;
現在,因為類在 C# 中為參考型別,所以 ee1 被視為對 Employee 的引用。在前面的兩行中,第一行在記憶體中建立 Employee 的一個執行個體,並設定 ee1 以引用該執行個體。因此,將 ee2 設定為等於 ee1 時,它包含了對記憶體中的類的重複引用。如果現在更改 ee2 上的屬性,則 ee1 上的屬性將反映這些更改,因為兩者都指向記憶體中的相同對象,如下所示:
裝箱和unboxing
將實值型別轉換為參考型別的過程稱為裝箱。反之,將參考型別轉換為實值型別的過程則稱為unboxing。下面的代碼說明了這一點:
C# 複製代碼
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
Java 要求手動執行這種轉換。可以通過構造這種對象或裝箱,將基中繼資料類型轉換為封裝類的對象。同樣地,可以通過在這種對象上調用合適的方法或unboxing,從封裝類對象中提取基中繼資料類型的值。有關裝箱和unboxing的更多資訊,請參見裝箱轉換(C# 編程指南)和unboxing轉換(C# 編程指南)。
請參見參考
資料類型(C# 編程指南)
概念
C# 編程指南
其他資源
Visual C#
C# 程式設計語言(針對 Java 開發人員)