最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

第 57 講:反射(一):反射的概念和基本使用

2021-09-13 08:19 作者:SunnieShine  | 我要投稿

考慮一種情況。如果我們需要檢查你的這個(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 NameFullName 屬性

要想獲取這個(gè)類(lèi)型本身的名稱(chēng)的字符串寫(xiě)法的話(huà),我們可以使用這個(gè)操作來(lái)得到:

這樣我們得到的輸出結(jié)果是 "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)型吧

這里 XXXInfoXXX 不是實(shí)際類(lèi)型名稱(chēng)的一部分,而是一個(gè)替換。實(shí)際上,就是在說(shuō)前面 ConstructorInfoFieldInfo 這些東西。

因?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 類(lèi)型

我們?nèi)绻麖哪硞€(gè)地方獲取到了一個(gè)字段信息的話(huà),它一般是用 FieldInfo 表示的。以這個(gè)類(lèi)型表示出來(lái)的話(huà),里面有很多成員可提供我們使用。

屬性

  • IsPublicIsPrivate:檢測(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、agegender,那么調(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)型。

屬性

  • CanReadCanWrite:表示這個(gè)屬性是否可讀或者可寫(xiě);

  • PropertyType:表示這個(gè)屬性的類(lèi)型,用 Type 類(lèi)型的實(shí)例表示返回值結(jié)果;

  • GetMethodSetMethod:表示這個(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é)果;

  • IsPublicIsPrivate:看這個(gè)方法的修飾符是不是 public 或者 private;

  • IsHideBySig:如果這個(gè)方法標(biāo)記了 new 修飾符表示直接阻斷繼承鏈的話(huà),這個(gè)屬性獲取的就是這個(gè)效果:看是不是這個(gè)方法被派生類(lèi)型隱藏掉了;

  • IsStatic、IsVirtual、IsAbstractIsFinal:獲取這個(gè)方法是不是用 static、virtualabstract 或者 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é)果。

屬性

  • AddMethodRemoveMethod:獲取事件的 addremove 訪問(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 類(lèi)型派生,而所有數(shù)組就包含了鋸齒數(shù)組和正常的方陣數(shù)組。正常的數(shù)組,中括號(hào)里是用逗號(hào)隔開(kāi)傳參獲取數(shù)值的,而鋸齒數(shù)組則需要中括號(hào)外部加中括號(hào)來(lái)取值。所以,根本無(wú)法統(tǒng)一一個(gè)索引器規(guī)則來(lái)取值,因此 Array 自己是沒(méi)有這么方便的取值的方式的。

那么怎么存取值呢?Array 類(lèi)型里有 GetValueSetValue 方法可以做到。我們此時(shí)把 try 塊里的代碼改成這樣:

把原始的索引器用 SetValueGetValue 方法來(lái)表示。注意這倆是實(shí)例方法,而不是 Array.SetValueArray.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é)果:


第 57 講:反射(一):反射的概念和基本使用的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
鄂州市| 中卫市| 平度市| 孟村| 石屏县| 金门县| 岢岚县| 车险| 天峻县| 广饶县| 南漳县| 卢氏县| 阳泉市| 泰兴市| 东城区| 元江| 昂仁县| 安丘市| 兴安县| 吉林市| 富川| 东丽区| 织金县| 龙胜| 安岳县| 工布江达县| 无棣县| 同德县| 金昌市| 太白县| 厦门市| 广宗县| 扎赉特旗| 平顶山市| 墨脱县| 盖州市| 汪清县| 江都市| 丰顺县| 孙吴县| 九龙县|