第 57 講:反射(一):反射的概念和基本使用
考慮一種情況。如果我們需要檢查你的這個(gè)程序里到底有多少個(gè)數(shù)據(jù)類(lèi)型的時(shí)候,我們基本的代碼是無(wú)法實(shí)現(xiàn)的。因?yàn)樵诨镜?C# 語(yǔ)法里,是無(wú)法檢測(cè)代碼自身的。既然無(wú)法檢測(cè)自身,那我們應(yīng)該如何去完成這項(xiàng)復(fù)雜的任務(wù)呢?本篇章就會(huì)給大家介紹一個(gè)新的機(jī)制:反射(Reflection)。通過(guò)反射機(jī)制,我們可以通過(guò)寫(xiě)代碼,檢查代碼自身的基本信息。
Part 1 簡(jiǎn)單介紹一下 Type
類(lèi)型
既然要獲取一個(gè)類(lèi)型的基本數(shù)據(jù)信息,那么自然有一個(gè)數(shù)據(jù)類(lèi)型來(lái)表達(dá)這些數(shù)據(jù)信息才顯得比較合適,那么,Type
類(lèi)型就誕生了。Type
類(lèi)型是一個(gè)無(wú)法實(shí)例化的抽象類(lèi),但它本身能表達(dá)這個(gè)類(lèi)型里都帶有什么成員、有多少成員、成員類(lèi)型都是些什么等等。不過(guò),想要獲取這個(gè)類(lèi)型的基本信息,我們應(yīng)該如何去得到它呢?
我們有兩種方式:GetType
實(shí)例方法和 typeof
表達(dá)式。
1-1 GetType
無(wú)參實(shí)例方法
實(shí)際上,object
類(lèi)型里還帶有一個(gè)叫做 GetType
的、不是 virtual
修飾的實(shí)例方法。不是 virtual
修飾的方法意味著不能被重寫(xiě),而它放在 object
里是因?yàn)檫@個(gè)方法對(duì)任何數(shù)據(jù)類(lèi)型都有效。
舉個(gè)例子,假設(shè)我寫(xiě)了一個(gè) Student
類(lèi)型,它里面有 _age
、_gender
、_name
字段,也有 Age
、Gender
、Name
屬性,以及一個(gè)構(gòu)造器,傳入三個(gè)對(duì)應(yīng)賦值給字段的參數(shù)。那么,我們可以這么做:
請(qǐng)注意。這里的 GetType
方法是無(wú)參的方法,不需要你傳入?yún)?shù);另外,返回值是 Type
類(lèi)型,它里面就包裝好了你現(xiàn)在這個(gè)實(shí)例對(duì)象所處類(lèi)型里的基本成員信息。稍后我們進(jìn)一步對(duì) Type
類(lèi)型里的內(nèi)容進(jìn)行討論,現(xiàn)在我們暫時(shí)放一邊。
1-2 typeof
表達(dá)式
還記得 Enum
類(lèi)型嗎?這個(gè)類(lèi)型是給所有的枚舉類(lèi)型提供了基類(lèi)型的抽象類(lèi),它不可實(shí)例化,而且所有的枚舉類(lèi)型都從這個(gè)類(lèi)型派生,且枚舉類(lèi)型的繼承關(guān)系不可改變(必須從這個(gè)類(lèi)型派生)。
我們?cè)谑褂?Parse
方法的時(shí)候,由于 Enum
是封裝在枚舉類(lèi)型的基類(lèi)型上的,所以比所有枚舉類(lèi)型都要高一階。正是因?yàn)槿绱耍覀儫o(wú)法在 Enum
類(lèi)型上完成對(duì)具體枚舉類(lèi)型的字符串轉(zhuǎn)換的解析操作,所以我們第一個(gè)參數(shù)需要傳入一個(gè) typeof
表達(dá)式標(biāo)識(shí)這個(gè)類(lèi)型到底是哪個(gè)類(lèi)型。當(dāng)時(shí)我是這么說(shuō)的,對(duì)吧。
這個(gè) typeof
表達(dá)式的格式就是前面講解的那樣,在 typeof
后面添加一對(duì)小括號(hào),里面寫(xiě)上我們要獲取的 Type
類(lèi)型對(duì)象所屬的那個(gè)類(lèi)型。按前文 Student
類(lèi)型而言,我們也可這么書(shū)寫(xiě)代碼:
這么使用 typeof
表達(dá)式的執(zhí)行效果,應(yīng)和前面介紹的、通過(guò) GetType
實(shí)例方法獲取到的 Type
類(lèi)型的實(shí)例是完全一樣的東西。所以,你就當(dāng)成兩種獲取 Type
的不同方式就行。顯然,typeof
要方便很多,但它必須在小括號(hào)里寫(xiě)類(lèi)型名,因此這一點(diǎn)來(lái)說(shuō),如果是實(shí)例本身的話(huà),你只能通過(guò) GetType
方法來(lái)完成了。
Part 2 讓我們了解一下 Type
類(lèi)型里面的東西吧
下面,我們來(lái)說(shuō)一下,Type
類(lèi)型里到底有一些什么。因?yàn)?Type
類(lèi)型里的成員相當(dāng)多、相當(dāng)復(fù)雜,因此我們提出來(lái)一些重要的來(lái)說(shuō),其它就自己看、查資料就行。
2-1 ==
和 !=
運(yùn)算符重載
C# 提供了 Type
類(lèi)型的運(yùn)算符重載,這意味著我們直接可以使用 ==
和 !=
來(lái)完成對(duì)兩個(gè) Type
類(lèi)型是否表示的是同一種數(shù)據(jù)類(lèi)型就可以。
這個(gè)輸出結(jié)果應(yīng)該是為 true
的,即使獲取方式不同,但因?yàn)槎坚槍?duì)于 Student
類(lèi)型作比較。因?yàn)?Type
類(lèi)型判斷相等性只看表示類(lèi)型本身,所以結(jié)果是 true
。
2-2 Name
和 FullName
屬性
要想獲取這個(gè)類(lèi)型本身的名稱(chēng)的字符串寫(xiě)法的話(huà),我們可以使用這個(gè)操作來(lái)得到:
"Student"
。不過(guò),這個(gè)基本輸出有時(shí)候是不夠的,因?yàn)樗鼪](méi)有直接體現(xiàn)出數(shù)據(jù)類(lèi)型的位置(如果多個(gè)命名空間都包含這個(gè)類(lèi)型名稱(chēng)的類(lèi)型的話(huà),這樣寫(xiě)就無(wú)法區(qū)分它們),如果我們要想獲取這個(gè)數(shù)據(jù)類(lèi)型包含命名空間的絕對(duì)位置的話(huà),就需要使用 FullName
屬性來(lái)代替 Name
這樣顯示的結(jié)果就是帶命名空間的寫(xiě)法。假設(shè) Student
是在 CustomProject
命名空間下的話(huà),那么顯示結(jié)果就是 "CustomProject.Student"
。
實(shí)際上,object
里帶有的 ToString
方法,默認(rèn)顯示的結(jié)果就是這里說(shuō)的后者。所以在源代碼里,object
類(lèi)型的這一部分實(shí)現(xiàn)代碼是這樣的:
當(dāng)然,this
可以不寫(xiě)出來(lái)。這里寫(xiě)出來(lái)是為了體現(xiàn) 對(duì)象.GetType()
這個(gè)用法。
2-3 獲取成員信息
如果要繪制成員信息表的話(huà),我們需要用到的是如下的這些方法:

其它的成員(索引器,類(lèi)型轉(zhuǎn)換器和運(yùn)算符重載)沒(méi)有直接的方法可提供使用。下面我們來(lái)看一下,怎么使用。
假設(shè)我們就獲取 object
類(lèi)型里到底有什么數(shù)據(jù)信息,寫(xiě)法是這樣的:
這些返回值類(lèi)型匹配的類(lèi)型名稱(chēng)都在 System.Reflection
命名空間下,所以你需要先寫(xiě)一句 using System.Reflection;
才可以使用它們。
最后,你可能會(huì)得到如下的結(jié)果:
大概是這么一些東西。你似乎不能從輸出結(jié)果里找到字段信息和屬性信息,因?yàn)?object
類(lèi)型里沒(méi)有這些信息,object
類(lèi)型里是不包含字段和屬性的。
不過(guò),可以從輸出結(jié)果類(lèi)型里看到,輸出顯示的類(lèi)型名稱(chēng)有的加了 System
,有的沒(méi)有(比如 Boolean
),這個(gè)屬于底層的機(jī)制的問(wèn)題,我們不去討論;不過(guò)我們一定要注意的是,它輸出的字符串信息,一定是把所有數(shù)據(jù)類(lèi)型的 BCL 名稱(chēng)作為結(jié)果顯示出來(lái)的,而關(guān)鍵字的別名記法是不會(huì)呈現(xiàn)的。
順帶一提,
.ctor
是構(gòu)造器的意思。如果你看到輸出結(jié)果里是帶有.ctor
的名字的話(huà),那么這個(gè)必然是一個(gè)構(gòu)造器。
更進(jìn)一步。如果我們想要更精確搜索信息的話(huà),我們可以在 GetMethods
啊、GetConstructors
這些方法后傳入一個(gè)參數(shù),來(lái)表示你到底要搜索哪些信息。默認(rèn)的情況下,這些方法只會(huì)去找用 public
修飾過(guò)的成員;如果成員沒(méi)有用 public
修飾的話(huà),那么這些方法是無(wú)法找到它們的。
那么,如果去找到這些成員呢?我們需要在參數(shù)里傳入一個(gè) BindingFlags
枚舉類(lèi)型的參數(shù),來(lái)表示你搜索的范圍。比如我想找靜態(tài)的 public
方法的話(huà),我們需要做的是,傳入 BindingFLags.Static | BindingFlags.Public
來(lái)作為參數(shù)。
這樣的話(huà),我們就只看得到兩個(gè)方法了:
這個(gè) BindingFlags
枚舉類(lèi)型是一個(gè)特殊的枚舉類(lèi)型,在我們之前介紹枚舉類(lèi)型的時(shí)候,我們說(shuō)過(guò),我們有時(shí)候會(huì)使用位或運(yùn)算符來(lái)簡(jiǎn)化枚舉類(lèi)型傳參的過(guò)程,以節(jié)省執(zhí)行效率,前提是我們必須得給每個(gè)枚舉的成員設(shè)置 1、2、4、8 這樣的 2 的次冪作為特征值。這個(gè) BindingFlags
就是這么一個(gè)已經(jīng)這樣設(shè)計(jì)過(guò)的枚舉類(lèi)型,因此我們用位或運(yùn)算符表示疊加兩個(gè)搜索的條件內(nèi)容。請(qǐng)注意,這里 BindingFlags.Public | BindingFlags.Static
必須都寫(xiě)上,即使你只想找跟訪問(wèn)修飾符無(wú)關(guān)的靜態(tài)方法,也必須帶上訪問(wèn)修飾符的對(duì)應(yīng)的 BindingFlags
枚舉數(shù)值。如果你找的不是 public
修飾的成員的話(huà),那么就寫(xiě) BindingFlags.NonPublic
;反之就寫(xiě) BindingFlags.Public
,別無(wú)其它;如果兩種情況都找的話(huà),就把它倆位或起來(lái)。
順帶一提。BindingFlags
枚舉也在 System.Reflection
命名空間下。
Part 3 了解一下 XXXInfo
類(lèi)型吧
這里 XXXInfo
的 XXX
不是實(shí)際類(lèi)型名稱(chēng)的一部分,而是一個(gè)替換。實(shí)際上,就是在說(shuō)前面 ConstructorInfo
、FieldInfo
這些東西。
因?yàn)榍拔臎](méi)有細(xì)致說(shuō)明這些類(lèi)型的具體用法,因此這里說(shuō)一下。
本節(jié)內(nèi)容不給出任何的用法,僅給出常用的屬性和方法可能對(duì)你有幫助和經(jīng)常使用的成員。這些成員不會(huì)用的話(huà),請(qǐng)上網(wǎng)查資料,或者到時(shí)候看視頻學(xué)習(xí)。每個(gè)都講有點(diǎn)太多了。
3-1 FieldInfo
我們?nèi)绻麖哪硞€(gè)地方獲取到了一個(gè)字段信息的話(huà),它一般是用 FieldInfo
表示的。以這個(gè)類(lèi)型表示出來(lái)的話(huà),里面有很多成員可提供我們使用。
屬性
IsPublic
和IsPrivate
:檢測(cè)字段本身是不是public
的,以及是不是private
的;IsInitOnly
:檢測(cè)字段是不是只在構(gòu)造器里初始化后,就不能再修改了(即標(biāo)記了readonly
修飾符);IsStatic
:檢測(cè)字段是不是靜態(tài)的;FieldType
:獲取這個(gè)字段的本身的類(lèi)型,用的是Type
類(lèi)型表達(dá)結(jié)果。
方法
GetValue
:獲取這個(gè)字段的值(需要傳入一個(gè)參數(shù):參數(shù)表示你到底是對(duì)哪個(gè)實(shí)例對(duì)象獲取其數(shù)值,如果是this
就寫(xiě)this
,如果是obj
就寫(xiě)obj
;如果字段本身是靜態(tài)的,那么就跟實(shí)例無(wú)關(guān),那這個(gè)參數(shù)就傳null
進(jìn)去);SetValue
:給這個(gè)字段進(jìn)行賦值(需要傳入兩個(gè)參數(shù):第一個(gè)參數(shù)表示實(shí)例,和GetValue
方法里的這個(gè)參數(shù)意義是一樣的;第二個(gè)參數(shù)表示賦的值是什么。如果是 3 就寫(xiě) 3,如果是 0.8 就寫(xiě) 0.8,如果是null
就寫(xiě)null
)。
3-2 ConstructorInfo
類(lèi)型
同樣地,如果要看構(gòu)造器的基本信息,那么用的是 ConstructorInfo
表示的。另外,這個(gè)類(lèi)型里面沒(méi)有我們常用到的屬性成員,因此這里僅給的是方法成員。
Invoke
:傳入一個(gè)參數(shù),是一個(gè)數(shù)組,這個(gè)數(shù)組代表了你要調(diào)用的構(gòu)造器的所有參數(shù),比如說(shuō)Student
的構(gòu)造器要三個(gè)參數(shù)name
、age
和gender
,那么調(diào)用這個(gè)方法的時(shí)候,按數(shù)組的傳參方式得寫(xiě)成new object[] { "Sunnie", 25, Gender.Male }
;GetParameters
:獲取這個(gè)構(gòu)造器的所有參數(shù)信息,用ParameterInfo[]
表示結(jié)果(ParameterInfo
類(lèi)型稍后介紹)。
3-3 PropertyInfo
類(lèi)型
要獲取一個(gè)屬性的基本信息,我們用的是 PropertyInfo
類(lèi)型。
屬性
CanRead
和CanWrite
:表示這個(gè)屬性是否可讀或者可寫(xiě);PropertyType
:表示這個(gè)屬性的類(lèi)型,用Type
類(lèi)型的實(shí)例表示返回值結(jié)果;GetMethod
和SetMethod
:表示這個(gè)屬性的get
方法和set
方法的具體信息,用MethodInfo
類(lèi)型(一會(huì)兒才講到)表達(dá)出來(lái)。
方法
GetAccessors
:獲取這個(gè)屬性的所有取值器或賦值器,并用MethodInfo[]
類(lèi)型表示其結(jié)果;GetIndexerParameters
:如果屬性是索引器的話(huà),那么這個(gè)方法將會(huì)正常返回該索引器的所有參數(shù)信息(用ParameterInfo[]
表示,其中的ParameterInfo
稍后說(shuō)明);GetValue
:相當(dāng)于調(diào)用該屬性的get
方法以獲取結(jié)果,需要傳入一個(gè)參數(shù)表示到底是獲取哪個(gè)實(shí)例的結(jié)果。如果實(shí)例不存在(換句話(huà)說(shuō)就是這個(gè)屬性是靜態(tài)屬性的話(huà)),傳入null
即可;SetValue
:相當(dāng)于調(diào)用該屬性的set
方法以賦值過(guò)去一個(gè)數(shù)值;傳入兩個(gè)參數(shù):第一個(gè)參數(shù)是實(shí)例,第二個(gè)參數(shù)則是賦的值。
3-4 MethodInfo
類(lèi)型
要獲取一個(gè)方法的基本信息,用的是 MethodInfo
類(lèi)型。
屬性
ReturnType
:獲取這個(gè)方法的返回值類(lèi)型,用Type
類(lèi)型實(shí)例表達(dá)結(jié)果;IsPublic
和IsPrivate
:看這個(gè)方法的修飾符是不是public
或者private
;IsHideBySig
:如果這個(gè)方法標(biāo)記了new
修飾符表示直接阻斷繼承鏈的話(huà),這個(gè)屬性獲取的就是這個(gè)效果:看是不是這個(gè)方法被派生類(lèi)型隱藏掉了;IsStatic
、IsVirtual
、IsAbstract
、IsFinal
:獲取這個(gè)方法是不是用static
、virtual
、abstract
或者sealed
修飾過(guò)。
方法
GetBaseDefinition
:如果這個(gè)方法是重寫(xiě)后的方法的話(huà),那么這個(gè)方法會(huì)幫你去找這個(gè)方法原始定義的位置在哪里;GetParameters
:獲取這個(gè)方法的所有參數(shù)信息,用ParameterInfo[]
表示結(jié)果(ParameterInfo
類(lèi)型稍后介紹);Invoke
:調(diào)用該方法,和ConstructorInfo
里的Invoke
方法使用效果完全一樣。
3-5 EventInfo
類(lèi)型
要獲取一個(gè)事件的基本信息,用的是 EventInfo
表示的結(jié)果。
屬性
AddMethod
和RemoveMethod
:獲取事件的add
和remove
訪問(wèn)器對(duì)應(yīng)的方法信息(用MethodInfo
表示結(jié)果);IsMulticast
:表示事件是不是多播事件(即回調(diào)函數(shù)列表里不止一個(gè)方法)。
方法
AddEventHandler
:相當(dāng)于調(diào)用事件的add
訪問(wèn)器,去追加一個(gè)委托對(duì)象。傳入兩個(gè)參數(shù),第一個(gè)是傳入事件是在哪個(gè)實(shí)例里完成的(如果是靜態(tài)事件則傳入null
即可),第二個(gè)參數(shù)是委托對(duì)象;RemoveEventHandler
:相當(dāng)于調(diào)用事件的remove
訪問(wèn)器,去刪減一個(gè)委托對(duì)象。傳入兩個(gè)參數(shù),和AddEventHandler
的調(diào)用是一樣的參數(shù)類(lèi)型。
3-6 ParameterInfo
類(lèi)型
該類(lèi)型并不屬于某個(gè)成員直接反饋出來(lái)的結(jié)果,而是 MethodInfo
這些會(huì)帶有參數(shù)的成員信息類(lèi)型,通過(guò)獲取參數(shù)才能得到的信息。
屬性
IsOut
:表示該參數(shù)是不是out
修飾過(guò)的;ParameterType
:表示該參數(shù)的類(lèi)型,用Type
類(lèi)型表示;Member
:表示這個(gè)參數(shù)所處的成員是哪個(gè),用MemberInfo
表示其結(jié)果。這里的MemberInfo
是所有這些XXXInfo
的基類(lèi)型,所以到時(shí)候記得判斷類(lèi)型以及強(qiáng)制轉(zhuǎn)換;Position
:獲取該參數(shù)到底是整個(gè)參數(shù)表列里的第幾個(gè)參數(shù):第一個(gè)參數(shù)返回 0,第二個(gè)參數(shù)返回 1,以此類(lèi)推。
Part 4 非零下標(biāo)數(shù)組的實(shí)例化
數(shù)組在我們之前介紹里講過(guò),C# 是無(wú)法創(chuàng)建下標(biāo)不為 0 開(kāi)始的數(shù)組的。但是,因?yàn)?.NET 還含有比如 VB.NET(簡(jiǎn)稱(chēng) VB 了)之類(lèi)的編程語(yǔ)言,它們支持非 0 下標(biāo)開(kāi)始的數(shù)組,所以 .NET 環(huán)境下自然是允許這樣的東西存在的。那么,C# 里要?jiǎng)?chuàng)建這樣的數(shù)組,辦法是怎么樣的呢?
實(shí)際上,我們直接的 new
是無(wú)法創(chuàng)建這樣的數(shù)組的,它們默認(rèn)下標(biāo)從 0 開(kāi)始;但是我們可以使用反射來(lái)創(chuàng)建,這需要我們使用一個(gè)庫(kù) API,叫做 CreateInstance
。這個(gè)方法是一個(gè)靜態(tài)方法,包含在 Array
這個(gè)類(lèi)型里。我們說(shuō)過(guò),所有的數(shù)組都從 Array
這個(gè)引用類(lèi)型自動(dòng)派生下來(lái),雖然我們語(yǔ)法上實(shí)例化好像看不太出來(lái),但實(shí)際上它隱式從這里派生。而這里的這個(gè) CreateInstance
就是其中的一個(gè)靜態(tài)方法。
我們給大家介紹一個(gè)例子,然后告訴大家這個(gè)方法應(yīng)該怎么用。
首先我們?cè)囍@么書(shū)寫(xiě)代碼。第 3 行代碼里我們用到了這個(gè)方法。方法傳入三個(gè)參數(shù)。
第一個(gè)參數(shù)是我們要實(shí)例化的數(shù)組的元素的類(lèi)型是什么。這里因?yàn)轭?lèi)型要當(dāng)成參數(shù)(確實(shí)它需要接收一個(gè) Type
類(lèi)型的對(duì)象),因此我們需要 typeof
表達(dá)式。假設(shè)我們這里需要用 typeof(int)
以創(chuàng)建一個(gè) int
類(lèi)型為元素的數(shù)組,所以第一個(gè)參數(shù)我們寫(xiě)為 typeof(int)
。
第二個(gè)參數(shù)是這個(gè)數(shù)組的每一個(gè)維度的長(zhǎng)度。這個(gè)數(shù)組特別神奇,比如我要?jiǎng)?chuàng)建一個(gè)二維數(shù)組,那么這里這個(gè)參數(shù)就應(yīng)該是一個(gè)兩個(gè)元素的 int[]
類(lèi)型對(duì)象。如果是二維數(shù)組,那么第一個(gè)維度(行)有 m 個(gè)元素,那么就寫(xiě) m;而第二個(gè)維度有 n 個(gè)元素,就寫(xiě) n。因此,如果這樣的話(huà),這里寫(xiě)的應(yīng)該就是 new int[] { m, n }
這樣的表達(dá)式。
第三個(gè)參數(shù)則是每一個(gè)維度的第一個(gè)起始元素的下標(biāo)是多少。這就是我們這里說(shuō)的非 0 下標(biāo)起始的數(shù)組了。假設(shè)還是一個(gè)二維數(shù)組,那么這里仍然是一個(gè)兩個(gè)元素構(gòu)成的一個(gè) int[]
數(shù)組對(duì)象。而假設(shè)第一個(gè)維度是從 1 下標(biāo)開(kāi)始計(jì)算的話(huà),我們就傳入 1,下標(biāo) 2 開(kāi)始就傳入 2,這樣的意思。默認(rèn)是 0 所以正常情況應(yīng)該對(duì)應(yīng)的表達(dá)式是 new int[] { 0, 0 }
這樣的東西;不過(guò)我們這里故意要非 0 下標(biāo)開(kāi)始的數(shù)組,所以就不能是 0。
然后我們?cè)囍训玫降臄?shù)組轉(zhuǎn)換為 int[]
后賦值給 a
變量。這樣可以方便讀寫(xiě)數(shù)據(jù)。
這樣的話(huà),我們來(lái)看看,程序會(huì)不會(huì)出問(wèn)題。

很遺憾,我們看到了異常的信息。它走到了我們?cè)即a的第 16 行,這段代碼會(huì)捕獲一個(gè)叫 InvalidCastException
的異常類(lèi)型。這個(gè) InvalidCastException
一般出現(xiàn)在類(lèi)型轉(zhuǎn)換失敗或者異常的時(shí)候會(huì)有這樣的異常拋出。所以很顯然這暗示我們類(lèi)型轉(zhuǎn)換上出了問(wèn)題??墒?,我得到了一個(gè)數(shù)組,轉(zhuǎn)為 int[]
好像沒(méi)問(wèn)題吧?
實(shí)際上,這么轉(zhuǎn)換確實(shí)錯(cuò)了。這是 C# 里和 .NET 環(huán)境交互的時(shí)候的一點(diǎn)小小的奇怪約定:C# 的數(shù)組必須是從 0 為下標(biāo)開(kāi)始的,如果通過(guò)反射來(lái)創(chuàng)建這種特殊數(shù)組的話(huà),C# 里是無(wú)法使用任何一種數(shù)據(jù)類(lèi)型表達(dá)出這個(gè)類(lèi)型的具體信息的,這樣的非 0 下標(biāo)數(shù)組在錯(cuò)誤信息里經(jīng)常被顯示為 int[*]
而非 int[]
。而我們寫(xiě)類(lèi)型的時(shí)候是無(wú)法照著這個(gè)寫(xiě)法來(lái)寫(xiě)類(lèi)似 int[*] arr = ...
的東西的,這語(yǔ)法上就不支持。
因此,這樣的類(lèi)型僅能使用 Array
來(lái)表達(dá)。那么,一旦去掉類(lèi)型轉(zhuǎn)換改回去的話(huà),下面的 a[1]
賦值就會(huì)統(tǒng)統(tǒng)出問(wèn)題:

所有的索引器上全部報(bào)錯(cuò)。因?yàn)?Array
類(lèi)型自身是沒(méi)有索引器這樣的運(yùn)算符的。畢竟你想想,所有數(shù)組都從 Array
Array
自己是沒(méi)有這么方便的取值的方式的。
那么怎么存取值呢?Array
類(lèi)型里有 GetValue
和 SetValue
方法可以做到。我們此時(shí)把 try
塊里的代碼改成這樣:
把原始的索引器用 SetValue
和 GetValue
方法來(lái)表示。注意這倆是實(shí)例方法,而不是 ,要注意使用。Array.SetValue
和 Array.GetValue
注意,第一個(gè)參數(shù)傳入的是賦值的數(shù)值,它本來(lái)接收的數(shù)據(jù)類(lèi)型是
object
類(lèi)型。因?yàn)樗袛?shù)據(jù)類(lèi)型都隱式從object
類(lèi)型派生(指針:那我走,我:是的你走吧我不攔你),不過(guò)要裝箱,影響一丟丟性能;然后GetValue
方法會(huì)返回object
類(lèi)型,所以記得強(qiáng)制轉(zhuǎn)換(當(dāng)然你不強(qiáng)制轉(zhuǎn)換也行……看你自己怎么用)。
此時(shí)你可以看到正常結(jié)果:
