古詩詞大全網 - 個性簽名 - 如何:用反射發出定義泛型方法

如何:用反射發出定義泛型方法

第二個過程演示如何發出方法體,以及如何使用泛型方法的類型參數創建泛型類型的實例以及調用其方法。 第三個過程演示如何調用泛型方法。 重要事項 不能僅因為某個方法屬於泛型類型且使用泛型類型的類型參數,就稱該方法是泛型方法。 只有當方法具有自己的類型參數列表時,才能稱其為泛型方法。 泛型方法可以出現在非泛型類型上,在本例中就是如此。 有關泛型類型上的非泛型方法的示例,請參見 如何:用反射發出定義泛型類型。 定義泛型方法 首先,如果使用高級語言編寫,則查看泛型方法的顯示方式會十分有用。 下面的代碼包含在此主題的代碼示例中,用於調用泛型方法的代碼同樣也包含在其中。 該方法具有兩個類型參數 TInput 和TOutput,後者必須為引用類型 (class),必須具有無參數構造函數 (new),並且必須實現 ICollection(Of TInput)(在 C# 中為 ICollection)。 此接口約束確保 ICollectionTAdd 方法可用於將元素添加到該方法創建的 TOutput 集合中。 該方法具有壹個形參 input,這是壹個 TInput 的數組。 該方法創建類型 TOutput 的集合,並將 input 的元素復制到該集合中。 Public Shared Function Factory(Of TInput, _ TOutput As {ICollection(Of TInput), Class, New}) _ (ByVal input() As TInput) As TOutput Dim retval As New TOutput() Dim ic As ICollection(Of TInput) = retval For Each t As TInput In input ic.Add(t) Next Return retval End Function public static TOutput Factory(TInput[] tarray) where TOutput : class, ICollection, new() { TOutput ret = new TOutput(); ICollection ic = ret; foreach (TInput t in tarray) { ic.Add(t); } return ret; } 定義壹個動態程序集和壹個動態模塊,以包含該泛型方法所屬的類型。 在本例中,該程序集僅擁有壹個模塊(名為 DemoMethodBuilder1),模塊名稱即為程序集名稱加上壹個擴展名。 在此示例中,將程序集保存到磁盤中並執行,因此指定了 AssemblyBuilderAccessRunAndSave。 您可以使用 Ildasm.exe(MSIL 反匯編程序) 檢查DemoMethodBuilder1.dll,並將其與步驟 1 中顯示的方法的 Microsoft 中間語言 (MSIL) 進行比較。 Dim asmName As New AssemblyName("DemoMethodBuilder1") Dim domain As AppDomain = AppDomain.CurrentDomain Dim demoAssembly As AssemblyBuilder = _ domain.DefineDynamicAssembly(asmName, _ AssemblyBuilderAccess.RunAndSave) ' Define the module that contains the code. For an ' assembly with one module, the module name is the ' assembly name plus a file extension. Dim demoModule As ModuleBuilder = _ demoAssembly.DefineDynamicModule( _ asmName.Name, _ asmName.Name & ".dll") AssemblyName asmName = new AssemblyName("DemoMethodBuilder1"); AppDomain domain = AppDomain.CurrentDomain; AssemblyBuilder demoAssembly = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave); // Define the module that contains the code. For an // assembly with one module, the module name is the // assembly name plus a file extension. ModuleBuilder demoModule = demoAssembly.DefineDynamicModule(asmName.Name, asmName.Name+".dll"); 定義該泛型方法所屬的類型。 該類型不壹定為泛型類型。 泛型方法可以屬於泛型類型,也可以屬於非泛型類型。 在此示例中,該類型為壹個類,它不是泛型類型,名稱為 DemoType。 Dim demoType As TypeBuilder = demoModule.DefineType( _ "DemoType", _ TypeAttributes.Public) TypeBuilder demoType = demoModule.DefineType("DemoType", TypeAttributes.Public); 定義泛型方法。 如果泛型方法的形參的類型由該泛型方法的泛型類型參數指定,請使用 DefineMethod(String, MethodAttributes) 方法重載定義該方法。 該方法的泛型類型參數尚未定義,因此不能在對 DefineMethod 的調用中指定該方法的形參的類型。 在此示例中,該方法名為 Factory。 該方法是公***方法,並且是 static(在 Visual Basic 中為 Shared)的。 Dim factory As MethodBuilder = _ demoType.DefineMethod("Factory", _ MethodAttributes.Public Or MethodAttributes.Static) MethodBuilder factory = demoType.DefineMethod("Factory", MethodAttributes.Public | MethodAttributes.Static); 通過將包含參數名稱的字符串數組傳遞給 MethodBuilderDefineGenericParameters 方法來定義 DemoMethod 的泛型類型參數。 這使得該方法成為泛型方法。 下面的代碼使得 Factory 成為具有類型參數 TInput 和TOutput 的泛型方法。 若要使代碼更易於閱讀,可創建具有這些名稱的變量,以保存表示這兩個類型參數的 GenericTypeParameterBuilder 對象。 Dim typeParameterNames() As String = {"TInput", "TOutput"} Dim typeParameters() As GenericTypeParameterBuilder = _ factory.DefineGenericParameters(typeParameterNames) Dim TInput As GenericTypeParameterBuilder = typeParameters(0) Dim TOutput As GenericTypeParameterBuilder = typeParameters(1) string[] typeParameterNames = {"TInput", "TOutput"}; GenericTypeParameterBuilder[] typeParameters = factory.DefineGenericParameters(typeParameterNames); GenericTypeParameterBuilder TInput = typeParameters[0]; GenericTypeParameterBuilder TOutput = typeParameters[1]; 可以選擇為類型參數添加特殊約束。 特殊約束是通過使用 SetGenericParameterAttributes 方法添加的。 在此示例中,TOutput 被約束為引用類型,並且具有無參數構造函數。 TOutput.SetGenericParameterAttributes( _ GenericParameterAttributes.ReferenceTypeConstraint Or _ GenericParameterAttributes.DefaultConstructorConstraint) TOutput.SetGenericParameterAttributes( GenericParameterAttributes.ReferenceTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint); 可以選擇為類型參數添加類約束和接口約束。 在此示例中,類型參數 TOutput 被約束為實現 ICollection(Of TInput)(在 C# 中為 ICollection)接口的類型。 這確保 Add 方法可用於添加元素。 Dim icoll As Type = GetType(ICollection(Of )) Dim icollOfTInput As Type = icoll.MakeGenericType(TInput) Dim constraints() As Type = { icollOfTInput } TOutput.SetInterfaceConstraints(constraints) Type icoll = typeof(ICollection); Type icollOfTInput = icoll.MakeGenericType(TInput); Type[] constraints = {icollOfTInput}; TOutput.SetInterfaceConstraints(constraints); 使用 SetParameters 方法定義該方法的形參。 在此示例中,Factory 方法具有壹個參數,即 TInput 的數組。 此類型是通過對表示 TInput 的 GenericTypeParameterBuilder 調用 MakeArrayType 方法創建的。 SetParameters 的參數是 Type 對象的數組。 Dim params() As Type = { TInput.MakeArrayType() } factory.SetParameters(params) Type[] parms = {TInput.MakeArrayType()}; factory.SetParameters(parms); 使用 SetReturnType 方法定義該方法的返回類型。 在此示例中,返回 TOutput 的壹個實例。 factory.SetReturnType(TOutput) factory.SetReturnType(TOutput); 使用 ILGenerator 發出該方法體。 有關詳細信息,請參見附帶的過程發出方法體。 重要事項 發出對泛型類型的方法的調用,而且這些類型的類型變量為該泛型方法的類型參數時,您必須使用 TypeBuilder 類的static GetConstructor(Type, ConstructorInfo)、 GetMethod(Type, MethodInfo) 和 GetField(Type, FieldInfo) 方法重載,以獲取這些方法的構造形式。 發出方法體的附帶過程對此進行了演示。 完成包含該方法的類型並保存程序集。 附帶的過程調用泛型方法演示了調用完整方法的兩種方式。 ' Complete the type. Dim dt As Type = demoType.CreateType() ' Save the assembly, so it can be examined with Ildasm.exe. demoAssembly.Save(asmName.Name & ".dll") // Complete the type. Type dt = demoType.CreateType(); // Save the assembly, so it can be examined with Ildasm.exe. demoAssembly.Save(asmName.Name+".dll"); 發出方法體 獲取代碼生成器並聲明局部變量和標簽。 DeclareLocal 方法用於聲明局部變量。 Factory 方法具有四個局部變量:retVal 用於保存該方法返回的新 TOutput,ic 用於在 TOutput 轉換為 ICollection(Of TInput)(在 C# 中為 ICollection)時將其保存,input 用於保存 TInput 對象的輸入數組,而 index 用於循環訪問該數組。 該方法還具有使用 DefineLabel 方法定義的兩個標簽,壹個用於進入循環 (enterLoop),另壹個用於循環的頂部 (loopAgain)。 該方法首先使用 Ldarg_0 操作碼加載其參數,然後使用 Stloc_S 操作碼將參數存儲在局部變量 input 中。 Dim ilgen As ILGenerator = factory.GetILGenerator() Dim retVal As LocalBuilder = ilgen.DeclareLocal(TOutput) Dim ic As LocalBuilder = ilgen.DeclareLocal(icollOfTInput) Dim input As LocalBuilder = _ ilgen.DeclareLocal(TInput.MakeArrayType()) Dim index As LocalBuilder = _ ilgen.DeclareLocal(GetType(Integer)) Dim enterLoop As Label = ilgen.DefineLabel() Dim loopAgain As Label = ilgen.DefineLabel() ilgen.Emit(OpCodes.Ldarg_0) ilgen.Emit(OpCodes.Stloc_S, input) ILGenerator ilgen = factory.GetILGenerator(); LocalBuilder retVal = ilgen.DeclareLocal(TOutput); LocalBuilder ic = ilgen.DeclareLocal(icollOfTInput); LocalBuilder input = ilgen.DeclareLocal(TInput.MakeArrayType()); LocalBuilder index = ilgen.DeclareLocal(typeof(int)); Label enterLoop = ilgen.DefineLabel(); Label loopAgain = ilgen.DefineLabel(); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Stloc_S, input); 使用 ActivatorCreateInstance 方法的泛型方法重載發出代碼以創建 TOutput 的實例。 使用此重載需要指定類型具有無參數構造函數,這也是向 TOutput 添加該約束的原因。 通過將 TOutput 傳遞到 MakeGenericMethod 創建構造的泛型方法。 發出代碼以調用該方法後,發出代碼以使用 Stloc_S 將其存儲在局部變量 retVal 中 Dim createInst As MethodInfo = _ GetType(Activator).GetMethod("CreateInstance", Type.EmptyTypes) Dim createInstOfTOutput As MethodInfo = _ createInst.MakeGenericMethod(TOutput) ilgen.Emit(OpCodes.Call, createInstOfTOutput) ilgen.Emit(OpCodes.Stloc_S, retVal) MethodInfo createInst = typeof(Activator).GetMethod("CreateInstance", Type.EmptyTypes); MethodInfo createInstOfTOutput = createInst.MakeGenericMethod(TOutput); ilgen.Emit(OpCodes.Call, createInstOfTOutput); ilgen.Emit(OpCodes.Stloc_S, retVal); 發出代碼以將新的 TOutput 對象強制轉換為 ICollection(Of TInput),並將其存儲在局部變量 ic 中。 ilgen.Emit(OpCodes.Ldloc_S, retVal) ilgen.Emit(OpCodes.Box, TOutput) ilgen.Emit(OpCodes.Castclass, icollOfTInput) ilgen.Emit(OpCodes.Stloc_S, ic) ilgen.Emit(OpCodes.Ldloc_S, retVal); ilgen.Emit(OpCodes.Box, TOutput); ilgen.Emit(OpCodes.Castclass, icollOfTInput); ilgen.Emit(OpCodes.Stloc_S, ic); 獲取表示 ICollectionTAdd 方法的 MethodInfo。 此方法對 ICollection(Of TInput)(在 C# 中為 ICollection)進行操作,因此有必要獲取特定於該構造類型的 Add 方法。 不能使用 GetMethod 方法直接從 icollOfTInput 獲取此 MethodInfo,因為 GetMethod 在已使用 GenericTypeParameterBuilder 構造的類型上不受支持。 而應該對包含 ICollectionT 泛型接口的泛型類型定義的 icoll 調用 GetMethod。 然後使用 GetMethod(Type, MethodInfo)static 方法生成構造類型的 MethodInfo。 下面的代碼對此進行了演示。 Dim mAddPrep As MethodInfo = icoll.GetMethod("Add") Dim mAdd As MethodInfo = _ TypeBuilder.GetMethod(icollOfTInput, mAddPrep) MethodInfo mAddPrep = icoll.GetMethod("Add"); MethodInfo mAdd = TypeBuilder.GetMethod(icollOfTInput, mAddPrep); 發出代碼以初始化 index 變量(通過加載 32 位整數 0 並將其存儲在變量中)。 發出代碼以分支到標簽 enterLoop。 因為此標簽位於循環內,所以尚未進行標記。 該循環的代碼在下壹步中發出。 ' Initialize the count and enter the loop. ilgen.Emit(OpCodes.Ldc_I4_0) ilgen.Emit(OpCodes.Stloc_S, index) ilgen.Emit(OpCodes.Br_S, enterLoop) // Initialize the count and enter the loop. ilgen.Emit(OpCodes.Ldc_I4_0); ilgen.Emit(OpCodes.Stloc_S, index); ilgen.Emit(OpCodes.Br_S, enterLoop); 發出該循環的代碼。 第壹步是標記循環的頂部,方法是用 loopAgain 標簽調用 MarkLabel。 使用該標簽的分支語句現在將分支到代碼中的這個點。 下壹步是將強制轉換為 ICollection(Of TInput) 的TOutput 對象推入堆棧。 這不需要立即進行,但需要在調用 Add 方法之前完成。 接下來,輸入數組被推入堆棧,然後是包含該數組的當前索引的 index 變量。 Ldelem 操作碼從堆棧中彈出該索引和數組,然後將索引數組元素推入堆棧。 堆棧現在準備好調用 ICollectionTAdd 方法,該方法從堆棧中彈出集合和新的元素,並將該元素添加到該集合中。 循環中的其他代碼會使索引遞增,並通過測試查看循環是否完成:將索引和 32 位整數 1 推入堆棧並相加,在堆棧上保留總和;總和存儲在 index 中。 然後調用 MarkLabel 將該點設置為循環的入口點。 再次加載該索引。 將輸入數組推入堆棧,然後發出 Ldlen 以獲取其長度。 索引和長度現在位於堆棧中,並發出 Clt 對二者進行比較。 如果索引小於長度, Brtrue_S 會重新返回到循環的起始點。 ilgen.MarkLabel(loopAgain) ilgen.Emit(OpCodes.Ldloc_S, ic) ilgen.Emit(OpCodes.Ldloc_S, input) ilgen.Emit(OpCodes.Ldloc_S, index) ilgen.Emit(OpCodes.Ldelem, TInput) ilgen.Emit(OpCodes.Callvirt, mAdd) ilgen.Emit(OpCodes.Ldloc_S, index) ilgen.Emit(OpCodes.Ldc_I4_1) ilgen.Emit(OpCodes.Add) ilgen.Emit(OpCodes.Stloc_S, index) ilgen.MarkLabel(enterLoop) ilgen.Emit(OpCodes.Ldloc_S, index) ilgen.Emit(OpCodes.Ldloc_S, input) ilgen.Emit(OpCodes.Ldlen) ilgen.Emit(OpCodes.Conv_I4) ilgen.Emit(OpCodes.Clt) ilgen.Emit(OpCodes.Brtrue_S, loopAgain) ilgen.MarkLabel(loopAgain); ilgen.Emit(OpCodes.Ldloc_S, ic); ilgen.Emit(OpCodes.Ldloc_S, input); ilgen.Emit(OpCodes.Ldloc_S, index); ilgen.Emit(OpCodes.Ldelem, TInput); ilgen.Emit(OpCodes.Callvirt, mAdd); ilgen.Emit(OpCodes.Ldloc_S, index); ilgen.Emit(OpCodes.Ldc_I4_1); ilgen.Emit(OpCodes.Add); ilgen.Emit(OpCodes.Stloc_S, index); ilgen.MarkLabel(enterLoop); ilgen.Emit(OpCodes.Ldloc_S, index); ilgen.Emit(OpCodes.Ldloc_S, input); ilgen.Emit(OpCodes.Ldlen); ilgen.Emit(OpCodes.Conv_I4); ilgen.Emit(OpCodes.Clt); ilgen.Emit(OpCodes.Brtrue_S, loopAgain); 發出代碼以將 TOutput 對象推入堆棧,並從該方法返回。 局部變量 retVal 和ic 均包含對新的 TOutput 的引用;ic 僅用於訪問 ICollectionTAdd 方法。 ilgen.Emit(OpCodes.Ldloc_S, retVal) ilgen.Emit(OpCodes.Ret) ilgen.Emit(OpCodes.Ldloc_S, retVal); ilgen.Emit(OpCodes.Ret); 調用泛型方法Factory 為泛型方法定義。 若要調用泛型方法,您必須為泛型方法的泛型類型參數分配類型。 使用 MakeGenericMethod 方法可完成此操作。 下面的代碼通過為 TInput 指定 String 並為TOutput 指定List(Of String)(在 C# 中為 List)創建壹個構造的泛型方法,並顯示該方法的字符串表示形式。 Dim m As MethodInfo = dt.GetMethod("Factory") Dim bound As MethodInfo = m.MakeGenericMethod( _ GetType(String), GetType(List(Of String))) ' Display a string representing the bound method. Console.WriteLine(bound) MethodInfo m = dt.GetMethod("Factory"); MethodInfo bound = m.MakeGenericMethod(typeof(string), typeof(List)); // Display a string representing the bound method. Console.WriteLine(bound); 若要以後期綁定的形式調用該方法,請使用 Invoke 方法。 下面的代碼創建 Object(包含的唯壹元素為字符串數組)的數組,並將其作為泛型方法的參數列表傳遞。 Invoke 的第壹個參數為空引用,因為該方法為 static 的。 返回值被強制轉換為 List(Of String),並顯示它的第壹個元素。 Dim o As Object = bound.Invoke(Nothing, New Object() { arr }) Dim list2 As List(Of String) = CType(o, List(Of String)) Console.WriteLine("The first element is: {0}", list2(0)) object o = bound.Invoke(null, new object[]{arr}); List list2 = (List) o; Console.WriteLine("The first element is: {0}", list2[0]); 若要使用委托調用該方法,您必須具有與該構造的泛型方法的簽名匹配的委托。 實現上述目標的壹種簡便方式是創建壹個泛型委托。 下面的代碼使用 DelegateCreateDelegate(Type, MethodInfo) 方法重載,創建在代碼示例中定義的泛型委托 D 的壹個實例,然後調用該委托。 委托的性能比後期綁定調用好。 Dim dType As Type = GetType(D(Of String, List(Of String))) Dim test As D(Of String, List(Of String)) test = CType( _ [Delegate].CreateDelegate(dType, bound), _ D(Of String, List(Of String))) Dim list3 As List(Of String) = test(arr) Console.WriteLine("The first element is: {0}", list3(0)) Type dType = typeof(D); D test; test = (D) Delegate.CreateDelegate(dType, bound); List list3 = test(arr); Console.WriteLine("The first element is: {0}", list3[0]); 發出的方法也可以從引用保存的程序集的程序調用。 示例 下面的代碼示例使用泛型方法 Factory 創建壹個非泛型類型 DemoType。 此方法具有兩個泛型類型參數:TInput 用於指定輸入類型,TOutput 用於指定輸出類型。 TOutput 類型參數被限制為實現 ICollection(在 Visual Basic 中為 ICollection(Of TInput)),限制為引用類型,並且限制為具有無參數構造函數。 該方法具有壹個形參,這是壹個 TInput 的數組。 該方法返回 TOutput 的實例,該實例包含輸入數組中的所有元素。 TOutput 可以是實現 ICollectionT 泛型接口的任意泛型集合類型。 代碼執行時,該動態程序集會保存為 DemoGenericMethod1.dll,並可使用 Ildasm.exe(MSIL 反匯編程序) 對其進行檢查。 說明 學習如何發出代碼的壹種好方法是編寫執行您要發出的任務的 Visual Basic、C# 或 Visual C++ 程序,然後使用反匯編程序檢查編譯器生成的 MSIL。 該代碼示例包含等效於發出的方法的源代碼。 發出的方法是以後期綁定的形式進行調用的,同時也使用了在該代碼示例中聲明的泛型委托。 Imports System Imports System.Collections.Generic Imports System.Reflection Imports System.Reflection.Emit ' Declare a generic delegate that can be used to execute the ' finished method. ' Delegate Function D(Of TIn, TOut)(ByVal input() As TIn) As TOut Class GenericMethodBuilder ' This method shows how to declare, in Visual Basic, the generic ' method this program emits. The method has two type parameters, ' TInput and TOutput, the second of which must be a reference type ' (Class), must have a parameterless constructor (New), and must ' implement ICollection(Of TInput). This interface constraint ' ensures that ICollection(Of TInput).Add can be used to add ' elements to the TOutput object the method creates. The method ' has one formal parameter, input, which is an array of TInput. ' The elements of this array are copied to the new TOutput. ' Public Shared Function Factory(Of TInput, _ TOutput As {ICollection(Of TInput), Class, New}) _ (ByVal input() As TInput) As TOutput Dim retval As New TOutput() Dim ic As ICollection(Of TInput) = retval For Each t As TInput In input ic.Add(t) Next Return retval End Function Public Shared Sub Main() ' The following shows the usage syntax of the Visual Basic ' version of the generic method emitted by this program. ' Note that the generic parameters must be specified ' explicitly, because the compiler does not have enough ' context to infer the type of TOutput. In this case, TOutput ' is a generic List containing strings. ' Dim arr() As String = {"a", "b", "c", "d", "e"} Dim list1 As List(Of String) = _ GenericMethodBuilder.Factory(Of String, List(Of String))(arr) Console.WriteLine("The first element is: {0}", list1(0)) ' Creating a dynamic assembly requires an AssemblyName ' object, and the current application domain. ' Dim asmName As New AssemblyName("DemoMethodBuilder1") Dim domain As AppDomain = AppDomain.CurrentDomain Dim demoAssembly As AssemblyBuilder = _ domain.DefineDynamicAssembly(asmName, _ AssemblyBuilderAccess.RunAndSave) ' Define the module that contains the code. For an ' assembly with one module, the module name is the ' assembly name plus a file extension. Dim demoModule As ModuleBuilder = _ demoAssembly.DefineDynamicModule( _ asmName.Name, _ asmName.Name & ".dll") ' Define a type to contain the method. Dim demoType As TypeBuilder = demoModule.DefineType( _ "DemoType", _ TypeAttributes.Public) ' Define a Shared, Public method with standard calling ' conventions. Do not specify the parameter types or the ' return type, because type parameters will be used for ' those types, and the type parameters have not been ' defined yet. ' Dim factory As MethodBuilder = _ demoType.DefineMethod("Factory", _ MethodAttributes.Public Or MethodAttributes.Static) ' Defining generic type parameters for the method makes it a ' generic method. To make the code easier to read, each ' type parameter is copied to a variable of the same name. ' Dim typeParameterNames() As String = {"TInput", "TOutput"} Dim typeParameters() As GenericTypeParameterBuilder = _ factory.DefineGenericParameters(typeParameterNames) Dim TInput As GenericTypeParameterBuilder = typeParameters(0) Dim TOutput As GenericTypeParameterBuilder = typeParameters(1) ' Add special constraints. ' The type parameter TOutput is constrained to be a reference ' type, and to have a parameterless constructor. This ensures ' that the Factory method can create the collection type. ' TOutput.SetGenericParameterAttributes( _ GenericParameterAttributes.ReferenceTypeConstraint Or _ GenericParameterAttributes.DefaultConstructorConstraint) ' Add interface and base type constraints. ' The type parameter TOutput is constrained to types that ' implement the ICollection(Of T) interface, to ensure that ' they have an Add method that can be used to add elements. ' ' To create the constraint, first use MakeGenericType to bind ' the type parameter TInput to the ICollection(Of T) interface, ' returning the type ICollection(Of TInput), then pass ' the newly created type to the SetInterfaceConstraints ' method. The constraints must be passed as an array, even if ' there is only one interface. ' Dim icoll As Type = GetType(ICollection(Of )) Dim icollOfTInput As Type = icoll.MakeGenericType(TInput) Dim constraints() As Type = { icollOfTInput } TOutput.SetInterfaceConstraints(constraints) ' Set parameter types for the method. The method takes ' one parameter, an array of type TInput. Dim params()