新聞中心
這個問題是我在測試上一篇隨筆《C# 使用 Binder 類自定義反射》中的類時發(fā)現(xiàn)的,當(dāng)時為了能夠讓 PowerBinder 支持泛型方法綁定,完成了一些簡單的類型推斷工作,但是它只能支持直接使用泛型參數(shù) T 作為參數(shù)類型,對于 T[],IList

創(chuàng)新互聯(lián)建站專注于三門企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站開發(fā)。三門網(wǎng)站建設(shè)公司,為三門等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站制作,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務(wù)
或者舉個復(fù)雜點的例子,對于下面的泛型方法定義:
- void Method
(IList a, params T[] args);
再給出參數(shù)類型為:
- { typeof(IList
), typeof(int[]) } - { typeof(IList
), typeof(int[]) } - { typeof(IList
), typeof(int[][]) }
我希望能夠正確的推斷出 T 的類型分別為 int、int[] 和 int[]。
后來參考了《CSharp Language Specification》v5.0 中 7.5.2 類型推斷 一節(jié),規(guī)范中給出了 C# 中進(jìn)行類型推斷的兩階段算法,算法分為兩階段主要是為了支持實參表達(dá)式和匿名函數(shù)的推斷,而我的需求則要簡單很多,只要支持普通的參數(shù)就可以了。又參考了 7.5.2.13 方法組轉(zhuǎn)換的類型推斷 一節(jié),最終得到了下面的簡化算法。
首先對幾個名詞進(jìn)行區(qū)分:類型形參、類型實參、方法形參和方法實參。
對于泛型方法定義 void Method
對于相應(yīng)的封閉泛型方法的調(diào)用 Method
泛型方法的類型推斷,從形式上來定義,就是對給定泛型方法 Tr M
該算法首先認(rèn)為所有 Xi 均未固定(即沒有預(yù)設(shè)值),并從 D 的每個實參類型 Ui 到 M 的對應(yīng)形參類型 Ti 進(jìn)行下限推斷(前提是 Ti 包含類型形參,即 ContainsGenericParameters == true),但是如果 xi 為 ref 或 out 形參,則從 Ui 到 Ti 進(jìn)行精確推斷。如果沒有為任何 Xi 找到界限,則類型推斷將失敗。否則,所有將 Xi 均固定到對應(yīng)的 Si,它們是類型推斷的結(jié)果。下面給出詳細(xì)的推斷算法,這里的算法經(jīng)過了我的修改,與原規(guī)范并不完全相同。
1. 下限推斷
這里的下限推斷指的是對于給定的實參類型 U,找到合適的形參類型 V,使得 V.IsImplicitFrom(U)。
按如下所述從類型 U 到類型 V 進(jìn)行下限推斷:
- 如果 V 是 Xi 之一,則將 U 添加到 Xi 的下限界限集中。
- 否則,如果 V 為 V1? 類型,而 U 為 U1? 類型,則從 U1 到 V1 進(jìn)行下限推斷。
- 否則,如果 V 是數(shù)組類型 V1[…],U 是具有相同秩的數(shù)組類型 U1[…],或者 V 是一個 IEnumerable
、ICollection 或 IList ,U 是一維數(shù)組類型 U1[],則從 U1 到 V1 進(jìn)行下限推斷。 - 否則,如果 V 是構(gòu)造類、結(jié)構(gòu)、接口或委托類型 C
,并且存在唯一類型 C ,使 U 等于、(直接或間接)繼承自或者(直接或間接)實現(xiàn) C (“唯一性”限制表示對于 interface C {} class U: C , C {},不進(jìn)行從 U 到 C 的推斷,因為 U1 可以是 X 或 Y。),則從每個 Ui 到對應(yīng)的 Vi 進(jìn)行推斷,推斷依賴于 C 的第 i 個類型參數(shù): - 如果該參數(shù)是協(xié)變的,則進(jìn)行下限推斷。
- 如果該參數(shù)是逆變的,則進(jìn)行上限推斷。
- 如果該參數(shù)是固定的,則進(jìn)行精確推斷。
- 否則,不進(jìn)行任何推斷。
2. 精確推斷
這里的精確推斷指的是對于給定的實參類型 U,找到合適的形參類型 V,使得 U == V。
按如下所述從類型 U 到類型 V 進(jìn)行精確推斷:
- 如果 V 是 Xi 之一,則將 U 添加到 Xi 的精確界限集中。
- 否則,通過檢查是否存在以下任何一種情況來確定集合 V1…Vk 和 U1…Uk :
- V 是數(shù)組類型 V1[…] , U 是具有相同秩的數(shù)組類型 U1[…]。
- V 是類型 V1?,U 是類型 U1?。
- V 是構(gòu)造類型 C
并且 U 是構(gòu)造類型 C 。
如果存在以上任意情況,則從每個 Ui 到對應(yīng)的 Vi 進(jìn)行精確推斷。
- 否則,不進(jìn)行任何推斷。
3. 上限推斷
這里的上限推斷指的是對于給定的實參類型 U,找到合適的形參類型 V,使得 U.IsImplicitFrom(V)。
按如下所述從類型 U 到類型 V 進(jìn)行上限推斷:
- 如果 V 是 Xi 之一,則將 U 添加到 Xi 的上限界限集中。
- 否則,如果 V 為 V1? 類型,而 U 為 U1? 類型,則從 U1 到 V1 進(jìn)行上限推斷。
- 否則,如果 V 是數(shù)組類型 V1[…],U 是具有相同秩的數(shù)組類型 U1[…],或者 V 是一維數(shù)組類型 V1[],U 是一個 IEnumerable
、ICollection 或 IList ,則從 U1 到 V1 進(jìn)行上限推斷。 - 否則,如果 U 是構(gòu)造類、結(jié)構(gòu)、接口或委托類型 C
,V 是等于、(直接或間接)繼承自或者(直接或間接)實現(xiàn)唯一類型 C 的類、結(jié)構(gòu)、接口或委托類型(“唯一性”限制表示如果我們有 interface C {} class V : C >, C >{},則不進(jìn)行從 C 到 V 的推斷。也不進(jìn)行從 U1 到 X
或 Y
的推斷。),則從每個 Ui 到對應(yīng)的 Vi 進(jìn)行推斷,推斷依賴于 C 的第 i 個類型參數(shù):
- 如果該參數(shù)是協(xié)變的,則進(jìn)行上限推斷。
- 如果該參數(shù)是逆變的,則進(jìn)行下限推斷。
- 如果該參數(shù)是固定的,則進(jìn)行精確推斷。
- 否則,不進(jìn)行任何推斷。
4. 固定
固定是為了根據(jù)之前的算法得到的界限集,推斷出類型參數(shù)的合適的值。
具有界限集的類型變量 Xi 按如下方式固定:
- 候選類型集 Ui 是在 Xi 的界限集中的所有類型的集合。
- 然后我們依次檢查 Xi 的每個界限:對于 Xi 的每個精確界限 U,將與 U 不同的所有類型 Ui 都從候選集中移除(要求 U == Ui)。對于 Xi 的每個下限 U,將不存在從 U 進(jìn)行的隱式轉(zhuǎn)換的所有類型 Ui 都從候選集中移除(要求 Ui.IsImplicitFrom(U))。對于 Xi 的每個上限 U,將不存在從其到 U 進(jìn)行的隱式轉(zhuǎn)換的所有類型 Ui 都從候選集中移除(要求 U.IsImplicitFrom(Ui))。
- 如果在剩下的候選類型 Ui 中,存在唯一類型 V,該類型可由其他所有候選類型經(jīng)隱式轉(zhuǎn)換而來,則將 Xi 固定到 V(也就是說,要求 V 是其中最通用的類型)。
- 否則,類型推斷將失敗。
以上就是泛型方法的類型推斷算法,其中只考慮了方法實參和方法形參一一對應(yīng)的情況,如果需要處理 params T[] 參數(shù),則需要對最后一個參數(shù)進(jìn)行特殊處理,并分別使用 T 和 T[] 進(jìn)行一次類型推斷。做兩次類型推斷,就是為了判斷是否是方法的展開形式的調(diào)用。
或者說,對于泛型方法定義
- void Method
(T a, params T[] args);
如果參數(shù)為 { typeof(int), typeof(int[]) } 和 { typeof(nt[]), typeof(int[]) },雖然 T[] 對應(yīng)的實參是相同的,但推斷出的 T 卻是不同的,這就需要利用兩次類型推斷來處理。
這個算法的實現(xiàn)加上注釋大概有 500 多行,這里就不再貼出,基本就是按照上面的 4 步來的,只是在一些細(xì)節(jié)上采用了更高效的做法。所有源碼可以見這里。
網(wǎng)站欄目:C#泛型方法的類型推斷
新聞來源:http://www.dlmjj.cn/article/djppoed.html


咨詢
建站咨詢
