泛型的 T 並不能由變數的型別帶入,所以在編寫程式碼的時候會需要指定強型別,要做到動態泛型也會有一點難度
最近有一個需求是需要把傳入的物件陣列(int, float, string...),並根據他們的型別轉成 List<T>
程式碼看起來會像是這樣
var cls = new ClsTest();
ArrayList array = new ArrayList();
array.Add(123);
array.Add("name");
foreach (var arrObj in array)
{
var objType = arrObj.GetType();
var result = cls.MakeList<objType>(); //error
}
class ClsTest
{
//case 1
public IEnumerable<T> MakeList<T>()
{
return new List<T>();
}
//case 2
public static string StaticDisplay()
{
return "OK";
}
//case 3
public IEnumerable<T> Read<T>(bool isAddDefault = true)
{
var result = new List<T>();
if (isAddDefault)
result.Add(default(T));
return result;
}
public IEnumerable<T> Read<T>(string name, bool isAddDefault = true)
{
return Read<T>(isAddDefault);
}
}
然而在cls.MakeList<objType>()中卻不允許我們丟入變數當成 type,即便這個變數是個 type
這個時候我們就需要做到「動.態.泛.型」的事情了
為了達到這個目地,可以使用 反射 與 MakeGenericMethod 的方法達到
GetInvoke 第一個參數需填入 instance 的參數, 第二個參數則是填入輸入參數的值,因為這個方法不需傳入參數,所以這裡放 null 即可
var cls = new ClsTest();
MethodInfo method = cls.GetType().GetMethod("MakeList");
foreach (var arrObj in array)
{
var objType = arrObj.GetType();
MethodInfo generic = method.MakeGenericMethod(objType);
var resultPara = generic.Invoke(cls, null); //需有實體物件 cls,沒有參數放 null
Console.WriteLine(resultPara.GetType());
}
另外如果呼叫的是 static method 的話,做法又有一點不同
GetInvoke 第一個參數就不用填入 instance 的參數,因為 static method 是不需要實體化的
MethodInfo method = typeof(ClsTest).GetMethod("StaticDisplay");
var resultPara = method.Invoke(null, null); //不需實體化 與參數,都放null
Console.WriteLine(resultPara.GetType());
但如果今天你的 method 需要有多載的功能,並不是那麼單純根據 method name 就可以抓出來的話,就像 ClsTest.Read 這個方法
提供了 Generic 的方法,而且又有多種參數的入口這時候就需要用 GetMethods 的方法來進行過瀘, 以下僅展示傳回 List<int>,並預設帶回一組 default<T> 的資料
var cls = new ClsTest();
var methods = cls.GetType().GetMethods().Where(m => m.Name == "Read"
&& m.IsGenericMethod
&& m.GetParameters().Count() == 1
)
.ToList();
MethodInfo method = methods.First();
MethodInfo generic = method.MakeGenericMethod(typeof(int));
var resultPara = generic.Invoke(cls, new object[] { true }); //即便方法裡的參數是 default parameter,並不代表我們可以不丟參數進去
Console.WriteLine(resultPara.GetType());
參考文件
https://docs.microsoft.com/zh-tw/dotnet/api/system.type.getmethod?view=netframework-4.8
https://dotblogs.com.tw/regionbbs/2011/02/09/invoke_generic_methods