一. System.Object
公用執行個體方法 |
簡要說明 |
virtual bool Equals(object obj) |
確定兩個對象是否相等,如果相等則返回true,否則false |
virtual int GetHashCode() |
返回this對象的一個雜湊碼,如果該對象被一個集合當作索引值使用,則這個值將起作用 |
Type GetType() |
返回當前對象的類型。類型由一個繼承自 System.Type的執行個體對象表示 |
virtual string ToString() |
返回一個可以代表當前對象的字串,在 System.Object中該方法返回當前物件類型的完整名稱 |
執行個體 Equals 方法
比較當前對象和參數對象是否相等。
在 System.Object中,這個方法是“引用比較”比較this引用和參數引用是否引用同一個對象,是則true否則false
假設兩個字串變數a,b分別賦相同值,邏輯相同但此時按照Equals()規則則不同。故設計新類時可重寫此方法
實現內容相同。.NET中,System.ValueType作為所有實值型別的基類,重寫此方法以實現內容的比較。
執行個體 GetHashCode 方法
用來返回當前對象的一個雜湊碼,當對象被用作集合的索引值時,此方法被調用。
3種方法檢驗GetHashCode的演算法正確性
1. 如果兩個對象相等(Equals),則兩個對象調用GetHashCode方法應該返回相同值
2. 同一個對象無論在何種 場合調用GetHashCode方法,傳回值應該返回想同值
3. 不同對象調用GetHashCode方法,傳回值應該均勻分布在整數集合之中
GetType 方法
公用方法:GetType。 返回一個代表當前“物件類型”的執行個體。
即尋找程式集中的中繼資料,找到該類型的定義。 System.Object的ToString方法利用了 GetType方法
公用靜態方法 |
簡要說明 |
bool Equals(object objA, object objB) |
確定兩個對象是否相等,如果相等則返回true,否則false |
bool ReferenceEquals(object objA, object objB) |
比較兩個對象的引用是否相等。相等則返回true,否則false |
ReferenceEquals方法
實現兩個對象的引用比較,如果引用相等返回true,否則false。無論是何種實值型別,返回結果必為false。
Equals方法
實現較簡單,依靠執行個體的Equals方法實現內容比較
public static bool Equals(object left, object right) { //查看是否是同一對象 if (left == right) return true; if ((left == null) || (right == null)) return false; return left.Equals(right); }
受保護執行個體方法 |
簡要說明 |
object MemberwiseClone() |
淺複製當前對象執行個體,並返回複製對象的引用 |
Finalize |
.NET的析構方法 |
二. 實值型別與參考型別
(1)實值型別
繼承自 System.ValueType
常有的實值型別包括 結構、枚舉、整數型、浮點數、布爾型等
(2)參考型別
以class關鍵字定義的類型都是參考型別
(3)區別
1).賦值時區別
實值型別的變數將直接獲得一個真實的資料副本
參考型別的賦值僅僅是吧對象的引用賦給變數,可導致多個變數引用一個實際對象執行個體上
2).記憶體配置的區別
實值型別的對象會在堆棧上分配記憶體,
參考型別的對象會在堆上分配記憶體。
3).繼承結構的區別
實值型別都繼承自 System.ValueType 對象分配在 堆棧 上
System.Object和所有參考型別對象分配在 堆 上
三.裝箱和拆箱
裝箱
System.Object是所有實值型別的基類,所有實值型別必然可以隱式轉換成 System.Object類型。轉換髮生時,CLR需要做額外的工作把堆棧上的實值型別移到堆上。 這個操作為 裝箱。
步驟
1. 在堆上分配一個記憶體空間,大小等於需要裝箱的實值型別對象大小加上兩個參考型別對象都擁有的成員:類型對象
指標和同步區塊引述
2. 把堆棧上的實值型別對象複製到堆上新分配的對象
3. 返回一個指向堆上新對象的引用,並且儲存到堆棧上被裝箱的那個實值型別的對象裡
這些步驟不需程式員自己編寫,在出現裝箱的地方,編譯器會自動加上執行以上功能的中間代碼。
拆箱
裝箱的反操作,把堆中的對象複製到堆棧中,並且返回其值
過程中,拆箱操作將 判斷拆箱的物件類型 和 將要被複製的實值型別引用 是否一致,不一致將拋出一個 InvalidCastException的異常
影響: 裝箱與拆箱意味著堆和堆棧空間的一系列操作,這些操作對效能代價很大,尤其是在速度相對於堆棧慢很多的堆操作,並且這些操作可能引發記憶體回收。
適用場合:
1. 實值型別格式化輸出 (裝箱)
以下代碼可編譯執行,但會引發一次不必要的裝箱操作
int i = 10; Console.WriteLine("i的值是:"+i);
最佳化後,不在涉及裝箱
int i = 10; //ToString方法得到一個字串對象,字串是參考型別 Console.WriteLine("i的值是:o{0}"+i.ToString());
2.System.Object類型的容器
常用的容器類如 ArrayList就是System.Object容器,任何實值型別被放入ArrayList的對象中,都會引發一次裝箱操作,相應的取出引發一次拆箱操作。
因此,在可以確定類型的情況下應該使用泛型技術而避免使用System.Object類型容器。
三. C#中是否全域變數
C#中沒有傳統意義的全域變數,在C#程式中,任何對象資料都必須屬於某個類型。
通過公用靜態變數,可以實現以前全域變數的所有功能。如asp.net程式中使用Application對象和使用設定檔儲存可配置資料等。
四. 結構 struct
可看作 一個微型的類。 支援 私人成員變數和公用成員變數
支援屬性和方法
支援建構函式 但 不能自訂無參的構造方法,C#編譯器會為結構添加一個無參公用構造
函數 在該方法中成員被自動化為0。也不能在定義時設定初始值
支援重寫定義在System.Object中的虛方法
不支援繼承
繼承自 System.ValueType 類,故struct是實值型別。
經常用於儲存資料的集合,但那些涉及複雜操作的類型,應被設計成類而不是結構。
五. 類型初始化器
1.概念
在.NET中,每個類型都有一個初始化器。可以自己定義初始化器,也可以使用預設的編譯器添加的初始化器
類型的初始化器擁有和類型相同的名字 以 static 關鍵字定義,並且沒有參數,也沒有任何傳回值。
類型初始化器不同於建構函式,初始化器不能被顯式地調用,並且每個類型只能有一個類型初始化器。 CLR保證
的任何靜態成員被使用之前,類型的初始化器會被調用。這個概念和靜態成員變數的初始設定式本質上沒區別。
public class CCtor { //初始設定式 public static int m_Int = 1; //初始化器 static CCtor() { m_Int = 2; } //建構函式 public CCtor() { Console.WriteLine("建構函式調用。"); } static void Main(string[] args) { Console.Read(); } }
C# 編譯器在編譯時間,會把所有靜態成員變數初始設定式加入到初始化器中,並且添加的位置在自訂的初始化器內容之前。
2.調用策略
在中間代碼中初始化器的名字為cctor()。 兩種調用策略
1). 在類型不具有BeforeFieldInit中繼資料屬性時,CLR將在類型的任何成員被使用之前調用初始化器。
2). 在當類型具有BeforeFieldInit中繼資料屬性時,CLR可以自由調度初始化器的調用,只需確保任何靜態成員變數在使用之前初始化器已經被調用。
六. 方法參數的的傳遞方式
關鍵字 |
簡要說明 |
Ref |
引用傳遞參數,需要在傳遞前初始化 |
Out |
引用傳遞參數,需要在返回前初始化 |
Params |
可以指定在參數數目可變處採用參數的方法參數 |
1). ref和out關鍵字
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Test{ class useles { static void Main(string[] args) { int i = 0; Console.WriteLine("原始值:" + i.ToString()); NotRef(i); Console.WriteLine("調用非引用傳遞參數方法後:" + i.ToString()); Ref(i); Console.WriteLine("調用引用傳遞參數方法後:"+i.ToString()J); Console.Read(); } //不使用ref關鍵字來傳遞參數 public static void NotRef(int i) { i++; } //使用ref關鍵字來傳遞參數 public static void Ref(int i) { i++; } }}
結果為 0,0,1
NotRef方法並不以引用方式傳遞參數,所以方法得到的是整數i的一個副本,怎麼操作,與原資料無關。
2). params
params是一個非常實用的關鍵字,它允許方法在定義時不確定參數的數量,類似於數組參數。
缺點: 方法聲明了params參數後,不允許在其後面再有任何參數。
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Test{ class useles { //不使用params關鍵字來傳遞參數 public static void NotParams(Object[] par) { foreach (Object obj in par) Console.WriteLine(obj); } //使用params關鍵字來傳遞參數 public static void ParamsParam(params Object[] par) { foreach (Object obj in par) Console.WriteLine(obj); } //測試 static void Main(string[] args) { String s = "我是字串"; int i = 10; double f = 2.3; //需現聲明數組對象以通過其傳入 Object[] par = new Object[3] { s, i, f }; NotParams(par); //params關鍵字方式 ParamsParam(s, i, f); Console.Read(); } }}
七. 存取層級
類型存取層級
名稱 |
C#中關鍵字 |
簡述 |
Private |
private |
同一類型的所有方法或類型中的內嵌類型的所有方法可訪問 |
Family |
protected |
同一類型的所有方法、類型中的內嵌類型的所有方法和衍生類別型中的所有方法可訪問 |
Assemly |
internal |
同一程式集中的所有方法可訪問 |
Family&Assemly |
沒有實現 |
同時滿足Family約束和Assemly約束的所有方法 |
Assemly or Family |
protected internal |
所有滿足Assembly約束或滿足Family約束的方法 |
Public |
public |
所有方法都可訪問 |
類型成員的存取層級
類型 |
C#中預設的可訪問性層級 |
可設定的可訪問性層級 |
Enum |
public |
不可設定 |
Class |
private |
pubic protected internal private protected internal |
Interface |
public |
不可設定 |
Struct |
private |
public internal private |
八. 深複製與淺複製
淺複製
複製一個對象時,複製原始對象中所有的非靜態實值型別成員和所有的參考型別成員的引用。
深複製
不因複製所有的非靜態實值型別成員,而且複製所有參考型別成員的實際對象。
基類System.Object已經為所有類型都實現了淺複製 object MemberwiseClone() 方法。
介面 ICloneable 只包含了一個方法 Clone()。繼承此介面的類根據取捨不同實現Clone()可實現為深複製或前複製。
也可以用其他方式實現深複製,不局限與IConeable介面的Clone()方法。
可被繼承的類應避免實現IConeable介面,因為那樣將導致所有子類都必須實現ICloneable介面。
九. 迴圈
while 和 do…while經常用在迴圈不確定的場合,死迴圈危險係數較高。
for 和 foreach 經常用來遍曆數組和集合。for迴圈功能強大
foreach用來遍曆那些實現了IEnumberable的容器類型。數組和常用容器類如ArrayList、List等都實現了IEnumberable介面。 但它有一定限制
不能改變項目值,不能對項目賦值,不能通過屬性為項目內部成員賦值。 但項目的公用方法可以被調用。
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Test{ class useles { public struct A { public int _i; //通過屬性改變成員 public int I { set { _i = value; } } //通過公用方法改變成員 public void ModifyI(int i) { _i=i; } public override string ToString() { return _i.ToString(); } } //測試 static void Main(string[] args) { A[] array = new A[3]; foreach (A a in array) { //a=new A(1); //編譯不通過,不能改變項目 //a._i=1; //編譯不通過,不能改變項目成員變數 //a.I=1; //編譯不通過,不能通過屬性改變成員變數 a.ModifyI(1); Console.WriteLine(a); } } }}
十. using機制
1)概述
.NET的環境中,託管的資源由.NET的記憶體回收機制來釋放
非託管的資源需要程式員自己手動地釋放。
兩種主動和被動釋放非託管資源方式。
IDisposable介面的Dispose方法
類型自己的Finalize方法
任何帶有非託管資源的類型,都有必要實現IDisposable的Dispose方法,並且在使用完這些類型之後需要手動地調用對象的Dispose方法來釋放對象中的非託管資源。
如果類型正確地實現了Finalize方法,即使Dispose方法不被調用,非託管資源也最終會被釋放,但那時資源已被很長時間無謂地佔用了
2)using語句提供了一個高效的調用對象Dispose方法的方式。
對於任何實現IDisposable介面的類型,都可使用using語句,而對於那些沒有實現IDisposable介面的類型,使用using則導致編譯錯誤
using 語句能夠保證使用的對象的Dispose方法在using語句塊結束時被調用,無論是否有異常被拋出。
C#編譯器在編譯時間為using語句加上try/finally語句塊。本質與異常捕捉語句一樣,但是它更簡潔。