《果殼中的C# C# 5.0 權威指南》 [作者] (美) Joseph Albahari (美) Ben Albahari[譯者] (中) 陳昇 管學理 曾少寧 楊慶川[出版] 中國水利水電出版社[版次] 2013年08月 第1版[印次] 2013年08月 第1次 印刷[定價] 118.00元 【 ...
《果殼中的C# C# 5.0 權威指南》
========== ========== ==========
[作者] (美) Joseph Albahari (美) Ben Albahari
[譯者] (中) 陳昇 管學理 曾少寧 楊慶川
[出版] 中國水利水電出版社
[版次] 2013年08月 第1版
[印次] 2013年08月 第1次 印刷
[定價] 118.00元
========== ========== ==========
【前言】
C# 5.0 是微軟旗艦編程語言的第4次重大升級。
C# 5.0 及相關 Framework 的新特性已經被標註清楚,因此也可以將本書作為 C# 4.0 參考書使用。
【第01章】
(P001)
C# 在面向對象方面的特性包括:
1. 統一的類型系統 —— C# 中的基礎構建塊是一種被稱為類型的數據與函數的封裝單元。C# 有一個統一的類型系統,其中所有類型最終都共用一個公共的基類。這意味著所有的類型,不管它們是表示業務對象,或者像數字等基本類型,都共用相同的基本功能集;
2. 類與介面 —— 在純粹的的面向對象泛型中,唯一的類型就是類。但是 C# 中還有其他幾種類型,其中一種是介面。介面與類相似,但它只是某種類型的定義,而不是實現。在需要用多繼承時,它是非常有用的;
3. 方法、屬性與事件 —— 在純粹的面向對象泛型中,所有函數都是方法。在 C# 中,方法只是一種函數成員,也包含一些屬性和事件以及其他組成部分。屬性是封裝了一部分對象狀態的函數成員。事件是簡化對象狀態變化處理的函數成員;
C# 首先是一種類型安全的語言,這意味著類型只能夠通過它們定義的協議進行交互,從而保證每一種類型的內部一致性。
C# 支持靜態類型化,這意味著這種語言會在編譯時執行靜態類型安全性檢查。
(P002)
靜態類型化能夠在程式運行之前去除大量的錯誤。
C# 允許部分代碼通過新的 dynamic 關鍵字來動態指定類型。然而,C# 在大多數情況下仍然是一種靜態類型化的語言。
C# 之所以被稱為一種強類型語言,是因為它的類型規則是非常嚴格的。
C# 依靠運行時環境來執行自動的記憶體管理。
C# 並沒有去除指針 : 它只是使大多數編程任務不需要使用指針。對於性能至關重要的熱點和互操作性方面,還是可以使用指針,但是只允許在顯式標記為不安全的代碼塊中使用。
C# 依賴於一個運行時環境,它包括許多特性,如自動記憶體管理和異常處理。
(P003)
.NET Framework 由名為 Common Language Runtime (CLR) 的運行時環境和大量的程式庫組成。這些程式庫由核心庫和應用庫組成。
CLR 是執行托管代碼的運行時環境。C# 是幾種將源代碼編譯為托管語言之一。托管代碼會被打包成程式集,它可以是可執行文件或程式庫的形式,包括類型信息或元數據。
托管代碼用 Intermediate Language 或 IL 表示。
Red Gate 的 .Net Reflector 是一個重要的分析程式集內容的工具 (可以將它作為反編譯器使用) 。
CLR 是無數運行時服務的主機。這些服務包括記憶體管理、程式庫載入和安全性服務。
CLR 是與語言無關的,它允許開發人員用多種語言開發應用程式。
(P004)
.NET Framework 由只支持基於所有 Windows 平臺或 Web 的應用程式的程式庫組成。
C# 5.0 還實現了 Windows Runtime (WinRT) 庫的互操作。
WinRT 是一個擴展介面和運行時環境,它可以用面向對象和與語言無關的方式訪問庫。Windows 8 帶有這個運行時庫,屬於 Microsoft 組件對象模型或 COM 的擴展版本。
Windows 8 帶有一組非托管 WinRT 庫,它是通過 Microsoft 應用商店交付的支持觸摸屏的 Metro 風格應用程式框架。作為 WinRT ,這些程式庫不僅可以通過 C# 和 VB 訪問,也可以通過 C++ 和 JavaScript 訪問。
WinRT 與普通 COM 的區別是,WinRT 的程式庫支持多種語言,包括 C# 、 VB 、 C++ 和 JavaScript,所以每一種語言 (幾乎) 都將 WinRT 類型視為自己的專屬類型。
(P005)
C# 5.0 兩個較大的新特性是通過兩個關鍵字 (async 和 await) 支持非同步功能 (asynchronous function)。
C# 4.0 增加的新特性有 : 動態綁定、可選參數和命名參數、用泛型介面和代理實現類型變化、改進 COM 互操作性。
C# 3.0 增加的這些特性主要集中在語言集成查詢功能上 (Language Integrated Query,簡稱 LINQ) 。
C# 3.0 中用於支持 LINQ 的新特性還包括隱式類型化局部變數 (Var) 、匿名類型、對象構造器、 Lambda 表達式、擴展方法、查詢表達式和表達式樹。
(P006)
C# 3.0 也增加了自動化和局部方法。
【第02章】
(P007)
在 C# 中語句按順序執行。每個語句都以分號 (;) 結尾。
C# 語句按順序執行,以分號 (;) 結尾。
(P008)
方法是執行一系列語句的行為。這些語句叫做語句塊。語句塊由一對大括弧中的 0 個或多個語句組成。
編寫可調用低級函數的高級函數可以簡化程式。
方法可以通過參數來接收調用者輸入的數據,並通過返回類型給調用者返回輸出數據。
C# 把 Main 方法作為程式的預設執行入口。 Main 方法也可以返回一個整數 (而不是 void) ,從而為程式執行的環境返回一個值。 Main 方法也可以接受一個字元串數組作為參數 (數組中包含可傳遞給可執行內容的任何參數) 。
數組代表某種特定類型,固定數量的元素的集合。數組由元素類型和它後面的方括弧指定。
類由函數成員和數據成員組成,形成面向對象的構建塊。
(P009)
在程式的最外層,類型被組織到命名空間中。
.NET Framework 的組織方式為嵌套的命名空間。
using 指令僅僅是為了方便,也可以用 “命名空間 + 類型名” 這種完全限定名稱來引用某種類型。
C# 編譯器把一系列 .cs 擴展名的源代碼文件編譯成程式集。
程式集是 .NET 中的最小打包和部署單元。
一個程式集可以是一個應用程式,或者是一個庫。
一個普通的控制台程式或 Windows 應用程式是一個 .exe 文件,包含一個 Main 方法。
一個庫是一個 .dll 文件,它相當於一個沒有入口的 .exe 文件。
庫是用來被應用程式或其他的庫調用 (引用) 的。
.NET Framework 就是一組庫。
C# 編譯器名稱是 csc.exe。可以使用像 Visual Studio 這樣的 IDE 編譯 C# 程式,也可以在命令行中手動調用 csc 命令編譯 C# 程式。
(P010)
標識符是程式員為類、方法、變數等選擇的名字。
標識符必須是一個完整的詞、它是由字母和下劃線開頭的 Unicode 字元組成的。
C# 標識符是區分大小寫的。
通常約定參數、局部變數和私有變數欄位應該由小寫字母開頭,而其他類型的標識符則應該由大寫字母開頭。
關鍵字是編譯器保留的名稱,不能把它們用作標識符。
如果用關鍵字作為標識符,可以在關鍵字前面加上 @ 首碼。
@ 並不是標識符的一部分。
@ 首碼在調用其他有不同關鍵字的 .NET 語言編寫的庫時非常有用。
(P011)
點號 (.) 表示某個對象的成員 (或數字的小數點)。
括弧在聲明或調用方法時使用,空括弧在方法沒有參數時使用。
等號則用於賦值操作。
C# 提供了兩種方式的註釋 : 單行註釋和多行註釋。
單行註釋由雙斜線開始,到本行結束為止。
多行註釋由 /* 開始,由 */ 結束。
變數代表它的值可以改變,而常量則表示它的值不可以更改。
(P012)
C# 中所有值都是一種類型的實例。一個值或一個變數所包含的一組可能值均由其類型決定。
預定義類型是指那些由編譯器特別支持的類型。
預定義類型 bool 只有兩種值 : true 和 false 。 bool 類型通常與 if 語句一起用於條件分支。
在 C# 中,預定義類型 (也稱為內建類型) 被當做 C# 關鍵字。在 .NET Framework 中的 System 命名空間下包含了很多並不是預定義類型的重要類型。
正如我們能使用簡單函數來構建複雜函數一樣,也可以使用基本類型來構建複雜類型。
(P013)
類型包含數據成員和函數成員。
C# 的一個優點就是預定義類型和自定義類型只有很少的不同。
實例化某種類型即可創建數據。
預定義類型可以簡單地通過字面值進行實例化。
new 運算符用於創建自定義類型的實例。
使用 new 運算符後會立刻實例化一個對象,對象的構造方法會在初始化時被調用。
構造方法像方法一樣被定義,不同的是方法名和返回類型簡化成它所屬的類型名。
由類型的實例操作的數據成員和函數成員被稱為實例成員。
在預設情況下,成員就是實例成員。
(P014)
那些不是由類型的實例操作而是由類型本身操作的數據成員和函數成員必須標記為 static 。
public 關鍵字將成員公開給其他類。
把成員標記為 public 就是在說 : “這就是我想讓其他類型看到的,其他的都是我自己私有的” 。
用面向對象語言,我們稱之為公有 (public) 成員封裝了類中的私有 (private) 成員。
在 C# 中,相容類型的實例可以相互轉換。
轉換始終會根據一個已經存在的值創建一個新的值。
轉換可以是隱式或顯式。
隱式轉換自動發生,而顯式轉換需要 cast 關鍵字。
long 容量是 int 的兩倍。
(P015)
隱式轉換隻有在下列條件都滿足時才被允許 :
1. 編譯器能保證轉換總是成功;
2. 沒有信息在轉換過程中丟失;
只有在滿足下列條件時才需要顯式轉換:
1. 編譯器不能保證轉換總是能成功;
2. 信息在轉換過程中有可能丟失;
C# 還支持引用轉換,裝箱轉換和自定義轉換。
對於自定義轉換,編譯器並沒有強制遵守上面的規則,所以設計不好的類型有可能在轉換時出現預想不到的結果。
所有 C# 類型可以分成以下幾類 : 值類型、引用類型、泛型類型、指針類型。
值類型包含大多數內建類型 (具體包括所有的數值類型、 char 類型和 bool 類型) 以及自定義 struct 類型和 enum 類型。
引用類型包括所有的類、數據、委托和介面類型。
值類型和引用類型最根本的不同是它們在記憶體中的處理方式。
值類型變數或常量的內容僅僅是一個值。
可以通過 struct 關鍵字定義一個自定義值類型。
對值類型實例的賦值操作總是會複製這些實例。
將一個非常大的 long 轉換成 double 類型時,有可能造成精度丟失。
(P016)
引用類型比值類型複雜,它由兩部分組成 : 對象和對象的引用。
引用類型變數或常量的內容是對一個包含值的對象的引用。
(P017)
一個引用可以賦值為字面值 null,這表示它不指向任何對象;
相對的,值類型通常不能有 null 值;
C# 中也有一種代表類型值為 null 的結構,叫做可空 (nullable) 類型。
(P018)
值類型實例正好占用需要存儲其欄位的記憶體。
從技術上說,CLR 用整數倍欄位的大小來分配記憶體地址。
引用類型要求為引用和對象單獨分配存儲空間。
對象占用了和欄位一樣的位元組數,再加上額外的管理開銷。
每一個對象的引用都需要額外的 4 或 8 位元組,這取決於 .NET 運行時是運行在 32 位平臺還是 64 位平臺上。
C# 中的預定義類型又稱框架類型,它們都在 System 命名空間下。
在 CLR 中,除了 decimal 之外的一系列預定義值類型被認為是基本類型。之所以將其稱為基本類型,是因為它們在編譯過的代碼中被指令直接支持。因此它們通常被翻譯成底層處理器直接支持的指令。
(P019)
System.IntPtr 和 System.UIntPtr 類型也是基本類型。
在整數類型中,int 和 long 是最基本的類型, C# 和運行時都支持它們。其他的整數類型通常用於實現互操作性或存儲空間使用效率非常重要的情況。
在實數類型中,float 和 double 被稱為浮點類型,通常用於科學計算。
decimal 類型通常用於要求10位精度以上的數值計算和高精度的金融計算。
整型字面值可使用小數或十六進位小數標記,十六進位小數用 0x 首碼表示。
實數字面值可使用小數和指數標記。
從技術上說,decimal 也是一種浮點類型,但是在 C# 語言規範中通常不將其認為是浮點類型。
(P020)
預設情況下,編譯器認為數值字面值或者是 double 類型或者是整數類型 :
1. 如果這個字面值包含小數點或指數符號 (E),那麼它被認為是 double ;
2. 否則,這個字面值的類型就是下列能滿足這個字面值的第一個類型 : int 、 uint 、 long 和 ulong ;
數值尾碼顯式地定義了一個字面值的類型。尾碼可以是下列小寫或大寫字母 : F (float) 、 D (double) 、 M (decimal) 、 U (uint) 、 L (long) 、 UL (ulong) 。
尾碼 U 、 L 和 UL 很少需要,因為 uint 、 long 和 ulong 總是可以表示 int 或從 int 隱式轉換過來的類型。
從技術上講,尾碼 D 是多餘的,因為所有帶小數點的字面值都被認為是 double 類型。總是可以給一個數字類型加上小數點。
尾碼 F 和 M 是最有用的,它在指定 float 或 decimal 字面值時使用。
double 是無法隱式轉換成 float 的,同樣的規則也適用於 decimal 字面值。
整型轉換在目標類型能表示源類型所有可能的值時是隱式轉換,否則需要顯式轉換。
(P021)
float 能隱式轉換成 double ,因為 double 能表示所有可能的 float 的值。反過來則必須是顯式轉換。
所有的整數類型可以隱式轉換成浮點數,反過來則必須是顯式轉換。
將浮點數轉換成整數時,小數點後的數值將被截去,而不會四捨五入。
靜態類 System.Convert 提供了在不同值類型之間轉換的四捨五入方法。
把一個大的整數類型隱式轉換成浮點類型會保留整數部分,但是有時會丟失精度。這是因為浮點類型總是有比整數類型更大的數值,但是可能只有更少的精度。
所有的整數類型都能隱式轉換成 decimal 類型,因為小數類型能表示所有可能的整數值。其他所有的數值類型轉換成小數類型或從小數類型轉換到數值類型必須是顯式轉換。
算術運算符 (+ 、 - 、 * 、 / 、 %) 用於除了 8 位和 16 位的整數類型之外的所有數值類型。
自增和自減運算符 (++ 、 --) 給數值加 1 或減 1 。這兩個運算符可以放在變數的前面或後面,這取決於你想讓變數在計算表達式之前還是之後被更新。
(P022)
整數類型的除法運算總是會截斷餘數。用一個值為 0 的變數做除數將產生一個運行時錯誤 (DivisionByZeroException) 。
用字面值 0 做除數將產生一個編譯時錯誤。
整數類型在運行算術運算時可能會溢出。預設情況下,溢出默默地發生而不會拋出任何異常。儘管 C# 規範不能預知溢出的結果,但是 CLR (通用語言運行時) 總是會造成溢出行為。
checked 運算符的作用是在運行時當整型表達式或語句達到這個類型的算術限制時,產生一個 OverflowException 異常而不是默默的失敗。
checked 運演算法在有 ++ 、 -- 、 + 、 - (一元運算符和二元運算符) 、 * 、 / 和整數類型間顯式轉換運算符的表達式中起作用。
checked 操作符對 double 和 float 數據類型沒有作用,對 decimal 類型也沒有作用 (這種類型總是受檢的)。
checked 運算符能用於表達式或語句塊的周圍。
可以通過在編譯時加上 /checked+ 命令行開關 (在 Visual Studio 中,可以在 Advanced Build Settings 中設置) 來預設使程式中所有表達式都進行算術溢出檢查。如果你只想禁用指定表達式或語句的溢出檢查,可以用 unchecked 運算符。
(P023)
無論是否使用了 /checked 編譯器開關,編譯時的表達式計算總會檢測溢出,除非應用了 unchecked 運算符。
C# 支持如下的位運算符 : ~ (按位取反) 、 & (按位與) 、 | (按位或) 、 ^ (按位異或) 、 << (按位左移) 、 >> (按位右移) 。
8 位和 16 位整數類型指的是 byte 、 sbyte 、 short 和 ushort 。這些類型缺少它們自己的算術運算符,所以 C# 隱式把它們轉換成所需的大一些類型。
不同於整數類型,浮點類型包含某些操作要特殊對待的值。這些特殊的值是 NaN (Not a Number) 、 +∞ 、 -∞ 和 -0 。
float 和 double 類型包含用於 NaN 、 +∞ 、 -∞ 值 (MaxValue 、 MinValue 和 Epsilon) 的常量。
(P024)
非零值除以零的結果是無窮大。
零除以零或無窮大減去無窮大的結果是 NaN。
使用比較運算符 (==) 時,一個 NaN 的值永遠也不等於其他的值,甚至不等於其他的 NaN 值。
必須使用 float.IsNaN 或 double.IsNaN 方法來判斷一個值是不是 NaN 。
無論何時使用 object.Equals 方法,兩個 NaN 的值都是相等的。
NaN 在表示特殊值時很有用。
float 和 double 遵循 IEEE 754 格式類型規範,原生支持幾乎所有的處理器。
double 類型在科學計算時很有用。
decimal 類型在金融計算和計算那些 “人為” 的而非真實世界的值時很有用。
(P025)
float 和 double 在內部是基於 2 來表示數值的。因此只有基於 2 表示的數值才能被精確的表示。事實上,這意味著大多數有小數的字面值 (它們基於10) 將無法精確的表示。
decimal 基於 10,它能夠精確地表示基於10的數值 (也包括它的因數,基於2和基於5) 。因為實型字面值是基於 10 的,所以 decimal 能精確地表示像 0.1 這樣的數。然而,double 和 decimal 都不能精確表示那些基於 10 的極小數。
C# 中的 bool (System.Boolean 類型的別名) 能表示 true 和 false 的邏輯值。
儘管布爾類型值僅需要 1 位存儲空間,但是運行時卻用 1 位元組空間。這是因為位元組是運行時和處理器能夠有效使用的最小單位。為避免在使用數組時的空間浪費,.NET Framework 提供了 System.Collections 命名空間下的 BitArray 類,它被設置成每個布爾值使用 1 位。
bool 不能轉換成數值類型,反之亦然。
== 和 != 運算符用於判斷任何類型相等還是不相等,總是返回一個 bool 值。
(P026)
對於引用類型,預設情況的相同是基於引用的,而不是底層對象的實際值。
相等和比較運算符 == 、 != 、 < 、 > 、 >= 和 <= 用於所有的數值類型,但是用於實數時要特別註意。
比較運算符也用於枚舉 (enum) 類型成員,它比較枚舉的潛在整數值。
&& 和 || 運算符用於判斷 “與” 和 “或” 條件。它們常常與代表 “非” 的 (!) 運算符一起使用。
&& 和 || 運算符會在可能的情況下執行短路計算。
短路計算在允許某些表達式時是必要的。
& 和 | 運算符也用於判斷 “與” 和 “或” 條件。
不同之處是 & 和 | 運算符不支持短路計算。因此它們很少用於代替條件運算符。
不同於 C 和 C++ , & 和 | 運算符在用於布爾表達式時執行布爾比較 (非短路計算) 。& 和 | 運算符只在用於數值運算時才執行位操作。
三元條件運算符 (簡稱為條件運算符) 使用 q ? a : b 的形式,它在條件 q 為真時,計算 a,否則計算 b 。
(P027)
條件表達式在 LINQ 語句中特別有用。
C# 中的 char (System.Char 類型的別名) 表示一個 Unicode 字元,它占用 2 個位元組。字元字面值在單引號 (') 中指定。
轉義字元不能按照字面表示或解釋。轉義字元由反斜杠(\)和一個表示特殊意思的字元組成。
\' 單引號
\" 雙引號
\\ 斜線
\0 空
\a 警告
\b 退格
\f 走紙
\n 換行
\r 回車
\t 水平製表符
\v 垂直製表符
\u (或 \x ) 轉義字元通過 4 位十六進位代碼來指定任意 Unicode 字元。
從字元類型到數值類型的隱式轉換隻在這個數值類型可以容納無符號 short 類型時有效。對於其他的數值類型,則需要顯式轉換。
(P028)
C# 中的字元串類型 (System.String 的別名) 表示一些不變的、按順序的 Unicode 字元。字元串字面值在雙引號 (") 中指定。
string 類型是引用類型而不是值類型,但是它的相等運算符卻遵守值類型的語義。
對 char 字面值有效的轉移字元在字元串中也有效。
C# 允許逐字字元串字面值,逐字字元串字面值要加首碼 @ ,它不支持轉義字元。
逐字字元串字面值也可以貫穿多行。
可以通過在逐字字元串中寫兩次的方式包含雙引號字元。
(+) 運算符連接兩個字元串。
右面的操作對象可以是非字元串類型的值,在這種情況下這個值的 ToString 方法將被調用。
既然字元串是不變的,那麼重覆地用 (+) 運算符來組成字元串是低效率的 : 一個更好的解決方案是用 System.Text.StringBuilder 類型。
(P029)
字元串類型並不支持 < 和 > 的比較,必須使用字元串類型的 CompareTo 方法。
數組代表固定數量的特定類型元素,為了高效率地讀取,數組中的元素總是存儲在連續的記憶體塊中。
數組用元素類型後加方括弧表示。
方括弧也可以檢索數組,通過位置讀取特定元素。
數組索引從 0 開始。
數組的 Length 屬性返回數組中的元素數量。一旦數組被建立,它的長度將不能被更改。
System.Collection 命名空間和子命名空間提供了像可變數組等高級數據結構。
數組初始化語句定義了數組中的每個元素。
所有的數組都繼承自 System.Array 類,它提供了所有數組的通用服務。這些成員包括與數組類型無關的獲取和定義元素的方法。
建立數組時總是用預設值初始化數組中的元素,類型的預設值是值為 0 的項。
無論數組元素類型是值類型還是引用類型都有重要的性能影響,若元素類型是值類型,每個元素的值將作為數組的一部分進行分配。
(P030)
無論是任何元素類型,數組本身總是引用類型對象。
多維數組分為兩種類型 : “矩形數組” 和 “鋸齒形數組” 。 “矩形數組” 代表 n 維的記憶體塊,而 “鋸齒形數組” 則是數組的數組。
矩形數組聲明時用逗號 (,) 分隔每個維度。
數組的 GetLength() 方法返回給定維度的長度 (從 0 開始) 。
鋸齒形數組在聲明時用兩個方括弧表示每個維度。
鋸齒形數組內層維度在聲明時可不指定。
不同於矩形數組,鋸齒形數組的每個內層數組都可以是任意長度;每個內層數組隱式初始化成空 (null) 而不是一個空數組;每個內層數組必須手工創建。
有兩種方式可以簡化數組初始化表達式。第一種是省略 new 運算符和類型限制條件,第二種是使用 var 關鍵字,使編譯器隱式確定局部變數類型。
(P032)
隱式類型轉換能進一步用於一維數組的這種情況,能在 new 關鍵字之後忽略類型限制符,而由編譯器推斷數組類型。
為了使隱式確定數組類型正常工作,所有的元素都必須可以隱式轉換成同一種類型。
運行時給所有的數組索引進行邊界檢查,如果使用了不合法的索引,就會拋出 IndexOutOfRangeException 異常。
和 Java 一樣,數組邊界檢查對類型安全和簡化調試是很有必要的。
通常來說,邊界檢查的性能消耗很小,即時編譯器會進行優化。像在進入迴圈之前預先檢查所有的索引是不安全的,以此來避免在每輪迴圈中都檢查索引。
C# 提供 "unsafe" 關鍵字來顯式繞過邊界檢查。
變數表示存儲著可變值的存儲空間,變數可以是局部變數、參數 (value 、 ref 或 out) 、 欄位 (instance 或 static) 或數組元素。
“堆” 和 “棧” 是存儲變數和常量的地方,它們每個都有不同的生存期語義。
“棧” 是存儲局部變數和參數的記憶體塊,棧在進入和離開一個函數時邏輯增加和減少。
(P033)
“堆” 是指對象殘留的記憶體塊,每當一個新的對象被創建時,它就被分配進堆,同時返回這個對象的引用。
當程式執行時,堆在新對象創建時開始填充。
.NET 運行時有垃圾回收器,它會定期從堆上釋放對象。
只要對象沒有被引用,他就會被選中釋放。
無論變數在哪裡聲明,值類型實例以及對象引用一直存在。如果聲明的實例作為對象中的欄位或數組元素,那麼實例存儲於堆上。
在 C# 中你無法顯式刪除對象,但在 C++ 中可以。未引用的對象最終被垃圾回收器回收。
堆也存儲靜態欄位和常量。不同於堆上被分配的對象 (可以被垃圾回收器回收),靜態欄位和常量將一直存在直到應用程式域結束。
C# 遵守明確賦值的規定。在實踐中,這是指在沒有 unsafe 上下文情況下是不能訪問未初始化記憶體的。明確賦值有三種含義 :
1. 局部變數在讀取之前必須被賦值;
2. 當調用方法時必須提供函數的參數;
3. 其他的所有變數 (像欄位和數組元素) 都自動在運行時被初始化;
(P034)
欄位和數組元素都會用其類型的預設值自動初始化。
所有類型實例都有預設值。預定義類型的預設值是值為 0 的項 :
[類型] - [預設值]
所有引用類型 - null
所有數值和枚舉類型 - 0
字元類型 - '\0'
布爾類型 - false
能夠對任何類型使用 default 關鍵字來獲得其預設值。
自定義值類型中的預設值與自定義類型定義的每個欄位的預設值相同。
方法有一連串的參數,其中定義了一系列必須提供給方法的參數。
(P035)
能通過 ref 和 out 修飾符來改變參數傳遞的方式 :
[參數修飾符] - [傳遞類型] - [必須明確賦值的參數]
none - 值類型 - 傳入
ref - 引用類型 - 傳入
out - 引用類型 - 傳出
通常,C# 中參數預設是按值傳遞的,這意味著在將參數值傳給方法時創建參數值的副本。
值傳遞引用類型參數將賦值給引用而不是對象本身。
(P036)
如果按引用傳遞參數,C# 使用 ref 參數修飾符。
註意 ref 修飾符在聲明和調用時都是必需的,這樣就清楚地表明瞭將執行什麼。
ref 修飾符對於轉換方法是必要的。
無論參數是引用類型還是值類型,都可以實現值傳遞或引用傳遞。
out 參數和 ref 參數類似,除了 :
1. 不需要在傳入函數之前賦值;
2. 必須在函數結束之前賦值;
(P037)
out 修飾符通常用於獲得方法的多個返回值。
和 ref 參數一樣, out 參數是引用傳遞。
當引用傳遞參數時,是為已存變數的存儲空間起了個別名,而不是創建了新的存儲空間。
params 參數修飾符在方法最後的參數中指定,它使方法接收任意數量的指定類型參數,參數類型必須聲明為數組。
(P038)
也可以將通常的數組提供給 params 參數。
從 C# 4.0 開始,方法、構造方法和索引器都可以被聲明成可選參數,只要在聲明時提供預設值,這個參數就是可選參數。
可選參數在調用方法時可以被省略。
編譯器在可選參數被用到的地方用了預設值代替了可選參數。
被其他程式集調用的 public 方法在添加可選參數時要求重新編譯所有的程式集,因為參數是強制的。
可選參數的預設值必須由常量表達式或無參數的值類型構造方法指定,可選參數不能被標記為 ref 或 out 。
強制參數必須在可選參數方法聲明和調用之前出現 (params 參數例外,它總是最後出現)。
相反的,必須將命名參數和可選參數聯合使用。
命名參數可以按名稱而不是按參數的位置確定參數。
(P039)
命名參數能按任意順序出現。
不同的是參數表達式按調用端參數出現的順序計算。通常,這隻對相互作用的局部有效表達式有所不同。
命名參數和可選參數可以混合使用。
按位置的參數必須出現在命名參數之前。
命名參數在和可選參數混合使用時特別有用。
如果編譯器能夠從初始化表達式中推斷出變數的類型,就能夠使用 var 關鍵字 (C# 3.0 中引入) 來代替類型聲明。
因為是直接等價,所以隱式類型變數是靜態指定類型的。
(P040)
當無法直接從變數聲明中推斷出變數類型時,var 關鍵字將降低代碼的可讀性。
表達式本質上表示的是值。最簡單的表達式是常量和變數。表達式能夠用運算符進行轉換和組合。運算符用一個或多個輸入操作數來輸出新的表達式。
C# 中的運算符分為一元運算符、二元運算符和三元運算符,這取決它們使用的操作數數量 (1 、 2 或 3) 。
二元運算符總是使用中綴標記法,運算符在兩個操作數中間。
基礎表達式由 C# 語言內置的基礎運算符表達式組成。
(. 運算符) 執行成員查找;
(() 運算符) 執行方法調用;
空表達式是沒有值的表達式。
因為空表達式沒有值,所以不能作為操作數來創建更複雜的表達式。
賦值表達式用 = 運算符將一個表達式的值賦給一個變數。
(P041)
賦值表達式不是空表達式,實際上它包含了賦值操作的值,因此能再加上另一個表達式。
複合賦值運算符是由其他運算符組合而成的簡化運算符。
當表達式包含多個運算符時,運算符的優先順序和結合性決定了計算的順序。
優先順序高的運算符先於優先順序低的運算符執行。
如果運算符的優先順序相同,那麼運算符的結合性決定計算的順序。
二元運算符 (除了賦值運算符、 lambda 運算符 、 null 合併運算符) 是左結合運算符。換句話說,它們是從左往右計算。
賦值運算符、 lambda 運算符、 null 合併運算符和條件運算符是右結合運算符。換句話說,它們從右往左計算。右結合運算符允許多重賦值。
(P043)
函數包含按出現的字面順序執行的語句。語句塊是大括弧 ({}) 中出現的一系列語句。
(P044)
聲明語句可以聲明新變數,也可以用表達式初始化變數。聲明語句以分號結束。可以用逗號分隔的列表聲明多個同類型的變數。
常量的聲明和變數聲明類似,除了不能在聲明之後改變它的值和必須在聲明時初始化。
局部變數和常量的作用範圍是在當前的語句塊中。不能在當前的或嵌套的語句塊中聲明另一個同名的局部變數。
變數的作用範圍是它所在的整個代碼段。
表達式語句是表達式也是合法的語句,表達式語句必須改變狀態或調用某些改變的狀