C Sharp/數據類型
C#是靜態類型語言。即每個變量和常量都有固定的類型。類型分為值類型與引用類型兩大類。值類型變量存放在棧上;引用類型在堆中分配空間。在.Net內建類型外,用戶也可以定製類型。
值類型
[編輯]- 函數參數傳值。
- 值類型沒有/不需要調用構造函數。總是自動初始化。
- 值類型的字段初始化為0或null.
- 值類型不能賦值為null值,但可用Nullable類型
- 值類型可以裝箱。
- 存儲在棧中。聲明一個值類型變量,則立即分配內存。如果變量出了作用域,則自動銷毀。
- 值類型包括簡單類型、枚舉類型和結構類型。
struct
[編輯]用戶自定義的值類型:
struct Person
{
public string Name;
public int Height;
public string Occupation;
}
public class StructWikiBookSample2
{
public static void Main()
{
Person john = new Person { Name = "John", Height = 182, Occupation = "Programmer" };
}
}
struct是值類型的容器。可以有構造函數、方法、實現接口。但不支持繼承。總是有一個缺省構造函數。作為函數參數時傳值。
從性能角度,建議struct不超過16個字節。
枚舉
[編輯]enums是代表整數的命名的值:
enum Season
{
Winter = 0,
Spring = 1,
Summer = 2,
Autumn = 3,
Fall = Autumn // Autumn is called Fall in American English.
}
enum變量默認初始化為0:
Season season;
season = Season.Spring;
enum類型是整數值,允許加減運算。但乘除運算需要顯式cast。枚舉類型和整型的相互轉換,也需要顯式cast。
season = (Season)2; // cast 2 to an enum-value of type Season.
season = season + 1; // Adds 1 to the value.
season = season + season2; // Adding the values of two enum variables.
int value = (int)season; // Casting enum-value to integer value.
season++; // Season.Spring (1) becomes Season.Summer (2).
season--; // Season.Summer (2) becomes Season.Spring (1).
枚舉類型可以按位或操作:
Color myColors = Color.Green | Color.Yellow | Color.Blue;
裝箱和拆箱
[編輯]裝箱(Boxing)就是從值類型到引用類型的轉換。[1]裝箱在C#是隱式的。拆箱(unboxing)是從引用類型到值類型的轉換。拆箱需要顯式的類型cast。
class Test
{
static void Mian()
{
int i = 3;
object a = i;//装箱
int j = (int)a;//拆箱
}
}
可空類型
[編輯]可空類型(nullable type) T?表示該類型還可以取空值。例如:
int? a=null;
任何可空值類型T?是泛型結構System.Nullable<T> 的實例。System.Nullable<T>有兩個只讀屬性:Value與HasValue。
C# 2.0引入了可空類型。允許值類型為null(用於適配數據庫)。
int? n = 2;
n = null;
Console.WriteLine(n.HasValue);
實際上這是用Nullable<T> struct實現的:
Nullable<int> n = 2;
n = null;
Console.WriteLine(n.HasValue);
從可空值類型轉化為基礎值類型,可用空值結合運算符??或者System.Nullable<T>.GetValueOrDefault()。例如:
int? c = null; int d = c ?? -1; Console.WriteLine($"d is {d}"); // output: d is -1
引用類型
[編輯]引用類型的變量指向堆中的一塊空間。為受管的引用。構造函數調用時,在堆上分配創建一個對象,並把它賦給變量。CLR執行下述4步:
- CLR計算堆中需要的內存;
- CLR在堆中插入數據;
- CLR標記內存空間中的佔用結尾標記;
- CLR返回新創建空間的引用
當引用類型的變量出了作用域,則引用失效(broken)。如果一個對象沒有引用,則被標記為垃圾。垃圾回收器隨後會收集、摧毀它。
引用變量如果為null,則它不引用任何對象。
引用類型包括:
- 接口
- 類
- 裝箱類型
- 委託
- 自定義類
- 數組
- 指針
說明:儘管string是類,但如果用到了相等運算符==或者!=時則表示比較string對象的值。
定製類型
[編輯]- 定製的值類型使用struct或enum關鍵字。
- 定製的引用類型使用class關鍵字。
.NET框架中對應的值類型
[編輯]// C#
public void UsingCSharpTypeAlias()
{
int i = 42;
}
public void EquivalentCodeWithoutAlias()
{
System.Int32 i = 42;
}
' Visual Basic .NET
Public Sub UsingVisualBasicTypeAlias()
Dim i As Integer = 42
End Sub
Public Sub EquivalentCodeWithoutAlias()
Dim i As System.Int32 = 42
End Sub
值類型的長度是固定的。引用類型都是繼承自object
類,其長度隨平台而定。
.NET框架的類型有成員方法,如:
int i = 97;
string s = i.ToString(); // The value of s is now the string "97".
System.Int32
類型實現了Parse()
方法:
string s = "97";
int i = int.Parse(s); // The value of i is now the integer 97.
值類型可以裝箱,然後可以拆箱:
object boxedInteger = 97;
int unboxedInteger = (int) boxedInteger;
裝箱與拆箱不是類型安全的。編譯器不會產生報錯,彈運行時可能爆異常:
object getInteger = "97";
int anInteger = (int) getInteger; // No compile-time error. The program will crash, however.
C#內建類型與對應的.NET框架類型列表:
整型
[編輯]C# 別名 | .NET 類型 | 比特長度 | 值域 |
---|---|---|---|
sbyte |
System.SByte | 8 | -128 to 127 |
byte |
System.Byte | 8 | 0 to 255 |
short |
System.Int16 | 16 | -32,768 to 32,767 |
ushort |
System.UInt16 | 16 | 0 to 65,535 |
char |
System.Char | 16 | A unicode character of code 0 to 65,535 |
int |
System.Int32 | 32 | -2,147,483,648 to 2,147,483,647 |
uint |
System.UInt32 | 32 | 0 to 4,294,967,295 |
long |
System.Int64 | 64 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
ulong |
System.UInt64 | 64 | 0 to 18,446,744,073,709,551,615 |
整型字面量後綴(不區分大小寫)u、l、ul,分別表示:uint、long、ulong。
字符字面量用單引號圍起來。內容可以是轉義序列,可以用\x前綴後跟1至4個十六進制數字,或者\u後跟4個十六進制數字。
整型字面量只允許十進制或者十六進制(0x前綴)。
浮點和定點
[編輯]C# 別名 | .NET類型 | 比特長度 | 精度 | 值域 |
---|---|---|---|---|
float |
System.Single | 32 | 7 digits | 1.5 x 10-45 to 3.4 x 1038 |
double |
System.Double | 64 | 15-16 digits | 5.0 x 10-324 to 1.7 x 10308 |
decimal |
System.Decimal | 128 | 28-29 decimal places | 1.0 x 10-28 to 7.9 x 1028 |
字面量後綴(不區分大小寫)m、d、f,分別表示:decimal、double、float。
浮點字面量或者是帶小數點的定點表示,如3.14;或者是科學計數法。
整型將被隱式轉換為 decimal 類型。浮點型和 decimal 類型之間不存在隱式轉換;因此,必須使用強制轉換以在這兩個類型之間轉換。可以在同一表達式中混合使用 decimal 和數值整型。但是,不進行強制轉換就混合使用 decimal 和浮點型將導致編譯錯誤。通過使用 String.Format 方法或Console.Write 方法(其調用String.Format())來設置結果的格式。 貨幣格式是使用標準貨幣格式字符串「C」或「c」指定的
在CLR中,Decimal類型不是基元類型。這就意味着CLR沒有知道如何處理Decimal的IL指令。
其他預定義類型
[編輯]C# 類型 | .NET 類型 | 比特長度 | 值域 |
---|---|---|---|
bool |
System.Boolean | 32 | true or false, which aren't related to any integer in C#. |
object |
System.Object | 32/64 | Platform dependent (a pointer to an object). |
string |
System.String | 16*length | A unicode string with no special upper bound. |
字符串類型
[編輯]string是System.String的別名。System.String不是一個struct因此不是基本類型。
string表示一個不可變的unicode字符序列。string可包含多個\x0字符。
System.StringBuilder類可用做「可變的」字符串。
var sb = new StringBuilder();
sb.Append('H');
sb.Append("el");
sb.AppendLine("lo!");
// Declare without initializing.
string message1;
// Initialize to null.
string message2 = null;
// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;
// Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";
// Initialize with a verbatim string literal.
string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0";
// Use System.String if you prefer.
System.String greeting = "Hello World!";
// In local variables (i.e. within a method body)
// you can use implicit typing.
var temp = "I'm still a strongly-typed System.String!";
// Use a const string to prevent 'message4' from
// being used to store another string value.
const string message4 = "You can't get rid of me!";
// Use the String constructor only when creating
// a string from a char*, char[], or sbyte*. See
// System.String documentation for details.
char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);//不要使用new运算符创建string对象,除非用字符数组初始化
string.IsNullOrEmpty(message4);//为避免NullReferenceException
//String.Format利用占位符来构造格式字符串。如:
var pw = (firstName: "Phillis", lastName: "Wheatley", born: 1753, published: 1773);
Console.WriteLine("{0} {1} was an African American poet born in {2}.", pw.firstName, pw.lastName, pw.born);
字符串對象是immutable
字符串字面量:
- 用引號的字符串字面量(Quoted string literals)
- 逐字的字符串字面量(Verbatim string literals):用@符號作為前綴。更適合多行、包含反斜線或嵌入的雙引號(用兩個雙引號表示)等情形。
- 原始字符串字面量(Raw string literals):從C#11.0開始,以連續3個雙引號開始和結束的字符串。對於多行情形,要求開始和結束的3個連續雙引號必須各自獨佔一行,該行前綴之後、後綴之前的字符都被忽略。
轉義字符序列需要注意:\u、\U、\x分別開始4位、8位、0至4位的16進制數字序列,對應Unicode的碼位。
C#6開始支持格式化(插值)字符串以$號為前綴,插值表達式寫在大括號中。是String.Format方法的簡化形式。語法為:
{<interpolatedExpression>[,<alignment>][:<formatString>]}
從C#10.0開始,可以用插值字符串初始化constant 字符串。從C#11.0開始,原始字符串中也可用插值字符串,並在原始字符串的前綴之前的$號的個數來表示插值表達式用多少層大括號包圍。逐字字符串字面量如果用$@ 或 @$作為前綴也可以使用插值表達式。
空字符串對象是String.Empty,即""對應的0長度字符串對象。null字符串不是指引到System.String的一個對象,調用其方法可引發NullReferenceException. 但可以用null字符串與其他字符串連接或比較操作。
方法String.Join可以把數組的多個元素連接為一個字符串並用指定的分隔符隔開。
StringBuilder類用於創建一個字符緩衝區,在此上可以逐個字符的修改、追加。適用於連續追加上百次操作的情形。
接口
[編輯]使用interface關鍵字聲明接口。它類似於類聲明。默認情況下,接口語句是public的。例如:
public interface ITransactions {
// interface members
void showTransaction();
double getAmount();
}
類
[編輯]裝箱類型
[編輯]委託
[編輯]委託和事件的變量,可以看作是一個或多個面向對象的函數指針綁定到一個變量上。
delegate void MouseEventHandler(object sender, MouseEventArgs e);
public class Button : System.Windows.Controls.Control
{
private event MouseEventHandler _onClick;
/* Imaginary trigger function */
void Click()
{
_onClick(this, new MouseEventArgs(data));
}
}
類中聲明的事件,只能在作為事件的擁有者的該類中才能調用。
public class MainWindow : System.Windows.Controls.Window
{
private Button _button1;
public MainWindow()
{
_button1 = new Button();
_button1.Text = "Click me!";
/* Subscribe to the event */
_button1.ClickEvent += Button1_OnClick;
/* Alternate syntax that is considered old:
_button1.MouseClick += new MouseEventHandler(Button1_OnClick); */
}
protected void Button1_OnClick(object sender, MouseEventArgs e)
{
MessageBox.Show("Clicked!");
}
}
也可以做定製事件實現:
private EventHandler _clickHandles = (s, e) => { };
public event EventHandler Click
{
add
{
// Some code to run when handler is added...
...
_clickHandles += value;
}
remove
{
// Some code to run when handler is removed...
...
_clickHandles -= value;
}
}
自定義類
[編輯]數組
[編輯]數組的元素具有相同的類型。數組類型的基類型是System.Array。
數組每個維度的長度可以不聲明:
string[] a_str;
給數組變量賦值時,要指明每個維度的長度:
a_str = new string[5];
聲明與初始化可以寫在一起:
string[] a_str = new string[5];
數組是傳引用。下例中數組的兩個元素的內容交換了:
static void swap (int[] a_iArray, int iI, int iJ)
{
int iTemp = a_iArray[iI];
a_iArray[iI] = a_iArray[iJ];
a_iArray[iJ] = iTemp;
}
C#的數組對應於C語言的動態數組。在運行時確定數組長度:
int[] numbers = new int[2];
numbers[0] = 2;
numbers[1] = 5;
int x = numbers[0];
數組初始化器
[編輯]// Long syntax
int[] numbers = new int[5]{ 20, 1, 42, 15, 34 };
// Short syntax
int[] numbers2 = { 20, 1, 42, 15, 34 };
// Inferred syntax
var numbers3 = new[] { 20, 1, 42, 15, 34 };
多維數組
[編輯]int[,] numbers = new int[3, 3];
numbers[1,2] = 2;
int[,] numbers2 = new int[3, 3] { {2, 3, 2}, {1, 2, 6}, {2, 4, 5} };
指針
[編輯]C#允許在「unsafe」上下文中像C語言那樣使用指針,不做運行時安全檢查。只有如下類型可以使用指針:一些基本類型, enums, string, pointer, 只包含了允許類型的arrays和struct。[2]
static void Main(string[] args)
{
unsafe
{
int a = 2;
int* b = &a;
Console.WriteLine("Address of a: {0}. Value: {1}", (int)&a, a);
Console.WriteLine("Address of b: {0}. Value: {1}. Value of *b: {2}", (int)&b, (int)b, *b);
// Will output something like:
// Address of a: 71953600. Value: 2
// Address of b: 71953596. Value: 71953600. Value of *b: 2
}
}
Struct只能是純結構,不能有受管的引用類型,如string或其他class:
public struct MyStruct
{
public char Character;
public int Integer;
}
public struct MyContainerStruct
{
public byte Byte;
public MyStruct MyStruct;
}
使用:
MyContainerStruct x;
MyContainerStruct* ptr = &x;
byte value = ptr->Byte;
在同一個聲明中聲明多個指針時,星號 * 僅與基礎類型一起寫入;而不是用作每個指針名稱的前綴。 例如:
int* p1, p2, p3; // 正确
int *p1, *p2, *p3; // 错误
c#中指向堆內存的指針必須在unsaved和fixed的上下文中使用。獲取數組空間的指針,需要使用fixed關鍵字:
int[] a = { 1, 2, 3 };
unsafe
{
fixed (int* p = a) {
//do something...
};
}
Dynamic
[編輯]C Sharp 4.0和w:.NET Framework 4.0引入了dynamic關鍵字,用於運行時動態確定類型。dynamic被編譯後是一個Object類型,編譯器編譯時不會對dynamic進行類型檢查。
dynamic x = new Foo();
x.DoSomething(); // Will compile and resolved at runtime. An exception will be thrown if invalid.
匿名類型
[編輯]C# 3.0引入匿名類型。是靜態編譯時的語法糖。用於表示複雜的數據類型,特別是匿名函數或LINQ查詢。
var carl = new { Name = "Carl", Age = 35 }; // Name of the type is only known by the compiler.
var mary = new { Name = "Mary", Age = 22 }; // Same type as the expression above
參考文獻
[編輯]- ↑ Archer, Part 2, Chapter 4:The Type System
- ↑ Pointer types (C# Programming Guide), http://msdn.microsoft.com/en-us/library/y31yhkeb.aspx