在之前的文章中(地址: "https://www.vinanysoft.com/c sharp basics/introducing/" ),以 HelloWorld 程式為基礎,介紹 C 語言、它的結構、基本語法以及如何編寫最簡單的程式有了初步理解。 接下來介紹基本的 C 類型,繼續鞏固 C 的基 ...
在之前的文章中(地址:https://www.vinanysoft.com/c-sharp-basics/introducing/),以 HelloWorld 程式為基礎,介紹 C# 語言、它的結構、基本語法以及如何編寫最簡單的程式有了初步理解。
接下來介紹基本的 C# 類型,繼續鞏固 C# 的基礎知識。本系列文章到目前為止只用過少量的內置數據類型,而且只是一筆帶過。在 C# 中有大量的類型,而且可以通過合併類型來創建新類型。
但 C# 有幾種類型非常簡單,是其他所有類型的基礎,它們稱為預定義類型(predefined type)或基元類型( primitive type)。
C# 提供了 16 種預定義類型,如下圖所示。其中包括 13 種簡單類型和 3 種非簡單類型。
所有預定義類型的名稱都由全小寫的字母組成。預定義的簡單類型包括以下 3 種。
- 11 種數值類型。
- 不同長度的有符號和無符號整數類型。
- 用於科學計算的二進位浮點類型
float
和double
。 - 一種用於金融計算的十進位高精度浮點類型
decimal
。與float
和double
不同,decimal
類型可以準確地表示分數。decimal
類型常用於貨幣的計算。
- 一種
Unicode
字元類型char
。 - 一種布爾類型
bool
。bool
類型表示布爾值並且必須為true
或false
。
非簡單類型如下:
object
,它是所有其他類型的基類。string
,它是一個Unicode
字元數組。dynamic
,使用動態語言編寫的程式集時使用。
所有預定義類型都直接映射到底層的 .NET 類型。C# 的類型名稱就是 .NET 類型的別名,所以使用 .NET 的類型名稱也能很好地符合 C# 語法,不過並不鼓勵這樣做。在 C# 程式中,應該儘量使用 C# 類型名稱而不是 .NET類型名稱。
整數類型
C# 有八種整數類型,可選擇最恰當的一種來存儲數據以避免浪費資源。下表列出了 C# 支持的整型類型:
C# 類型/關鍵字 | 範圍 | 大小 | .NET 類型 |
---|---|---|---|
sbyte | -128 到 127 | 8 位帶符號整數 | System.SByte |
byte | 0 到 255 | 無符號的 8 位整數 | System.Byte |
short | -32,768 到 32,767 | 有符號 16 位整數 | System.Int16 |
ushort | 0 到 65,535 | 無符號 16 位整數 | System.UInt16 |
int | -2,147,483,648 到 2,147,483,647 | 帶符號的 32 位整數 | System.Int32 |
uint | 0 到 4,294,967,295 | 無符號的 32 位整數 | System.UInt32 |
long | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | 64 位帶符號整數 | System.Int64 |
ulong | 0 到 18,446,744,073,709,551,615 | 無符號 64 位整數 | System.UInt64 |
C# 所有基元類型都有短名稱(最左列)和完整名稱(最右列)。完整名稱對應 BCL(基類庫)中的類型名稱。
由於基元數據類型是其他類型的基礎,所以 C# 為基元數據類型的完整名稱提供了短名稱(或稱為縮寫)。其實從編譯器的角度看,兩種名稱完全一樣,最終都生成相同的代碼。事實上,檢查最終生成的 CIL 代碼,根本看不出源代碼具體使用的名稱。
例如,以下聲明聲明瞭相同類型的變數:
int a = 123;
System.Int32 b = 123;
C# 支持完整 BCL 名稱和關鍵字,造成開發人員犯難在什麼時候用什麼。不要時而用這個,時而用那個,最好堅持用一種。
C# 開發人員一般用 C# 關鍵字。例如,用 int
而不是 System.Int32
,用 string
而不是 System.String
(甚至不要用 String
這種簡化形式)。
浮點類型(float
和 double
)
C# 支持以下預定義浮點類型:
C# 類型/關鍵字 | 大致範圍 | 精度 | 大小 | .NET 類型 |
---|---|---|---|---|
float | ±1.5 x 10−45 至 ±3.4 x 1038 | 大約 6-9 位數字 | 4 個位元組 | System.Single |
double | ±5.0 × 10−324 到 ±1.7 × 10308 | 大約 15-17 位數字 | 8 個位元組 | System.Double |
在上表中,最左側列中的每個 C# 類型關鍵字都是相應 .NET 類型的別名。 它們是可互換的。 例如,以下聲明聲明瞭相同類型的變數:
double a = 12.3;
System.Double b = 12.3;
decimal
類型
C# 類型/關鍵字 | 大致範圍 | 精度 | 大小 | .NET 類型 |
---|---|---|---|---|
decimal | ±1.0 x 10-28 至 ±7.9228 x 1028 | 28-29 位 | 16 個位元組 | System.Decimal |
與 float
和 double
相比,decimal
類型具有更高的精度和更小的範圍,因此它適合於財務和貨幣計算。
浮點數舍入誤差
float
和 double
在內部都是基於 2 來表示數值的。因此只有基於 2 表示的數值才能夠精確表示。事實上,這意味著大多數有小數部分的字面量(它們都基於 10)將無法精確表示。例如:
float f1 = 1F;
float f2 = 0.9F;
Console.WriteLine(f1 - f2);
double d1 = 1D;
double d2 = 0.9D;
Console.WriteLine(d1 - d2);
decimal decimal1 = 1M;
decimal decimal2 = 0.9M;
Console.WriteLine(decimal1 - decimal2);
輸出
0.100000024
0.09999999999999998
0.1
這就是為什麼 float
和 double
不適合金融計算。相反,decimal
基於 10,它能夠精確表示基於 10 的數值(也包括它的因數,基於 2 和基於 5 的數值)。因為實數的字面量都是基於 10 的,所以 decimal
能夠精確表示像 0.1
這樣的數。然而,float
、double
和 decimal
都不能精確表示那些基於 10 的迴圈小數:
decimal m = 1M / 6M;
Console.WriteLine(m);
decimal m2 = m + m + m + m + m + m;
Console.WriteLine(m2);
Console.WriteLine();
double d = 1d / 6d;
Console.WriteLine(d);
double d2 = d + d + d + d + d + d;
Console.WriteLine(d2);
輸出
0.1666666666666666666666666667
1.0000000000000000000000000002
0.16666666666666666
0.9999999999999999
字面量
字面量(literal value)表示源代碼中的固定值。
Console.WriteLine(123);
Console.WriteLine(456.789);
預設情況下,輸入帶小數點的字面量,編譯器自動把它解釋成 double
類型。 如果輸入的是整數值(沒有小數點)通常預設為 int
, 如果值太大,以至於無法用 int
來存儲。編譯器會把它解釋成 long
。
static void Main(string[] args)
{
var expectIsInt = 123;
var expectIsLong = 9223372036854775807;
var expectIsDouble = 3.14;
Console.WriteLine($"expectIsInt 的類型是:{expectIsInt.GetType().Name};" +
$"expectIsLong 的類型是:{expectIsLong.GetType().Name};" +
$"expectIsDouble 的類型是:{expectIsDouble.GetType().Name}");
}
輸出
expectIsInt 的類型是:Int32;expectIsLong 的類型是:Int64;expectIsDouble 的類型是:Double
由於帶小數點的值預設為 double
類型,所以下麵輸出的結果中,超過可容納的精度部分會被丟棄。
static void Main(string[] args)
{
Console.WriteLine(5.141231231234567898765);
}
輸出
5.141231231234568
要顯示具有完整精度的數字,必須將字面量顯式聲明為 decimal
類型,通過追加一個 M
(或者 m
)來實現。
static void Main(string[] args)
{
Console.WriteLine(5.141231231234567898765M);
}
輸出
5.141231231234567898765
還可以使用 F
和 D
作為尾碼,將字面量分別顯式聲明為 float
或者 double
。 對於整數數據類型,整數字面量的類型是像下麵這樣確定的:
- 如果整數字面量沒有尾碼,則其類型為以下類型中可表示其值的第一個類型:
int
、uint
、long
、ulong
。 - 如果整數字面量以
U
或u
為尾碼,則其類型為以下類型中可表示其值的第一個類型:uint
、ulong
。 如果整數字面量以
L
或l
為尾碼,則其類型為以下類型中可表示其值的第一個類型:long
、ulong
。備註:可以使用小寫字母
l
作為尾碼。 但是,這會生成一個編譯器警告,因為字母l
可能與數字1
混淆。 為清楚起見,請使用L
。如果整數字面量的尾碼為
UL
、Ul
、uL
、ul
、LU
、Lu
、lU
或lu
,則其類型為ulong
。
字面量的尾碼不區分大小寫。但一般推薦大寫,避免出現小寫字母 l
和數字 1
不好區分的情況。
從 C# 7.0 開始提供支持將 _
用作數字分隔符,可以將數字分隔符用於所有類型的數字文本。
double d = 3D;
d = 4d;
d = 3.934_001;
float f = 3_000.5F;
f = 5.4f;
decimal myMoney = 3_000.5m;
myMoney = 400.75M;
C# 支持使用科學記數法,指數記數法要求使用 e
或 E
中綴,在中綴字母後面添加正整數或者負整數,併在字面量最後添加恰當的數據類型尾碼。
double d = 0.42e2;
Console.WriteLine(d); // output 42;
float f = 134.45E-2f;
Console.WriteLine(f); // output: 1.3445
decimal m = 1.5E6m;
Console.WriteLine(m); // output: 1500000
使用 0x
或 0X
首碼表示十六進位計數法,在 C# 7.0 和更高版本中使用 0b
或 0B
首碼表示二進位計數法。
var hexLiteral = 0x2A;
var binaryLiteral = 0b_0010_1010;
總結
C# 語言的基元類型包括八種整數類型、兩種用於科學計算的二進位浮點類型、一種用於金融計算的十進位浮點類型。浮點型存在舍入誤差,使用的時候要註意。