執行階段決定泛形參數的型別

Feb 20, 2019




情景說明

在一個通用的資料轉換功能中,希望能依照傳入類別的屬性型別動態建立對應的 List<> 物件,因此需要在執行階段動態建立 List<> 物件,且清單的泛型會在執行階段決定,類似下面程式。

然而,這段測試程式別說可以通過了,根本是連編譯都無法通過,因為 objItemType 會被視為參數而不是型別 ( 雖然 objItemType 是 Type 型別 )。

 [TestMethod]
 public void TestMethod1()
 {
    //Arrange
    var objItemType = typeof(string);
    //Act
    var objList = new List<objItemType>();
    //Assert
    Assert.IsTrue(objList is MyContainer<string>);
 }

反射無法生成開放型泛型類別

如果直接呼叫建構元不可行,那就出大招,透過 反射(Reflection) 來建立實體,如下列程式所示,則會在執行階段擲出錯誤,因為 List<> 是一個「開放式泛型類型」

其實很明顯可以看出這段程式必然會有問題,因為在 Arrange 階段準備的型別並沒有機會在 Act 階段參與 List<> 物件生成,沒有定義當然就生不出來。

另外,若是查看 Activator.CreateInstance 函式的多形,並沒有提供給定泛形型別的多形可供呼叫。

 [TestMethod]
 public void TestMethod2()
 {
    //Arrange
    var objItemType = typeof(string);
    //Act
    var objType = typeof(List<>);
    var objInstance = Activator.CreateInstance(objType);
    //Assert
    Assert.IsTrue(objInstance is MyContainer<string>);
 }

訂做一個型別

前面提到反射的生成無法處理「開放式泛型類型」,所以要在執行階段決定類別的泛型參數的型別,需要先透過 MakeGenericType 方法,組合出封閉式泛型類型,再利用反射生成即可,例如下面的操作,就可以順利通過驗證。

 [TestMethod]
 public void TestMethod3()
 {
    //Arrange
    var objItemType = typeof(string);
    //Act
    var objType = typeof(List<>);
    var objGenericType = objType.MakeGenericType(objItemType);
    var objInstance = Activator.CreateInstance(objGenericType);
    //Assert
    Assert.IsTrue(objInstance is List<string>);
 }