情景說明
在一個通用的資料轉換功能中,希望能依照傳入類別的屬性型別動態建立對應的 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>);
}