初識C#
C#是微軟公司發佈的面向對象的、運行於.NET Framework之上的高級程序設計語言。與Java有着明顯不同,借鑑Delphi的特點,與COM(組件對象模型)直接集成,是微軟公司 .NET windows網絡框架的主角。
C#是一種語言,.net是一個平臺。C#不但可以開發基於.net的應用程序,也可以開發基於WinForm的程序。. net 是 Microsoft 的 XML Web 服務平臺,XML Web 服務能使應用程序在 Internet 上傳輸和共享數據。
特點:
1. 強類型語言,安全穩定;
2. 事件驅動、完全面向對象的可視化編程語言;
3. 便捷的面向組件編程的支持;
4. VB簡單的可視化操作和C++的高運行效率的結合;
5. 託管代碼和垃圾回收 (garbage collection);
// .NET ~ C# ~ ASP.NET
C#是.net平臺下的一種開發語言,用於開發桌面應用程序
asp.net是開發web程序的技術
.net是平臺,該平臺下包含很多技術如:asp.net ado.net winform WCF
.NET框架
CLR:公共語言運行庫,Common Language Runtime,.NET框架的核心組件,在操作系統的頂層並在運行期調用基類庫(BCL, Base Class Library)管理程序的執行。
· 內存管理機制
· 垃圾回收機制GC (Garbage Collector)
· JIT編譯器
CTS:公共類型系統,Common Type System,重要特性是所有類型都繼承自公共的基類object。
基礎學習
4個基礎命名空間
// .NET框架中的基礎類庫,用於實現一些基本的類。
using System; .NET應用程序中使用的大多數基本類型
using System.Collections.Generic; 處理集合的泛型類型
using Syatem.Text; 字符串處理和編碼相關的類型
using System.Linq; 提供支持使用語言集成查詢(LINQ)進行查詢的類和接口,用於對集合進行查詢
基礎概念
0. 結構 - 類
struct 是值類型,隱式密封的、不能被繼承,但可以實現接口。struct 成員默認是 public,有構造函數(實例、靜態),沒有析構函數,不允許字段初始化。
class 是引用類型,單繼承,可實現接口。class 成員默認是 private。
· 數據 成員:字段、常量;
· 函數 成員:屬性、方法、事件、索引器、構造函數、析構函數、操作符;
1. 字段 - 屬性 - 索引
字段 - private,屬性 - public;
屬性 是指定的一組2個匹配的、稱爲訪問器 (get 和 set) 的方法。屬性是函數成員,訪問器只能被隱式調用,執行代碼,但不爲數據存儲分配內存。公有屬性提供對私有字段的受控訪問。
索引 是一組 get 和 set 訪問器,類似屬性,索引是函數成員;索引通常用於訪問多個數據成員,類似數組利用索引運算符;索引不能聲明爲 static。訪問器只能被隱式調用,可以重載,參數列表必須不同。
· 索引沒有名稱,但 this 是必須的;
· 參數列表中至少必須聲明一個參數;
ReturnType this[參數列表] {
get {...}
set {...}
}
2. 靜態構造函數 - (普通的)實例構造函數
實例構造函數初始化類的每個新實例,static 構造函數初始化類層次的項目。static 構造函數不能訪問類的實例成員,通常用於初始化類的靜態字段,靜態構造函數被系統自動調用。靜態字段先於實例成員被初始化,類只能有一個 static 構造函數,不能帶參數、不能有訪問修飾符、也不能使用 this 訪問器。
[1]. 構造函數中不能調用虛方法;
[2]. ;
[3]. ;
3. 繼承
單繼承。
a. 重載:同一個類內的函數,函數名相同、參數列表不同;
b. 重寫(覆蓋):父子類之間的函數,簽名相同、返回類型相同;父類用 virtual 標識,子類用 override 標識;
c. 隱藏:默認或通過 new 顯式隱藏。base.數據成員/函數名 顯式訪問被隱藏的成員。
· 字段:名稱相同,類型相同;
· 函數:簽名相同(函數名、參數列表(個數、順序、類型、修飾符));
4. 抽象類 abstract - 接口 interface
a. 抽象類可以給出某些成員的一些實現,接口不能包含成員實現;
b. 抽象類的抽象成員可以被子類部分實現,接口的成員必須被實現類全部實現;
c. 一個類只能繼承一個抽象類(類單繼承),但是可以實現多個接口;
d. 類是對對象的抽象,抽象類是對類的抽象,接口是對行爲的抽象;
e. 從設計角度,抽象類和接口設計的思維過程不同(相反):
抽象類 - 自底向上,接口 - 自頂向下;
- 接口 -
引用類型,接口可以繼承接口,類和結構可以實現接口。接口允許訪問修飾符 public、protected、internal、private,接口成員不允許訪問修飾符,默認 public static。接口聲明不能包含數據成員,只能包含 屬性、方法、事件、索引。
類 A 實現接口 IA,將類 A 的對象引用轉換爲接口 IA 的引用:
a. 強制類型轉換:IA ia = (IA)objA;但是若類 A 未實現接口 IA,則拋出異常。
b. as 運算符:IA ia = objA as IA;若類 A 未實現接口 IA,返回 null、不拋出異常。
實現接口的類可以從它的基類繼承實現代碼。類實現 2 個接口,2 個接口包含同名方法,類的單一實現就可以滿足 2 個接口 或 顯式實現每一個接口。同時可以分別獲得每一個接口的獨立引用。
接口正常實現:接口方法不包含實現代碼,實現在類級別的方法中;
接口顯式實現:接口方法包含實現代碼,沒有類級別的方法;
注:接口的顯式實現成員只能被相應的接口引用訪問,需要強制轉換操作。
5. 密封類 - 抽象類 - 靜態類
a. 密封類:sealed,只能被用作獨立的類,不能被繼承,可實例化;
b. 抽象類:abstract,只能被繼承,不可實例化;
c. 靜態類:static,靜態類是密封的。不能被繼承,不可實例化;
6. 裝箱 - 拆箱
a. 裝箱:隱式轉換,把值類型打包到Object引用類型的一個實例中;
b. 拆箱:顯式轉換,從對象中提取值類型;
implicit、explicit 和 is、as
· implicit - 隱式轉換,explicit - 顯式轉換;
public static implicit/explicit operator 目標類型(源類型 源類型變量)
注:用戶自定義轉換僅針對於類和結構。is-as 不能用於用戶自定義轉換。is-as 不能重載。
· is:檢查對象類型兼容性並判斷能否成功轉換,返回bool,不拋出異常;
適應範圍:引用轉換、裝箱轉換、拆箱轉換,if(obj is Type) Type t = (Type)obj;
as:類似強制轉換,檢查對象類型兼容性並返回轉換結果,不拋出異常,失敗時返回null;
適應範圍:引用轉換、裝箱轉換,Type t = obj as Type; if(null !=t){…}
true/成功:obj 是 Type 類型或者 obj 是 Type 類型的子類型;
對於 is,CLR 對對象類型檢查了兩次:is操作首先檢查obj是否和Type類型兼容。若兼容,在if語句內執行轉換時CLR再檢查obj是否爲一個Type引用並轉換。對於 as,CLR 對對象類型檢查了一次:as操作檢查兼容性並直接轉換,效率高、性能好。
參考:is - as 詳解;
7. object sender - EventArgs e
a. object sender:保存觸發事件的對象的引用,指向發送通知的對象;
b. EventArgs e:EventArgs 是包含事件數據的類的基類,在事件觸發時傳遞數據,但是 EventArgs 不包含任何數據,所有的事件參數類都必須從 EventArgs 類派生;
8. 變體
變體分爲 協變 - 抗變 兩種,針對引用類型:
a. 協變:covariance,父=子,out,只能用作方法的返回值或屬性get的訪問器;
b. 抗變:contravariance,子=父,in,只能用作方法的參數;
9. 可空類型 ? - 空接合運算符 ??
a. 可空類型允許創建普通值類型的變量並標註其有效性。可空類型是對普通值類型的 private 封裝,其問號語法是通過 System.Nullable<T> 利用泛型實現,與普通值類型可以相互轉換。不能創建引用類型的可空類型。2 個只讀屬性:
- HasValue:bool 類型,標識是否有效;
- Value:變量值;(普通值類型的值、可空類型的值、null)
b. 空接合運算符允許在可空類型的變量爲 null 時返回一個給定值。
10. 泛型
類型是實例對象的模板,泛型類型是類型的模板。
類型參數的約束用 where 子句列出:where 參數:constraint, constraint, …
· 構造函數約束 new() 必須放在最後;
· 主約束(class/struct)至多一個,且必須放在第一位;
· 接口約束可以有多個;
只有 Type 類型或派生於 Type 類型的實參才能用於受約束的參數。
委託 - 事件
a. 委託 delegate:對函數的封裝,一種引用方法的類型 (引用類型),代表一類方法,具有相同參數列表和返回類型;
b. 事件 event:委託的一種特殊情形,事件的類型是委託,事件是委託類型的變量,事件不是類型,事件是成員(變量)且被隱式自動初始化爲null;
利用”+=”增加委託的實例/靜態方法,利用”-=”移除委託的實例/靜態方法。事件被觸發時,執行被委託的方法(調用委託來依次調用調用列表中的方法)。此處引用大話設計模式中的例子:
public delegate void CatShoutEventHandler(object sender, EventArgs e);
public event CatShoutEventHandler CatShout;
委託是面向對象、類型安全的並且是可靠受控、安全的。當委託被調用時,它調用有序方法列表中的每一個方法。委託是恆定的,委託對象被創建後就不會再被改變。調用空委託會拋出異常,通過把委託和null比較判斷委託的調用列表是否爲空,進而判斷委託是否爲null。
泛型委託
public delegate TR FunHandler<T1, T2, TR>(T1 p1, T2 p2)
匿名方法 -> Lambda表達式
匿名方法,anonymous method,可以避免創建使用獨立的具名方法,允許在創建並初始化委託或爲委託增加方法時包含小段的內聯inline代碼。
delegate(參數列表){語句塊};
Lambda表達式避免冗餘信息、簡化匿名方法的語法。
總結: 從 委託事件 到 觀察者模式;
擴展方法
允許向現有類型"添加"方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。類A需要新增一個方法,但類A是密封的或源代碼不可見,即不能修改類A,此時擴展方法允許在另一個類B中利用類A的公有可用成員實現類A需要新增的方法。
· 擴展方法必須是靜態的,所在類也必須是靜態的;
· 被擴展類型必須作爲第一個參數,必須用 this 修飾;
public static class ExtendMyClass {
public static 返回類型 Function(this MyClass mc) {
// 調用MyClass的公共成員實現新增方法
}
}
調用:mc.Function();,如果沒有this,只能以ExtendMyClass.Function(mc);方式調用。
擴展方法還可以結合泛型類,允許將(擴展)類中的靜態方法關聯到不同的泛型類上。擴展接口時,利用擴展方法比繼承接口(會強制要求實現接口下的所有方法)更方便。但是,擴展方法的優先級總是比被擴展類型本身中定義的同名實例方法低,且被擴展類型的子類不能繼承其父類型的擴展方法。
· 將靜態方法轉成擴展方法,擴展方法本質上是靜態方法;
· 編寫幫助類;
· 爲 Linq 服務,實現鏈式編程;
參考:談擴展方法的理解; C#擴展方法;
奇思妙想之擴展方法系列;
枚舉 ~ 枚舉數 ~ 可枚舉類型
枚舉 enum,值類型,成員是整數值常量,可以顯式爲其賦值初始化,但不能使用修飾符。枚舉可用於實現位標誌,注意添加 [Flags] 特性。
可枚舉類型是實現了GetEnumerator()方法的類型,返回用於(讀取並返回)集合中數據項的枚舉數,枚舉數是可以依次返回集合中數據項的類對象。
參考:迭代器學習系列;自定義類實現foreach;
[-1-]. IEnumerable / IEnumerator
非泛型枚舉數和可枚舉類型,枚舉數類通常聲明爲類中的嵌套類。
· IEnumerator
- Current:當前位置對應的數據項;
- MoveNext():下移位置,初始位置爲-1;
- Reset():復位;
· IEnumerable
- IEnumerator GetEnumerator():
[-2-]. IEnumerable<T> / IEnumerator<T>
泛型枚舉數和可枚舉類型,類型安全。
總結:IEnumerable / IEnumerator 學習 - sqh
某些重要的關鍵字/修飾符/運算符
0. object 類
C#中所有的類(型)都直接/間接繼承自System.Object類(型),值類型數據可以隱式轉換爲Object類型;object是引用類型,關鍵字object就是System.Object的別稱。
■ 靜態方法
[1]. public static bool Equals(object objA, object objB){}
調用實例方法Equals(object obj),判斷是否相等;
[2]. public static bool ReferenceEquals(object objA, object objB){}
判斷兩個對象是否引用相等;
■ 實例方法
[1]. public virtual bool Equals(object obj){}
方法需要重寫用於實現根據值來判斷對象是否相等;
[2]. public virtual int GetHashCode(){}:獲取對象的Hash值;
[3]. public Type GetType(){}
獲取當前實例的Type,查詢對象的元數據來確定對象的運行時類型;
[4]. public virtual string ToString(){}:獲取當前實例的字符串信息,對象的字符串表達形式;
■ 受保護方法
[1]. protected virtual void Finalize(){}
類或派生類可以訪問,允許 Object 在“垃圾回收”機制回收 Object 之前嘗試釋放資源並執行其他清理操作;
[2]. protected object MemberwiseClone(){}:創建當前實例的淺表副本;
1. partial
a. 把類定義放在多個代碼文件中;
b. 用於創建部分方法(定義聲明和方法實現),不能有訪問修飾符,返回值必須爲 void;
2. internal
類和類成員的訪問修飾符,同一程序集權限。類默認是 internal,類成員默認是 private。
protected internal:受保護的內部成員,同一程序集 or 子類權限。
參考:internal - 舉例參考
嵌套類:嵌套是指類聲明的位置,而不是類實例的位置。
嵌套類具有成員訪問級別,默認 private,可見性具體地:
· 嵌套類型的成員對封閉類型的成員具有完全訪問權限;
· 封閉類型的成員只能訪問嵌套類型的public和internal成員,不能訪問private和protected成員;
嵌套類型的對象訪問封閉類型,需要維護封閉類型的引用。
3. using
a. using 指令:命名空間指示符
b. using 別名:類型別名指示符
一個.cs文件引用了兩個不同的命名空間,但兩個空間都包括一個相同名字的類型,使用別名更簡潔。
using aClass = NameSpaceA.MyClass;
using bClass = NameSpaceB.MyClass;
c. using語句:資源的包裝和管理 -> 隱式的 try…finally 塊
定義一個範圍,在範圍結束時自動處理對象,自動調用這個類實例的 Dispose 方法。資源是一個實現 System.IDisposable 接口的類或結構。
4. 異常:try…catch…finally
結構化異常處理語法,標記出能處理異常的代碼和指令:
■ try:包含可能會拋出異常的代碼;
■ catch:拋出異常後要執行的代碼,catch塊可多個;
■ finally:始終一定會執行的代碼,釋放資源;
try 塊是必須的,catch 和 finally 必須至少有一個。所有的異常類均派生於 System.Exception 類。
異常嵌套的處理:如果異常出現在 Method2 方法內部,但是其 catch 塊沒有匹配的異常處理程序, 系統沿着調用棧向上搜索到 Method1,如果找到匹配的 catch 塊,系統先回到棧頂 Method2 處執行其 finally 塊,然後把 Method2 從調用棧中 pop(),最後執行 Method1 的相應 catch 塊和 finally 塊。
public void Method2() public void Method1()
{ {
try{ try{
... Method2();
} }
catch{...} catch{...}
finally{...} finally{...}
} }
■ throw:顯式拋出異常;
- throw Exception;異常拋出後,異常實例可以被 catch 塊捕獲。
- throw;此種只能在 catch 塊內,捕獲後再重新拋出。
5. String、StringBuffer 與 StringBuilder
String是字符串常量、定長,StringBuffer與StringBuilder是字符串變量、可變長、避免產生額外的臨時變量;StringBuffer線程安全,StringBuilder是非線程安全,三者的執行速度 StringBuilder > StringBuffer > String。具體區別詳見:
參考: String - StringBuffer - StringBuilder.
string - String
String是.NET Framework中的類,string是C#中的類,C#的string映射爲.NET Framework的String;string是C#中的關鍵字,可以作爲String或System.String的別名;
6. const 與 readonly
const只能在聲明語句中初始化,readonly可以在聲明語句或構造函數中初始化,const是編譯時常量、在內存中沒有存儲位置,readonly是運行時常量、在內存中有存儲位置;const是靜態的,readonly可以是靜態字段也可以是實例字段。
7. typeof 與 GetType
typeof:一元運算符,返回作爲它的參數的任何類型的 System.Type 對象,不能重載。
GetType:方法,可以調用 typeof 運算符,對任意類型的任意對象都有效。
8. var
推斷類型,弱化類型的定義,可替代任何類型,但是 var 並不改變 C# 強類型性質。類似object,但object是引用類型,效率比var低。
var 用於本地局部變量,不能用於字段,使用時必須初始化且不能再次賦類型不同的值;
常用函數
1. Convert.ToInt32 - int.Parse(Int32.Parse)- int.TryParse - (int)
a. Convert.ToInt32與int.Parse類似,Convert.ToInt32 內部調用了int.Parse,Convert.ToInt32 可以轉換的類型較多,int.Parse只能轉換數字類型的字符串;
b. int.TryParse與int.Parse類似,但不會拋出異常,返回值爲bool以指示解析是否成功,從而可以免去添加異常處理代碼的麻煩,out參數爲轉換輸出值;
此四者都可以解釋爲將類型轉換爲 int,eg:舉例參考.
注:所有預定義的簡單類型均包含靜態方法 Parse,將字符串解析爲相應的數據值。
2. Split
String類的內置方法,分割函數,參數可以爲單個字符、多個字符、字符串。
參考:Split - 常用舉例參考,Split的不同重載方法.
3. Trim
String類的內置方法,用於去除字符串前後的指定字符,另外還有TrimStart()和TrimEnd()方法。
參考:Trim - 舉例參考.
4. DateTime
· 與字符串string的轉換
- DateTime.Parse(timeString);
- Convert.ToDateTime(timeString);
- if (DateTime.TryParse(timeString, out datetime)) {
DateTime dm = datetime;
}
· DateTime
x. xxx
集合類:數據存儲和檢索
命名空間:using System.Collections;
using System.Collections.Generic;
與 ArrayList 對應的泛型集合是 List,與 HashTable 對應的泛型集合是 Dictionary。
- ArrayList:是Array的複雜版本,動態數組,實現了ICollection和IList接口,針對任意類型、任意長度,非類安全型的;
聲明:ArrayList mAList = new ArrayList();
具體地屬性方法類似List,此處不再贅述。
- HashTable:每個元素都是一個存儲在DictionaryEntry對象中的鍵值對。keyvalue鍵值對均爲object類型,支持任何類型的keyvalue鍵值對,非類安全型的;線程安全的;
聲明:Hashtable ht = new Hashtable();
遍歷哈希表元素:
foreach(DictionaryEntry de in ht)
哈希表排序:
ArrayList KeysAList = new ArrayList(ht.Keys);
KeyAList.Sort();
1. 泛型List
聲明:List<T> mList = new List<T>();
屬性方法:
- mList.Count:對鏈表mList元素計數
- mList.Add(T item):添加元素
- mList.Insert(int pos, T item):指定位置插入元素
- mList.AddRange(List list):鏈接2個List
- mList.Contains(T item):測試List是否包含元素item
- mList.Item(int idx):索引器,通過指定索引獲取或設置元素
- mList.Remove(T item):刪除指定的元素
- mList.RemoveAt(int pos):刪除指定位置的元素(推薦)
- mList.RemoveRange(int b, int n):刪除從b開始的n個元素
- mList.Clear():清空List
- mList.Reverse():反轉List
- mList.Sort():排序List
2. 泛型Dictionary
在C#中,Dictionary提供快速的基於鍵值的元素查找。Dictionary<[key], [value]>,鍵必須唯一且不能爲空引用null,值若爲引用類型則可以爲空。
聲明:Dictionary<T1, T2> mDict = new Dictionary<T1, T2>();
屬性方法:
- mDict.Count:對字典mDict元素計數
- mDict.Add(T1 key, T2 value):添加元素(鍵, 值)對
- mDict.ContainsKey(T1 key):字典是否包含鍵爲key的元素
- mDict.ContainsValue(T2 value):字典是否包含值爲value的元素
- mDict.Remove(T1 key):移除鍵爲key的元素
- mDict.Clear():清空Dict
- 遍歷字典元素
1. By KeyValuePair
foreach (KeyValuePair<T1, T2> kvp in mDict) 或 foreach(var kvp in mDict)
2. By Key
Dictionary<T1, T2>.KeyCollection keyCol = mDict.Keys;
foreach (T1 key in keyCol) 或 foreach(T1 key in mDict.Keys)
3. By Value
Dictionary<T1, T2>.ValueCollection valueCol = mDict.Values;
foreach (T2 value in valueCol) 或 foreach(T2 value in mDict.Values)
- mDict[key] = value:通過索引器讀寫鍵值對
- mDict.TryGetValue(T1 key, out value_T2):獲取與指定的鍵相關聯的值。通過鍵取值,包括兩個參數,一個是要查詢的鍵,另一個是獲取的值,注意值前面使用out關鍵字。
注:“判斷鍵存在”和“根據鍵取值”兩步轉化爲一步,鍵的哈希值只計算一次,效率高。
以下三個集合類,可以進一步參考 Stack - Queue - SortedList.
3. SortedList
System.Collections.SortedList類表示按鍵排序的鍵/值對的集合,可以按鍵或索引訪問,是數組和哈希表的組合。
聲明:SortedList sList = new SortedList();
遍歷排序列表元素:
foreach(DictionaryEntry de in sList)
- 泛型SortedList
4. 堆棧 Stack
System.Collections.Stack類表示對象的LIFO集合,處理順序多變。
聲明:Stack st = new Stack();
屬性方法:
- st.Peek:取棧頂元素,但不將其移除;
- st.Push(object obj):棧頂入棧;
- st.Pop():出棧,移除並返回位於Stack棧頂處的對象;
- 泛型Stack
5. 隊列 Queue
System.Collections.Queue類表示對象的FIFO集合,順序處理集合中的對象。
聲明:Queue qu = new Queue();
屬性方法:
- qu.Peek:取隊首元素,但不將其移除;
- qu.Enqueue(object obj):隊尾入隊;
- qu.Dequeue():出隊,移除並返回位於Queue開始處的對象;
- 泛型Queue
當有多個線程併發訪問集合時,應該用System.Collections.Concurrent命名空間代替上述命名空間中的對應類型,線程安全的集合類可由多個線程同時訪問:
- ConcurrentDictionary
- ConcurrentQueue
參考鏈接
[1]. 經典.Net面試題;
[2]. .Net面試題系列 0-9;