1.Class的可見性有public和internal兩種,public對所有程序集都可見,internal僅對其所在的程序 集可見。默認是public的。
2.友元程序集,
使用friend assembly可以實現單元測試,而不使用反射技術。
書上講的是按照命令行編譯。
我測試用的是vs2005的solution,如下:
3.成員的可訪問性
成員默認是private的,接口類型的成員都是public的。
子類重寫父類的成員時,原始成員與重寫成員要有相同的可訪問性——C#的約束;CLR的約束是,重寫 成員的可訪問性不能更低。
CLR和C#是不一樣的,如表:
CLR術語 C#術語 Private private Family protected Family and Assembly 不支持 Assembly internal Family or Assembly protected internal Public public4.靜態類
static只能用於class,不能用於struct,因為CLR要求值必須實例化,而且不能控制實例化過程。
C#對靜態類的約束:
靜態類必須直接從System.Object派生
靜態類不能實現任何接口
靜態類只能定義靜態成員:字段,方法,屬性,事件
靜態類不能用作:字段,方法,參數,局部變量。
在MSIL中,不會為靜態類生成ctor,會將其標記為abstract和sealed
5.部分類
CLR不支持partial,只是C#的語法。所以某個類型的源碼必須使用同一種編程語言
6.組件,多態和版本控制
.NET版本號2.7.1.34,包含4個部分:主版本號,次版本號,內部版本號,修訂版本號。
修訂版本,向後兼容,改變內部/修訂版本號;
發布新版本,不向後兼容,改變主/次版本號。
多態中,子類重寫父類的虛方法,會引起版本控制問題,即父類發生改變,其版本低於子類版本,會 導致子類行為變化。
C# 5個用於 類/類成員 的 影響組件版本控制 的 關鍵字:
abstract:用於類/類成員
virtual和override:用於成員
sealed:用於類/類成員。用於成員時,僅用於重寫了虛方法的方法。
new,用於類/類成員/常量/字段
C#調用虛方法:
CLR允許類中定義多個"同名方法",僅僅是返回類型不同,IL允許這樣做;C#不允許,忽略返回值的類 型,相應的用"轉換操作符"實現IL中的"同名方法"。
調用方法相應的MSIL:
一個是call,用來調用靜態方法,實例方法和虛方法。必須要指定調用方法的類型(對於靜態方法)或 者對象(對於實例方法/虛方法),如果在該類型/對象中找不到該方法,會檢查其基類來匹配方法。
另一個是callvirt,用來調用實例方法和虛方法,不能用於調用靜態方法。必須要指定調用方法的實 例對象,如果這個對象為null,會拋出NullReferenceException異常,這意味著每次調用前都會有額外的 null檢查,從而比調用call慢一些。
如下代碼所示:
public sealed class Program { public Int32 GetFive() { return 5; } public static void Main() { Program p = null; Int32 x = p.GetFive(); //在C#中,使用callvirt,會拋出 NullReferenceException異常 } }
在C#編譯器中,使用callvirt調用所有實例方法(包括虛方法),使用call調用所有靜態方法。對於其 他的編譯器,這一點不能保證,所以在虛方法和非虛方法之間改動而不重新編譯,會產生無法預測的問題 。
C#使用call而不用callvirt調用虛方法的特例:ToString,見下:
internal class SomeClass { public override string ToString() { return base.ToString(); } }
這時候,生成call的IL代碼。因為如果使用callvirt,意味著這時一個虛方法,從而遞歸執行該方法 ,直到AppDomain的堆棧溢出。
在調用值類型定義的方法時,使用call。這是因為,首先,值類型是密封的,從而不存在虛方法;另 外,值類型永遠不會為null,所以永遠不會拋出NullReferenceException異常;再者,如果使用callvirt ,就要使用裝箱機制,性能會有極大影響。
在設計class的過程中,要盡量少定義虛方法。取代辦法:可以定義一組重載方法,經其中最復雜的方 法虛擬化,而將所有有用的重載非虛擬化,示例如下:
public class Set { private Int32 m_length = 0; //這個有用的重載是非虛擬的 public Int32 Find(Object value) { return Find(value, 0, m_length); } //這個有用的重載是非虛擬的 public Int32 Find(Object value, Int32 startIndex) { return Find(value, 0, m_length - startIndex); } //功能最豐富的方法是虛擬的,可以被重寫 public Int32 Find(Object value, Int32 startIndex, Int32 endIndex) { .//具體實現 } }
sealed密閉類盡量使用。將sealed改為非密閉的容易,反之困難;性能也快,因為sealed一定是非虛 擬的,從而編譯器不用考慮其派生類,而虛方法的性能不如非虛方法;因為密閉了,所以安全性和可預測 性也就高。
子類中有父類的方法,會警告,不會報錯。這時要使用new關鍵字,告訴CLR,新舊方法沒有任何關系 。
補充:
1.靜態成員是在堆上分配的。CLR在創建TYPE對象的時候,靜態數據成員也會創建
2.實例類中可以有靜態成員