
在 年 Microsoft发布了四种ASP NET Web控件 用于为使用 Microsoft Internet Explorer 的 Web 访问者提供更好的体验 这些 Web 控件称为 Internet Explorer Web 控件 也简称为 IE Web 控件 包括以下四种控件 MultiPage Web 控件TabStrip Web 控件Toolbar Web 控件TreeView Web 控件这些 Web 控件通过提供 Web 访问者熟悉的用户界面 增强了 ASP NET Web 页面 例如 Toolbar Web 控件可以显示一个与各种 Microsoft Office 产品中的工具栏相类似的可单击工具栏 TabStrip 和 MultiPage Web 控件配合使用后可以显示选项卡式内容 TreeView Web 控件可以用来显示可单击 可展开的树中的数据 与在 Microsoft Windows 资源管理器中用树来显示 PC 文件系统中的驱动器和文件夹很类似 (您可以在 GotDotNet 站点找到这些 Web 控件的生动演示 WebControl Toolbar 演示 TabStrip/MultiPage WebControl 演示和 TreeView Sample WebControl 演示 )IE Web 控件可以在安装了 NET Framework 版本 或 的 Web 服务器上使用 尽管 IE Web 控件是设计用来与 Internet Explorer 配合使用的 但这些 Web 控件也可以显示在其他浏览器中 不过 在 Internet Explorer 以外的浏览器中 当用户与 IE Web 控件交互时(例如展开 TreeView Web 控件中的某个节点) 会发生回传 在 Internet Explorer 或更高版本中 IE Web 控件会发送 DHTML 代码 从而避免了回传的发生 也就是说 在访问您的 Web 站点时 使用 Internet Explorer 或更高版本的访问者将会比使用其他浏览器的用户获得更好的用户体验 当然 没有使用 IE 或更高版本的用户仍可以看到 IE Web 控件 并可以与之进行交互 下面 本文将主要介绍 TreeView IE Web 控件 并讨论如何使用此控件在 ASP NET Web 页面中显示数据 有关其他 IE Web 控件的详细信息 请参阅 Internet Explorer Web Controls Overview 和 Internet Explorer Web Controls Reference 安装IE Web控件要在 ASP NET Web 应用程序中使用 IE Web 控件 首先必须下载控件的源代码 然后运行一个编译批处理文件 对源代码进行编译并将所需的所有文件复制到相应的 Web 应用程序目录中 Internet Explorer Web 控件下载程序包是一个大小为 KB 的自解压安装文件 下载并安装完 IE Web 控件后 将创建一个新目录(默认为 C:\Program Files\IE Web Controls\ 您也可以在安装过程中配置此目录) 找到这个新目录 然后双击 build bat 文件 这将创建一个新的子目录 build 编译 src 子目录中的类 并将生成的程序集和支持文件复制到 build 子目录中 运行完 build bat 文件后 build 子目录将包含程序集文件 Microsoft Web UI WebControls dll 和子目录 Runtime 要在 ASP NET Web 应用程序中使用 IE Web 控件 必须将 build\Runtime 子目录中的内容复制到 Web 应用程序的 /webctrl_client/ _ 子目录中 并将程序集文件 (Microsoft Web UI WebControls dll) 复制到 Web 应用程序的 /bin 子目录中 (在 IE Web 控件的 README txt 文件中 提供了示例以及执行这些任务的命令行指令 )IE Web控件入门如果使用的是 Microsoft Visual Studio NET 来开发 ASP NET Web 应用程序 则将 IE Web 控件添加到 ASP NET Web 页是件轻松的事 首先 将 IE Web 控件包含在工具箱中 要完成这项 *** 作 请右击 Toolbox(工具箱) 然后选择 Customize Toolbox(自定义工具箱)选项 选择 NET Framework Components( NET Framework 组件)选项卡 然后单击 Browse(浏览)按钮 找到 Microsoft Web UI WebControls dll 程序集文件 然后单击 OK(确定) 这会将 MultiPage TabStrip Toolbar 和 TreeView 等 IE Web 控件添加到 Visual Studio NET 工具箱中 要将以上控件中的任何一种添加到 ASP NET Web 页中 只需要将相应的控件从工具箱拖放到设计器中即可 要在内含代码的类中使用 IE Web 控件 首先需要右击 Reference(引用) 然后选择 Add Reference(添加引用) 将引用添加到 Microsoft Web UI WebControls dll 程序集中 然后 在内含代码的类中 如果使用的是 C# 则添加 using Microsoft Web UI WebControls 如果使用的是 Microsoft Visual Basic NET 则添加 Imports Microsoft Web UI WebControls 如果不是使用 Visual Studio NET 作为 ASP NET Web 应用程序编辑器 则需要在 ASP NET Web 页的顶端手动添加以下 @Register 指令 <%@ Register TagPrefix=\whateverNamespace=\Microsoft Web UI WebControlsAssembly=\Microsoft Web UI WebControls\ %>然后 将IE Web控件添加到Web页中 可以使用以下语法 <whatever:WebControlName runat=\server\ > </whatever:WebControlName>例如 要添加 TreeView 控件 可以在页面顶端添加以下 @Register 指令 <%@ Register TagPrefix=\iewcNamespace=\Microsoft Web UI WebControlsAssembly=\Microsoft Web UI WebControls\ %>接着 在ASP NET Web页中希望显示TreeView的位置添加以下Web控件语法 <iewc:TreeView runat=\server\ > </iewc:TreeView>TreeView IE Web 控件入门当 TreeView IE Web 控件在访问者浏览器中显示时 会显示一棵树 此树与 Windows 资源管理器中的树非常类似 不同的是 TreeView 可以由任意多个 TreeNode 对象组成 每个 TreeNode 对象都可以关联文本和图像 另外 TreeNode 还可以显示为超链接并与某个 URL 相关联 每个 TreeNote 还可以包括任意多个子 TreeNote 对象 包含 TreeNode 及其子节点的层次结构构成了 TreeView 控件所呈现的树结构 假设您要构建一个用于显示家谱的 TreeView 控件 由于信息基本上不需要改动 因此您可能希望静态地指定 TreeView 结构 如果使用的是 Visual Studio NET 则静态指定 TreeView 结构就像填写几份表格一样简单 首先 通过将 TreeView 控件从工具箱拖放到设计器中 将新的 TreeView 控件添加到 ASP NET Web 页中 然后 将 TreeView 控件的 ID 属性设置为 tvFamilyTree 现在 要静态指定组成 TreeView 的 TreeNode 请从 Properties(属性)窗格中选择 Nodes(节点)属性 然后单击此属性右侧的省略号按钮 这时将显示 TreeNodeEditor(TreeNode 编辑器)对话框 现在可以将新的 TreeNode 添加到 TreeView 中 填充 TreeNodeEditor(TreeNote 编辑器)对话框后 以下标记将被添加到 ASP NET Web 页的 aspx 部分 <ie:TreeView id=\tvFamilyTree\ runat=\server\><ie:TreeNode Text=\John Smith\><ie:TreeNode Text=\Born: Jan rd \></ie:TreeNode><ie:TreeNode Text=\Died: Feb \></ie:TreeNode><ie:TreeNode Text=\Spouse\><ie:TreeNode Text=\Marie Ellsworth\><ie:TreeNode Text=\Born: Aug \></ie:TreeNode><ie:TreeNode Text=\Died: Unknown\></ie:TreeNode></ie:TreeNode><ie:TreeNode Text=\Children\><ie:TreeNode Text=\John Smith Jr \><ie:TreeNode Text=\Born: July \></ie:TreeNode><ie:TreeNode Text=\Died: Sept \></ie:TreeNode></ie:TreeNode><ie:TreeNode Text=\Mary Smith\><ie:TreeNode Text=\Born: June \></ie:TreeNode><ie:TreeNode Text=\Died: Aug \></ie:TreeNode><ie:TreeNode Text=\Spouse\><ie:TreeNode Text=\Edward Joy\><ie:TreeNode Text=\Born: Unknown\></ie:TreeNode><ie:TreeNode Text=\Died: Aug \></ie:TreeNode></ie:TreeNode><ie:TreeNode Text=\Children\><ie:TreeNode Text=\Michael Joy\><ie:TreeNode Text=\Born: Oct \></ie:TreeNode></ie:TreeNode><ie:TreeNode Text=\Michele Joy\><ie:TreeNode Text=\Born: May \></ie:TreeNode></ie:TreeNode></ie:TreeNode></ie:TreeNode></ie:TreeNode></ie:TreeNode></ie:TreeNode></ie:TreeNode></ie:TreeView>如果不是使用 Visual Studio NET 作为编辑器 则需要将此内容手动添加到 ASP NET Web 页 当通过浏览器查看 ASP NET Web 页时 将显示一棵可展开的树 默认情况下 将仅显示根节点 您可以设置 TreeNote 的 Expanded 属性 以指示首次查看此页面时该 TreeNote 应为展开状态 除了文本标签以外 TreeNote 还可以关联图像 尤其值得说明的是 每个 TreeNote 可以关联三幅图像 当 TreeNote 处于标准(折叠)状态时显示一幅图像 当 TreeNot lishixinzhi/Article/program/net/201311/12788
第二个过程演示如何发出方法体,以及如何使用泛型方法的类型参数创建泛型类型的实例以及调用其方法。 第三个过程演示如何调用泛型方法。 重要事项 不能仅因为某个方法属于泛型类型且使用泛型类型的类型参数,就称该方法是泛型方法。 只有当方法具有自己的类型参数列表时,才能称其为泛型方法。 泛型方法可以出现在非泛型类型上,在本例中就是如此。 有关泛型类型上的非泛型方法的示例,请参见 如何:用反射发出定义泛型类型。 定义泛型方法 首先,如果使用高级语言编写,则查看泛型方法的显示方式会十分有用。 下面的代码包含在此主题的代码示例中,用于调用泛型方法的代码同样也包含在其中。 该方法具有两个类型参数 TInput 和TOutput,后者必须为引用类型 (class),必须具有无参数构造函数 (new),并且必须实现 ICollection(Of TInput)(在 C# 中为 ICollection<TInput>)。 此接口约束确保 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 icAdd(t) Next Return retval End Function public static TOutput Factory<TInput, TOutput>(TInput[] tarray) where TOutput : class, ICollection<TInput>, new() { TOutput ret = new TOutput(); ICollection<TInput> ic = ret; foreach (TInput t in tarray) { icAdd(t); } return ret; } 定义一个动态程序集和一个动态模块,以包含该泛型方法所属的类型。 在本例中,该程序集仅拥有一个模块(名为 DemoMethodBuilder1),模块名称即为程序集名称加上一个扩展名。 在此示例中,将程序集保存到磁盘中并执行,因此指定了 AssemblyBuilderAccessRunAndSave。 您可以使用 Ildasmexe(MSIL 反汇编程序) 检查DemoMethodBuilder1dll,并将其与步骤 1 中显示的方法的 Microsoft 中间语言 (MSIL) 进行比较。 Dim asmName As New AssemblyName("DemoMethodBuilder1") Dim domain As AppDomain = AppDomainCurrentDomain Dim demoAssembly As AssemblyBuilder = _ domainDefineDynamicAssembly(asmName, _ AssemblyBuilderAccessRunAndSave) ' 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 = _ demoAssemblyDefineDynamicModule( _ asmNameName, _ asmNameName & "dll") AssemblyName asmName = new AssemblyName("DemoMethodBuilder1"); AppDomain domain = AppDomainCurrentDomain; AssemblyBuilder demoAssembly = domainDefineDynamicAssembly(asmName, AssemblyBuilderAccessRunAndSave); // 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 = demoAssemblyDefineDynamicModule(asmNameName, asmNameName+"dll"); 定义该泛型方法所属的类型。 该类型不一定为泛型类型。 泛型方法可以属于泛型类型,也可以属于非泛型类型。 在此示例中,该类型为一个类,它不是泛型类型,名称为 DemoType。 Dim demoType As TypeBuilder = demoModuleDefineType( _ "DemoType", _ TypeAttributesPublic) TypeBuilder demoType = demoModuleDefineType("DemoType", TypeAttributesPublic); 定义泛型方法。 如果泛型方法的形参的类型由该泛型方法的泛型类型参数指定,请使用 DefineMethod(String, MethodAttributes) 方法重载定义该方法。 该方法的泛型类型参数尚未定义,因此不能在对 DefineMethod 的调用中指定该方法的形参的类型。 在此示例中,该方法名为 Factory。 该方法是公共方法,并且是 static(在 Visual Basic 中为 Shared)的。 Dim factory As MethodBuilder = _ demoTypeDefineMethod("Factory", _ MethodAttributesPublic Or MethodAttributesStatic) MethodBuilder factory = demoTypeDefineMethod("Factory", MethodAttributesPublic | MethodAttributesStatic); 通过将包含参数名称的字符串数组传递给 MethodBuilderDefineGenericParameters 方法来定义 DemoMethod 的泛型类型参数。 这使得该方法成为泛型方法。 下面的代码使得 Factory 成为具有类型参数 TInput 和TOutput 的泛型方法。 若要使代码更易于阅读,可创建具有这些名称的变量,以保存表示这两个类型参数的 GenericTypeParameterBuilder 对象。 Dim typeParameterNames() As String = {"TInput", "TOutput"} Dim typeParameters() As GenericTypeParameterBuilder = _ factoryDefineGenericParameters(typeParameterNames) Dim TInput As GenericTypeParameterBuilder = typeParameters(0) Dim TOutput As GenericTypeParameterBuilder = typeParameters(1) string[] typeParameterNames = {"TInput", "TOutput"}; GenericTypeParameterBuilder[] typeParameters = factoryDefineGenericParameters(typeParameterNames); GenericTypeParameterBuilder TInput = typeParameters[0]; GenericTypeParameterBuilder TOutput = typeParameters[1]; 可以选择为类型参数添加特殊约束。 特殊约束是通过使用 SetGenericParameterAttributes 方法添加的。 在此示例中,TOutput 被约束为引用类型,并且具有无参数构造函数。 TOutputSetGenericParameterAttributes( _ GenericParameterAttributesReferenceTypeConstraint Or _ GenericParameterAttributesDefaultConstructorConstraint) TOutputSetGenericParameterAttributes( GenericParameterAttributesReferenceTypeConstraint | GenericParameterAttributesDefaultConstructorConstraint); 可以选择为类型参数添加类约束和接口约束。 在此示例中,类型参数 TOutput 被约束为实现 ICollection(Of TInput)(在 C# 中为 ICollection<TInput>)接口的类型。 这确保 Add 方法可用于添加元素。 Dim icoll As Type = GetType(ICollection(Of )) Dim icollOfTInput As Type = icollMakeGenericType(TInput) Dim constraints() As Type = { icollOfTInput } TOutputSetInterfaceConstraints(constraints) Type icoll = typeof(ICollection<>); Type icollOfTInput = icollMakeGenericType(TInput); Type[] constraints = {icollOfTInput}; TOutputSetInterfaceConstraints(constraints); 使用 SetParameters 方法定义该方法的形参。 在此示例中,Factory 方法具有一个参数,即 TInput 的数组。 此类型是通过对表示 TInput 的 GenericTypeParameterBuilder 调用 MakeArrayType 方法创建的。 SetParameters 的参数是 Type 对象的数组。 Dim params() As Type = { TInputMakeArrayType() } factorySetParameters(params) Type[] parms = {TInputMakeArrayType()}; factorySetParameters(parms); 使用 SetReturnType 方法定义该方法的返回类型。 在此示例中,返回 TOutput 的一个实例。 factorySetReturnType(TOutput) factorySetReturnType(TOutput); 使用 ILGenerator 发出该方法体。 有关详细信息,请参见附带的过程发出方法体。 重要事项 发出对泛型类型的方法的调用,而且这些类型的类型变量为该泛型方法的类型参数时,您必须使用 TypeBuilder 类的static GetConstructor(Type, ConstructorInfo)、 GetMethod(Type, MethodInfo) 和 GetField(Type, FieldInfo) 方法重载,以获取这些方法的构造形式。 发出方法体的附带过程对此进行了演示。 完成包含该方法的类型并保存程序集。 附带的过程调用泛型方法演示了调用完整方法的两种方式。 ' Complete the type Dim dt As Type = demoTypeCreateType() ' Save the assembly, so it can be examined with Ildasmexe demoAssemblySave(asmNameName & "dll") // Complete the type Type dt = demoTypeCreateType(); // Save the assembly, so it can be examined with Ildasmexe demoAssemblySave(asmNameName+"dll"); 发出方法体 获取代码生成器并声明局部变量和标签。 DeclareLocal 方法用于声明局部变量。 Factory 方法具有四个局部变量:retVal 用于保存该方法返回的新 TOutput,ic 用于在 TOutput 转换为 ICollection(Of TInput)(在 C# 中为 ICollection<TInput>)时将其保存,input 用于保存 TInput 对象的输入数组,而 index 用于循环访问该数组。 该方法还具有使用 DefineLabel 方法定义的两个标签,一个用于进入循环 (enterLoop),另一个用于循环的顶部 (loopAgain)。 该方法首先使用 Ldarg_0 *** 作码加载其参数,然后使用 Stloc_S *** 作码将参数存储在局部变量 input 中。 Dim ilgen As ILGenerator = factoryGetILGenerator() Dim retVal As LocalBuilder = ilgenDeclareLocal(TOutput) Dim ic As LocalBuilder = ilgenDeclareLocal(icollOfTInput) Dim input As LocalBuilder = _ ilgenDeclareLocal(TInputMakeArrayType()) Dim index As LocalBuilder = _ ilgenDeclareLocal(GetType(Integer)) Dim enterLoop As Label = ilgenDefineLabel() Dim loopAgain As Label = ilgenDefineLabel() ilgenEmit(OpCodesLdarg_0) ilgenEmit(OpCodesStloc_S, input) ILGenerator ilgen = factoryGetILGenerator(); LocalBuilder retVal = ilgenDeclareLocal(TOutput); LocalBuilder ic = ilgenDeclareLocal(icollOfTInput); LocalBuilder input = ilgenDeclareLocal(TInputMakeArrayType()); LocalBuilder index = ilgenDeclareLocal(typeof(int)); Label enterLoop = ilgenDefineLabel(); Label loopAgain = ilgenDefineLabel(); ilgenEmit(OpCodesLdarg_0); ilgenEmit(OpCodesStloc_S, input); 使用 ActivatorCreateInstance 方法的泛型方法重载发出代码以创建 TOutput 的实例。 使用此重载需要指定类型具有无参数构造函数,这也是向 TOutput 添加该约束的原因。 通过将 TOutput 传递到 MakeGenericMethod 创建构造的泛型方法。 发出代码以调用该方法后,发出代码以使用 Stloc_S 将其存储在局部变量 retVal 中 Dim createInst As MethodInfo = _ GetType(Activator)GetMethod("CreateInstance", TypeEmptyTypes) Dim createInstOfTOutput As MethodInfo = _ createInstMakeGenericMethod(TOutput) ilgenEmit(OpCodesCall, createInstOfTOutput) ilgenEmit(OpCodesStloc_S, retVal) MethodInfo createInst = typeof(Activator)GetMethod("CreateInstance", TypeEmptyTypes); MethodInfo createInstOfTOutput = createInstMakeGenericMethod(TOutput); ilgenEmit(OpCodesCall, createInstOfTOutput); ilgenEmit(OpCodesStloc_S, retVal); 发出代码以将新的 TOutput 对象强制转换为 ICollection(Of TInput),并将其存储在局部变量 ic 中。 ilgenEmit(OpCodesLdloc_S, retVal) ilgenEmit(OpCodesBox, TOutput) ilgenEmit(OpCodesCastclass, icollOfTInput) ilgenEmit(OpCodesStloc_S, ic) ilgenEmit(OpCodesLdloc_S, retVal); ilgenEmit(OpCodesBox, TOutput); ilgenEmit(OpCodesCastclass, icollOfTInput); ilgenEmit(OpCodesStloc_S, ic); 获取表示 ICollectionTAdd 方法的 MethodInfo。 此方法对 ICollection(Of TInput)(在 C# 中为 ICollection<TInput>)进行 *** 作,因此有必要获取特定于该构造类型的 Add 方法。 不能使用 GetMethod 方法直接从 icollOfTInput 获取此 MethodInfo,因为 GetMethod 在已使用 GenericTypeParameterBuilder 构造的类型上不受支持。 而应该对包含 ICollectionT 泛型接口的泛型类型定义的 icoll 调用 GetMethod。 然后使用 GetMethod(Type, MethodInfo)static 方法生成构造类型的 MethodInfo。 下面的代码对此进行了演示。 Dim mAddPrep As MethodInfo = icollGetMethod("Add") Dim mAdd As MethodInfo = _ TypeBuilderGetMethod(icollOfTInput, mAddPrep) MethodInfo mAddPrep = icollGetMethod("Add"); MethodInfo mAdd = TypeBuilderGetMethod(icollOfTInput, mAddPrep); 发出代码以初始化 index 变量(通过加载 32 位整数 0 并将其存储在变量中)。 发出代码以分支到标签 enterLoop。 因为此标签位于循环内,所以尚未进行标记。 该循环的代码在下一步中发出。 ' Initialize the count and enter the loop ilgenEmit(OpCodesLdc_I4_0) ilgenEmit(OpCodesStloc_S, index) ilgenEmit(OpCodesBr_S, enterLoop) // Initialize the count and enter the loop ilgenEmit(OpCodesLdc_I4_0); ilgenEmit(OpCodesStloc_S, index); ilgenEmit(OpCodesBr_S, enterLoop); 发出该循环的代码。 第一步是标记循环的顶部,方法是用 loopAgain 标签调用 MarkLabel。 使用该标签的分支语句现在将分支到代码中的这个点。 下一步是将强制转换为 ICollection(Of TInput) 的TOutput 对象推入堆栈。 这不需要立即进行,但需要在调用 Add 方法之前完成。 接下来,输入数组被推入堆栈,然后是包含该数组的当前索引的 index 变量。 Ldelem *** 作码从堆栈中d出该索引和数组,然后将索引数组元素推入堆栈。 堆栈现在准备好调用 ICollectionTAdd 方法,该方法从堆栈中d出集合和新的元素,并将该元素添加到该集合中。 循环中的其他代码会使索引递增,并通过测试查看循环是否完成:将索引和 32 位整数 1 推入堆栈并相加,在堆栈上保留总和;总和存储在 index 中。 然后调用 MarkLabel 将该点设置为循环的入口点。 再次加载该索引。 将输入数组推入堆栈,然后发出 Ldlen 以获取其长度。 索引和长度现在位于堆栈中,并发出 Clt 对二者进行比较。 如果索引小于长度, Brtrue_S 会重新返回到循环的起始点。 ilgenMarkLabel(loopAgain) ilgenEmit(OpCodesLdloc_S, ic) ilgenEmit(OpCodesLdloc_S, input) ilgenEmit(OpCodesLdloc_S, index) ilgenEmit(OpCodesLdelem, TInput) ilgenEmit(OpCodesCallvirt, mAdd) ilgenEmit(OpCodesLdloc_S, index) ilgenEmit(OpCodesLdc_I4_1) ilgenEmit(OpCodesAdd) ilgenEmit(OpCodesStloc_S, index) ilgenMarkLabel(enterLoop) ilgenEmit(OpCodesLdloc_S, index) ilgenEmit(OpCodesLdloc_S, input) ilgenEmit(OpCodesLdlen) ilgenEmit(OpCodesConv_I4) ilgenEmit(OpCodesClt) ilgenEmit(OpCodesBrtrue_S, loopAgain) ilgenMarkLabel(loopAgain); ilgenEmit(OpCodesLdloc_S, ic); ilgenEmit(OpCodesLdloc_S, input); ilgenEmit(OpCodesLdloc_S, index); ilgenEmit(OpCodesLdelem, TInput); ilgenEmit(OpCodesCallvirt, mAdd); ilgenEmit(OpCodesLdloc_S, index); ilgenEmit(OpCodesLdc_I4_1); ilgenEmit(OpCodesAdd); ilgenEmit(OpCodesStloc_S, index); ilgenMarkLabel(enterLoop); ilgenEmit(OpCodesLdloc_S, index); ilgenEmit(OpCodesLdloc_S, input); ilgenEmit(OpCodesLdlen); ilgenEmit(OpCodesConv_I4); ilgenEmit(OpCodesClt); ilgenEmit(OpCodesBrtrue_S, loopAgain); 发出代码以将 TOutput 对象推入堆栈,并从该方法返回。 局部变量 retVal 和ic 均包含对新的 TOutput 的引用;ic 仅用于访问 ICollectionTAdd 方法。 ilgenEmit(OpCodesLdloc_S, retVal) ilgenEmit(OpCodesRet) ilgenEmit(OpCodesLdloc_S, retVal); ilgenEmit(OpCodesRet); 调用泛型方法Factory 为泛型方法定义。 若要调用泛型方法,您必须为泛型方法的泛型类型参数分配类型。 使用 MakeGenericMethod 方法可完成此 *** 作。 下面的代码通过为 TInput 指定 String 并为TOutput 指定List(Of String)(在 C# 中为 List<string>)创建一个构造的泛型方法,并显示该方法的字符串表示形式。 Dim m As MethodInfo = dtGetMethod("Factory") Dim bound As MethodInfo = mMakeGenericMethod( _ GetType(String), GetType(List(Of String))) ' Display a string representing the bound method ConsoleWriteLine(bound) MethodInfo m = dtGetMethod("Factory"); MethodInfo bound = mMakeGenericMethod(typeof(string), typeof(List<string>)); // Display a string representing the bound method ConsoleWriteLine(bound); 若要以后期绑定的形式调用该方法,请使用 Invoke 方法。 下面的代码创建 Object(包含的唯一元素为字符串数组)的数组,并将其作为泛型方法的参数列表传递。 Invoke 的第一个参数为空引用,因为该方法为 static 的。 返回值被强制转换为 List(Of String),并显示它的第一个元素。 Dim o As Object = boundInvoke(Nothing, New Object() { arr }) Dim list2 As List(Of String) = CType(o, List(Of String)) ConsoleWriteLine("The first element is: {0}", list2(0)) object o = boundInvoke(null, new object[]{arr}); List<string> list2 = (List<string>) o; ConsoleWriteLine("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) ConsoleWriteLine("The first element is: {0}", list3(0)) Type dType = typeof(D<string, List <string>>); D<string, List <string>> test; test = (D<string, List <string>>) DelegateCreateDelegate(dType, bound); List<string> list3 = test(arr); ConsoleWriteLine("The first element is: {0}", list3[0]); 发出的方法也可以从引用保存的程序集的程序调用。 示例 下面的代码示例使用泛型方法 Factory 创建一个非泛型类型 DemoType。 此方法具有两个泛型类型参数:TInput 用于指定输入类型,TOutput 用于指定输出类型。 TOutput 类型参数被限制为实现 ICollection<TInput>(在 Visual Basic 中为 ICollection(Of TInput)),限制为引用类型,并且限制为具有无参数构造函数。 该方法具有一个形参,这是一个 TInput 的数组。 该方法返回 TOutput 的实例,该实例包含输入数组中的所有元素。 TOutput 可以是实现 ICollectionT 泛型接口的任意泛型集合类型。 代码执行时,该动态程序集会保存为 DemoGenericMethod1dll,并可使用 Ildasmexe(MSIL 反汇编程序) 对其进行检查。 说明 学习如何发出代码的一种好方法是编写执行您要发出的任务的 Visual Basic、C# 或 Visual C++ 程序,然后使用反汇编程序检查编译器生成的 MSIL。 该代码示例包含等效于发出的方法的源代码。 发出的方法是以后期绑定的形式进行调用的,同时也使用了在该代码示例中声明的泛型委托。 Imports System Imports SystemCollectionsGeneric Imports SystemReflection Imports SystemReflectionEmit ' 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 icAdd(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) = _ GenericMethodBuilderFactory(Of String, List(Of String))(arr) ConsoleWriteLine("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 = AppDomainCurrentDomain Dim demoAssembly As AssemblyBuilder = _ domainDefineDynamicAssembly(asmName, _ AssemblyBuilderAccessRunAndSave) ' 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 = _ demoAssemblyDefineDynamicModule( _ asmNameName, _ asmNameName & "dll") ' Define a type to contain the method Dim demoType As TypeBuilder = demoModuleDefineType( _ "DemoType", _ TypeAttributesPublic) ' 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 = _ demoTypeDefineMethod("Factory", _ MethodAttributesPublic Or MethodAttributesStatic) ' 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 = _ factoryDefineGenericParameters(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 ' TOutputSetGenericParameterAttributes( _ GenericParameterAttributesReferenceTypeConstraint Or _ GenericParameterAttributesDefaultConstructorConstraint) ' 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 = icollMakeGenericType(TInput) Dim constraints()
一、准备工作
具体说来,编程实现COM-->Assembly的功能,需要使用的以前几个类:
SystemRuntimeInteropServices
-TypeLibConverter提供一组服务,将托管程序集转换为 COM 类型库或进行反向转换。
-ITypeLibImporterNotifySink提供回调机制,以供类型库转换器向调用方通知转换的状态,并在转换过程本身之中涉及调用方。
SystemReflection
-StrongNameKeyPair(可选)封装对公钥或私钥对的访问,该公钥或私钥对用于为强名称程序集创建签名。
SystemReflectionEmit
-AssemblyBuilder 定义并表示动态程序集。
此外,还需要使用一个WinAPI,LoadTypeLibEx,具体定义如下:
[DllImport( "oleaut32dll", CharSet = CharSetUnicode, PreserveSig = false )]
private static extern void LoadTypeLibEx(String strTypeLibName, RegKind regKind, [MarshalAs(UnmanagedTypeInterface)] out Object typeLib );
为了让这个WinAPI function可以正常使用,我们还需要定义一个枚举,
private enum RegKind
{
RegKind_Default = 0,
RegKind_Register = 1,
RegKind_None = 2
}
注:上述类的说明来自MSDN。
大家都看到了,上述几个类中,仅有StrongNameKeyPair是可选的,这是因为如果我们不需要生成PIA,那么是不需要使用这个类的。同时,如果需要生成PIA,那么需要提供相应的密钥文件。在后面的描述中,我们将使用《走近COM Interop--浅谈PIA》中的例子做进一步的演示。
二、实战演练
在此,我们仍就由VB生成的PIADemodll展开演示。
1 载入一个COM组件
Object typeLib;
LoadTypeLibEx("PIADemodll", RegKindRegKind_None, out typeLib);
if(typeLib == null )
{
throw new Exception("载入失败!");
}
2 定义一个实现ITypeLibImporterNotifySink接口的类,基于提供回调机制,以供类型库转换器向调用方通知转换的状态,并在转换过程本身之中涉及调用方。
public class ConversionEventHandler: ITypeLibImporterNotifySink
{
public void ReportEvent(ImporterEventKind eventKind, int eventCode, string eventMsg )
{
// Do nothing
}
public Assembly ResolveRef(object typeLib)
{
// 此处直接返回null,避免把演示复杂化了
return null;
}
}
3 将COM类型库生成程序集
A 生成PIA Assembly
FileStream stream = new FileStream("commonsnk", FileModeOpen);
try
{
StrongNameKeyPair pair = new StrongNameKeyPair(stream);
TypeLibConverter converter = new TypeLibConverter();
ConversionEventHandler eventHandler = new ConversionEventHandler();
AssemblyBuilder ab = converterConvertTypeLibToAssembly(typeLib, "interopPIADemodll", TypeLibImporterFlagsPrimaryInteropAssembly, eventHandler, null, pair, null, null);
abSave("interopPIADemodll");
MessageBoxShow("Importing is ok");
Assembly asm = AssemblyLoadFile(ApplicationStartupPath + @"\interopPIADemodll");
Type t = asmGetType("interopPIADemoTestClass");
object obj = tInvokeMember(null, BindingFlagsDeclaredOnly | BindingFlagsPublic | BindingFlagsNonPublic | BindingFlagsInstance | BindingFlagsCreateInstance, null, null, null);
string ret = (string)tInvokeMember("Format", BindingFlagsDeclaredOnly | BindingFlagsPublic | BindingFlagsNonPublic |
BindingFlagsInstance | BindingFlagsInvokeMethod, null, obj, new object[]{"Go!"});
MessageBoxShow(ret);
}
catch(Exception ep)
{
if(stream != null)
{
streamClose();
}
MessageBoxShow(epMessage);
}
B 生成一般的Assembly
TypeLibConverter converter = new TypeLibConverter();
ConversionEventHandler eventHandler = new ConversionEventHandler();
AssemblyBuilder ab = converterConvertTypeLibToAssembly(typeLib, "interopPIADemodll", 0,
eventHandler, null, null, null, null);
abSave("interopPIADemodll");
MessageBoxShow("Importing is ok");
Assembly asm = AssemblyLoadFile(ApplicationStartupPath + @"\interopPIADemodll");
Type t = asmGetType("interopPIADemoTestClass");
object obj = tInvokeMember(null, BindingFlagsDeclaredOnly | BindingFlagsPublic | BindingFlagsNonPublic | BindingFlagsInstance | BindingFlagsCreateInstance, null, null, null);
string ret = (string)tInvokeMember("Format", BindingFlagsDeclaredOnly | BindingFlagsPublic | BindingFlagsNonPublic |
BindingFlagsInstance | BindingFlagsInvokeMethod, null, obj, new object[]{"Go!"});
MessageBoxShow(ret);
需要说明几点:
1 上述示例中使用的PIADemodll和Commonsnk都需要被copy至测试程序的bin目录中,否则,就需要指定可达到的文件路径。
2 AssemblyLoadFile的参数是要加载的文件的绝对路径,相对路径将会引发异常。
将资源文件放到设备上的某个目录中。
创建一个文本文件,其中每行列出一个文件及其路径。
以 gac 扩展名将该文本文件保存到设备的 Windows 目录中。
程序集文件可以是 ANSI 或 UTF-8 编码文件,但不支持其他 Unicode 编码。
以下是一个 MyDllsgac 示例,它列出了三个要安装到全局程序集缓存中的文件:
\Program Files\MyApp\MyDll1Dll
\Program Files\MyApp\MyDll2Dll
\Program Files\MyApp\MyDll3Dll
下次运行应用程序时,NET Compact Framework 会将您在 gac 文本文件中列出的文件移动到全局程序集缓存中。这些文件必须用强名称签名。目前不支持延迟签名。对 gac 文本文件的任何更改将导致以下结果:
如果从 Windows 目录中删除 gac 文本文件,则下次运行应用程序时将从全局程序集缓存中移除该文本文件中列出的文件。
如果更新 gac 文本文件,则 NET Compact Framework 将在全局程序集缓存中相应地更新该文件。
不能使用同一名称加载各个程序集。您必须更改 DLL 的名称,或在 DLL 具有强名称时,可将其放入全局程序集缓存中并使用具有完整强名称的Load方法。通过在桌面上打开命令行和使用
相反,必须通过在单独的文件中指定程序集属性来设置版本号。然后在 Webconfig 文件中使用 的 compilerOptions 属性,或在 aspx 页中使用 @ Page 指令的 CompilerOptions 属性。此过程使用程序集信息文件设置网站的版本号,并演示如何从 Webconfig 文件和 aspx 页中包含程序集信息文件。有关预编译的更多信息,请参见 ASPNET 网站预编译。为应用程序创建程序集信息文件使用文本编辑器创建一个新的程序集信息文件。对于 Visual Basic 应用程序,建议的文件名为 AssemblyInfovb。对于 C# 应用程序,建议的文件名为 AssemblyInfocs。将下列代码添加到程序集信息文件。<assembly:SystemReflectionAssemblyVersionAttribute("versionNumber")>[assembly:SystemReflectionAssemblyVersionAttribute("versionNumber")]有关versionNumber 参数的格式的信息,请参见 类。不要将程序集信息文件放在 App_Code 目录中。如果将程序集信息文件放在 App_Code 目录中,ASPNET 运行库将自动编译它,并且可能在以后的编译过程中导致编译错误。 在aspx 页中指定程序集信息文件在文本编辑器中打开 aspx 文件。将以下属性添加到 aspx 页中的 @ Page 指令。CompilerOptions="path\AssemblyInfovb"CompilerOptions="path\AssemblyInfocs"将path 参数替换为程序集信息文件在磁盘上的物理路径。如果程序集信息文件的路径包含空格,则必须用单引号 (') 将路径和文件名括起。CompilerOptions='"path with spaces\AssemblyInfovb"'CompilerOptions='"path with spaces\AssemblyInfocs"'将path with spaces 参数替换为程序集信息文件在磁盘上的物理路径。编译应用程序以进行部署。有关更多信息,请参见如何:预编译 ASPNET 网站以进行部署。在Webconfig 文件中指定程序集信息文件在文本编辑器中打开 Webconfig 文件。向Webconfig 文件添加下面的代码。<systemcodedom> <compilers> <compiler language="vb;vbs;visualbasic;vbscript" extension="vb" type="MicrosoftVisualBasicVBCodeProvider, System, Version=2036000, Culture=neutral, PublicKeyToken=b77a5c561934e089" compilerOptions="path\AssemblyInfovb" /> </compilers> </systemcodedom> <systemcodedom> <compilers> <compiler language="c#;cs;csharp" extension="cs" type="MicrosoftCSharpCSharpCodeProvider, System, Version=2036000, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="1" compilerOptions="path\AssemblyInfocs" /> </compilers> </systemcodedom>编译应用程序以进行部署。请参见
以上就是关于ASP.NET中使用TreeView控件系列全部的内容,包括:ASP.NET中使用TreeView控件系列、如何:用反射发出定义泛型方法、如何用C#将COM组件转换成程序集等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)