C# 泛型方法的類型推斷

來源:互聯網
上載者:User

這裡所謂的“泛型方法的類型推斷”,指的是根據已有的方法實參的類型,推斷出泛型方法的類型實參。例如一個泛型方法 void Method<T>(T[] args),如果我給出方法實參類型是 int[],那麼希望能夠推斷出 T = int。

這個問題是我在測試上一篇隨筆《C# 使用 Binder 類自訂反射》中的類時發現的,當時為了能夠讓 PowerBinder 支援泛型方法綁定,完成了一些簡單的類型推斷工作,但是它只能支援直接使用泛型參數 T 作為參數類型,對於 T[],IList<T> 這種複雜一些的情況是不能處理的。

或者舉個複雜點的例子,對於下面的泛型方法定義:

void Method<T>(IList<T> a, params T[] args);

再給出參數類型為:

{ typeof(IList<int>), typeof(int[]) }{ typeof(IList<int[]>), typeof(int[]) }{ typeof(IList<int[]>), typeof(int[][]) }

我希望能夠正確的推斷出 T 的類型分別為 int、int[] 和 int[]。

後來參考了《CSharp Language Specification》v5.0 中 7.5.2 類型推斷 一節,規範中給出了 C# 中進行類型推斷的兩階段演算法,演算法分為兩階段主要是為了支援實參運算式和匿名函數的推斷,而我的需求則要簡單很多,只要支援普通的參數就可以了。又參考了 7.5.2.13 方法群組轉換的類型推斷 一節,最終得到了下面的簡化演算法。

首先對幾個名詞進行區分:類型形參、類型實參、方法形參和方法實參。

對於泛型方法定義 void Method<T>(T a),其中的 T 是類型形參,T a 是方法形參。對於相應的封閉泛型方法的調用 Method<int>(10),其中的 int 就是類型實參,10 就是方法實參。

泛型方法的類型推斷,從形式上來定義,就是對給定泛型方法 Tr M<X1, …, Xn>(T1 x1, …, Tm xm),其中 Tr 是傳回值,X1, …, Xn 是類型形參,T1, …, Tm 是方法形參,和一個委託類型 D(U1 x1, …, Um xm),找到一群組類型實參 S1, …, Sn,使運算式 M<S1, …, Sn> 與 D 相容(D 可由 M<S1, …, Sn> 隱式轉換而來)。

該演算法首先認為所有 Xi 均未固定(即沒有預設值),並從 D 的每個實參類型 Ui 到 M 的對應形參類型 Ti 進行下限推斷(前提是 Ti 包含類型形參,即 ContainsGenericParameters == true),但是如果 xi 為 ref 或 out 形參,則從 Ui 到 Ti 進行精確推斷。如果沒有為任何 Xi 找到界限,則類型推斷將失敗。否則,所有將 Xi 均固定到對應的 Si,它們是類型推斷的結果。下面給出詳細的推斷演算法,這裡的演算法經過了我的修改,與原規範並不完全相同。

1. 下限推斷

這裡的下限推斷指的是對於給定的實參類型 U,找到合適的形參類型 V,使得 V.IsImplicitFrom(U)。

按如下所述從類型 U 到類型 V 進行下限推斷:

  1. 如果 V 是 Xi 之一,則將 U 添加到 Xi 的下限界限集中。
  2. 否則,如果 V 為 V1?  類型,而 U 為 U1? 類型,則從 U1 到 V1 進行下限推斷。
  3. 否則,如果 V 是數群組類型 V1[…],U 是具有相同秩的數群組類型 U1[…],或者 V 是一個 IEnumerable<V1>、ICollection<V1> 或 IList<V1>,U 是一維數群組類型 U1[],則從 U1 到 V1 進行下限推斷。
  4. 否則,如果 V 是構造類、結構、介面或委託類型 C<V1…Vk>,並且存在唯一類型 C<U1…Uk> ,使 U 等於、(直接或間接)繼承自或者(直接或間接)實現 C<U1…Uk>(“唯一性”限制表示對於 interface C<T>{} class U: C<X>, C<Y>{},不進行從 U 到 C<T>  的推斷,因為 U1 可以是 X 或 Y。),則從每個 Ui 到對應的 Vi 進行推斷,推斷依賴於 C 的第 i 個型別參數:
    • 如果該參數是協變的,則進行下限推斷。
    • 如果該參數是逆變的,則進行上限推斷。
    • 如果該參數是固定的,則進行精確推斷。
  5. 否則,不進行任何推斷。
2. 精確推斷

這裡的精確推斷指的是對於給定的實參類型 U,找到合適的形參類型 V,使得 U == V。

按如下所述從類型 U 到類型 V 進行精確推斷:

  1. 如果 V 是 Xi 之一,則將 U 添加到 Xi 的精確界限集中。
  2. 否則,通過檢查是否存在以下任何一種情況來確定集合 V1…Vk  和 U1…Uk :
    • V 是數群組類型 V1[…] , U 是具有相同秩的數群組類型 U1[…]。
    • V 是類型 V1?,U 是類型 U1?。
    • V 是構造類型 C<V1…Vk> 並且 U 是構造類型 C<U1…Uk> 。

    如果存在以上任意情況,則從每個 Ui 到對應的 Vi 進行精確推斷。

  3. 否則,不進行任何推斷。
3. 上限推斷

這裡的上限推斷指的是對於給定的實參類型 U,找到合適的形參類型 V,使得 U.IsImplicitFrom(V)。

按如下所述從類型 U 到類型 V 進行上限推斷:

  1. 如果 V 是 Xi 之一,則將 U 添加到 Xi 的上限界限集中。
  2. 否則,如果 V 為 V1?  類型,而 U 為 U1? 類型,則從 U1 到 V1 進行上限推斷。
  3. 否則,如果 V 是數群組類型 V1[…],U 是具有相同秩的數群組類型 U1[…],或者 V 是一維數群組類型 V1[],U 是一個 IEnumerable<U1>、ICollection<U1> 或 IList<U1>,則從 U1 到 V1 進行上限推斷。
  4. 否則,如果 U 是構造類、結構、介面或委託類型 C<U1…Uk>,V 是等於、(直接或間接)繼承自或者(直接或間接)實現唯一類型 C<V1…Vk> 的類、結構、介面或委託類型(“唯一性”限制表示如果我們有 interface C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{},則不進行從 C<U1> 到 V<Q> 的推斷。也不進行從 U1 到 X<Q> 或 Y<Q> 的推斷。),則從每個 Ui 到對應的 Vi 進行推斷,推斷依賴於 C 的第 i 個型別參數:
    • 如果該參數是協變的,則進行上限推斷。
    • 如果該參數是逆變的,則進行下限推斷。
    • 如果該參數是固定的,則進行精確推斷。
  5. 否則,不進行任何推斷。
4. 固定

固定是為了根據之前的演算法得到的界限集,推斷出型別參數的合適的值。

具有界限集的類型變數 Xi 按如下方式固定:

  1. 候選類型集 Ui 是在 Xi 的界限集中的所有類型的集合。
  2. 然後我們依次檢查 Xi 的每個界限:對於 Xi 的每個精確界限 U,將與 U 不同的所有類型 Ui 都從候選集中移除(要求 U == Ui)。對於 Xi 的每個下限 U,將不存在從 U 進行的隱式轉換的所有類型 Ui 都從候選集中移除(要求 Ui.IsImplicitFrom(U))。對於 Xi 的每個上限 U,將不存在從其到 U 進行的隱式轉換的所有類型 Ui 都從候選集中移除(要求 U.IsImplicitFrom(Ui))。
  3. 如果在剩下的候選類型 Ui 中,存在唯一類型 V,該類型可由其他所有候選類型經隱式轉換而來,則將 Xi 固定到 V(也就是說,要求 V 是其中最通用的類型)。
  4. 否則,類型推斷將失敗。

以上就是泛型方法的類型推斷演算法,其中只考慮了方法實參和方法形參一一對應的情況,如果需要處理 params T[] 參數,則需要對最後一個參數進行特殊處理,並分別使用 T 和 T[] 進行一次類型推斷。做兩次類型推斷,就是為了判斷是否是方法的展開形式的調用。

或者說,對於泛型方法定義

void Method<T>(T a, params T[] args);

如果參數為 { typeof(int), typeof(int[]) } 和 { typeof(nt[]), typeof(int[]) },雖然 T[] 對應的實參是相同的,但推斷出的 T 卻是不同的,這就需要利用兩次類型推斷來處理。

這個演算法的實現加上注釋大概有 500 多行,這裡就不再貼出,基本就是按照上面的 4 步來的,只是在一些細節上採用了更高效的做法。所有源碼可以見這裡。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.