类型和成员基础
類型的成員
類型中可以定義多種成員,本篇不作深入講解,后續(xù)再逐一介紹。
- 常量:數(shù)據(jù)值值恒定不變的符號(hào)。
- 字段:只讀或可讀/可寫的數(shù)據(jù)值。
- 實(shí)例構(gòu)造器:將對(duì)象的實(shí)例字段初始化的特殊方法。
- 類型構(gòu)造器:將類型的靜態(tài)字段初始化的特殊方法。
- 方法:更改或查詢類型或?qū)ο鬆顟B(tài)的函數(shù)。
- 操作符重載:操作符重載實(shí)際是方法,定義了當(dāng)操作符作用于對(duì)象時(shí),應(yīng)該如何操作該對(duì)象。
- 轉(zhuǎn)換操作符:定義如何隱式或顯式將對(duì)象從一種類型轉(zhuǎn)換為另一種類型的方法。
- 屬性:屬性允許用簡(jiǎn)單的、字段風(fēng)格的語(yǔ)法設(shè)置或查詢類型或?qū)ο蟮倪壿嫚顟B(tài),同時(shí)保證狀態(tài)不被破壞。
- 事件:靜態(tài)事件允許類型向一個(gè)或多個(gè)靜態(tài)或?qū)嵗椒òl(fā)出通知。實(shí)例事件允許對(duì)象向一個(gè)或多個(gè)靜態(tài)或?qū)嵗椒òl(fā)送通知。
- 類型:類型可定義其他嵌套類型。
類型的可見(jiàn)性
類型的可見(jiàn)性可以指定為public或internal。public類型對(duì)所有程序集可見(jiàn),而internal類型僅對(duì)定義程序集中的所有代碼可見(jiàn)。定義類型時(shí)不顯示指定可見(jiàn)性,C#編譯器會(huì)默認(rèn)指定為internal。
友元程序集
某些情況下,我們希望將可見(jiàn)性為internal的類型開(kāi)放給另外一個(gè)特定的程序集訪問(wèn),CLR和C#通過(guò)友元程序集(friend assembly)提供這方面的支持。生成程序集時(shí),使用System.Runtime.CompilerServices命名空間下的InternalsVisibleTo特性標(biāo)注友元程序集。該特性獲取標(biāo)識(shí)友元程序集名稱和公鑰的字符串參數(shù)。示例如下:
創(chuàng)建類庫(kù)項(xiàng)目FriendA和FriendB,分別將類命名為ClassA(Internal)和ClassB
internal class ClassA{...}在FriendA中添加以下特性標(biāo)記(不要忘記添加System.Runtime.CompilerServices命名空間)
[assembly:InternalsVisibleTo("TeamB,PublicKey=0024...88c4")]在FriendB項(xiàng)目中引用FriendA程序集,成功訪問(wèn)聲明為Internal的類ClassA
成員的可訪問(wèn)性
定義類型的成員時(shí),可以指定成員的可訪問(wèn)性。CLR自己定義了一組可訪問(wèn)性修飾符,但每種編程語(yǔ)言在向成員應(yīng)用可訪問(wèn)性時(shí),都選擇了自己的一組術(shù)語(yǔ)以及相應(yīng)的語(yǔ)法。下表總結(jié)了應(yīng)用于成員的可訪問(wèn)性修飾符。
| Private | private | 成員只能由定義類型或嵌套類型中的方法訪問(wèn) |
| Family | protected | 成員只能由定義類型、嵌套類型或者任意程序集中的派生類型中的方法訪問(wèn) |
| Family and Assembly | 不支持 | 成員只能由定義類型、嵌套類型或同一程序集中定義的派生類型中的方法訪問(wèn) |
| Assembly | internal | 成員只能由定義程序集中的方法訪問(wèn) |
| Family Or Assembly | protected internal | 成員只能由定義程序集中的方法、嵌套類型或任意程序集中的派生類型中的方法訪問(wèn) |
| Public | public | 成員可由任何程序集的任何方法訪問(wèn) |
在C#中,如果沒(méi)有顯示聲明成員的可訪問(wèn)性,編譯器通常默認(rèn)選擇private。(接口除外,接口的可訪問(wèn)性自動(dòng)設(shè)置為public)
派生類型重寫基類型定義的成員時(shí),C#編譯器要求原始成員和重寫成員具有相同的可訪問(wèn)性。(但CLR不同,CLR允許放寬但不允許收緊成員的可訪問(wèn)性限制)
靜態(tài)類
類可以聲明為static,以指示它僅包含靜態(tài)成員。靜態(tài)類不能實(shí)例化,static關(guān)鍵字只能應(yīng)用于類,不能應(yīng)用于結(jié)構(gòu)。靜態(tài)類相當(dāng)于一個(gè)sealed abstract類。
C#編譯器對(duì)靜態(tài)類進(jìn)行了如下限制:
組件、多態(tài)和版本控制
本節(jié)討論如何通過(guò)CLR和編程語(yǔ)言提供的功能來(lái)自動(dòng)適應(yīng)程序集可能發(fā)生的變化。將一個(gè)程序集中定義的類型作為另一個(gè)程序集中的類型的基類使用時(shí),如果基類版本低于派生類,就可能造成派生類的行為失常。C#提供了5個(gè)能影響程序集版本控制的關(guān)鍵字,可將它們應(yīng)用于類型以及類型成員。
| abstract | 不能構(gòu)造該類型的實(shí)例 | 為了構(gòu)造派生類的實(shí)例,派生類型必須重寫并實(shí)現(xiàn)這個(gè)成員 | (不允許) |
| virtual | (不允許) | 可由派生類重寫 | (不允許) |
| override | (不允許) | 派生類型重寫基類型的成員 | (不允許) |
| sealed | 不能用作基類型 | 不能被派生類型重寫,只能講該類關(guān)鍵字應(yīng)用于重寫虛方法的方法 | (不允許) |
| new | 應(yīng)用于嵌套類型、方法、屬性、事件、常量或字段,表示該成員與基類中相似的成員無(wú)任何關(guān)系 | ||
CLR如何調(diào)用虛方法、屬性和事件
編譯器在編譯代碼時(shí),會(huì)在程序集方法定義表的記錄項(xiàng)中添加一組標(biāo)志,指明方法是實(shí)例方法、虛方法還是靜態(tài)方法。生成調(diào)用代碼時(shí),編譯器會(huì)檢查方法定義的標(biāo)志,判斷如何生成IL代碼來(lái)正確調(diào)用方法。CLR提供兩個(gè)方法調(diào)用指令:
call
該指令可調(diào)用靜態(tài)方法、實(shí)例方法和虛方法。用call指令調(diào)用靜態(tài)方法,必須指定方法的定義類型。用call指令調(diào)用實(shí)例方法或虛方法,必須指定引用了對(duì)象的變量。call指令假定該變量不為null。
callvirt
該指令可調(diào)用實(shí)例方法和虛方法,不能調(diào)用靜態(tài)方法。用callvirt指令調(diào)用實(shí)例方法或虛方法,必須指定引用了對(duì)象的變量。用callvirt指令調(diào)用非虛實(shí)例方法,變量的類型指明了方法的定義類型。用callvirt指令調(diào)用虛實(shí)例方法,CLR調(diào)查發(fā)出調(diào)用的對(duì)象的實(shí)際類型,然后以多態(tài)方式調(diào)用方法。為了確定類型,發(fā)出調(diào)用的變量不能為null。JIT編譯器會(huì)生成代碼來(lái)驗(yàn)證變量的值是不是null。如果是,callvirt指令造成CLR拋出NullReferenceException異常。這種額外的檢查造成callvirt指令的執(zhí)行速度比call指令稍慢。
為什么C#編譯器不直接生成call指令呢?
因?yàn)镃#團(tuán)隊(duì)認(rèn)為,JIT編譯器應(yīng)生成代碼來(lái)驗(yàn)證發(fā)出調(diào)用的對(duì)象不為null。
下面來(lái)看一段簡(jiǎn)單的代碼
class Program {static void Main(string[] args){Console.WriteLine();Object o = new object();o.GetHashCode();o.GetType();} }編譯上述代碼,查看IL如下:
.method private hidebysig static void Main(string[] args) cil managed {.entrypoint// Code size 28 (0x1c).maxstack 1.locals init ([0] object o)IL_0000: nopIL_0001: call void [mscorlib]System.Console::WriteLine()IL_0006: nopIL_0007: newobj instance void [mscorlib]System.Object::.ctor()IL_000c: stloc.0IL_000d: ldloc.0IL_000e: callvirt instance int32 [mscorlib]System.Object::GetHashCode()IL_0013: popIL_0014: ldloc.0IL_0015: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()IL_001a: popIL_001b: ret } // end of method Program::Main我們清楚的可以看見(jiàn),C#編譯器用call指令調(diào)用Console的WriteLine方法,接著用callvirt調(diào)用GetHashCode和GetType方法。
轉(zhuǎn)載于:https://www.cnblogs.com/Answer-Geng/p/7440874.html
總結(jié)
- 上一篇: 二分图-匈牙利算法模板
- 下一篇: 用Excel制作改进前后漏斗模型图比较