VB.NET中的动态代码生成技巧

开发 后端
通常,VB.NET程序员会创建一个静态设计,而这一设计可以为程序的应用提供所需的一切。但是,在某些情况下,程序员或许不能够事先预计每种需求,这里就需要用到动态代码生成了。

本文的讨论也将着眼于这两种情况。首先是当程序员需要动态建立一个控件并将代码附着于控件之上时。例如,你或许想创建一个链接列表,但是不知道需要创建链接的数量或是链接中会出现什么样的数据。第二种是当程序员需要定义代码以反映出特殊需求的时候。例如,你或许要执行能反映用户系统配置的代码。

类似上述的情况当然不会每天都上演。事实上,它们只在非常情况下才出现。然而,作为程序员,仍然要意识到.NET为解决动态情形提供了方案。有了正确的技巧,你就可以写出能灵活处理动态情况的应用程序了。

使用动态控件

许多程序员总会遇到需要动态创建控件的时候。我们所展示的例子中程序员向FlowLayoutPanel中添加了LinkLabels。或许你可以个这样的设置来记录和保存常用的URL,文件,网络地址或是其他资源所在位置的数值。这一示例没有真正保存链接,但是你可以使用XML序列化功能来实现保存。

每次当用户点击Test按钮时,示例代码就会动态创建一个新的LinkLabel控件。真正的演示代码并不复杂。例一就展示了创建这类控件以及将控件放入FlowLayoutPanel,lstLabel中通常所需要做的一切。

例一:向FlowLayoutPanel中添加新的链接

  1. Private Sub btnTest_Click()   
  2. Handles btnTest.Click   
  3. ' Create a link.   
  4. Dim NewLink As LinkLabel =  
  5. New LinkLabel()   
  6. ' Add some properties to it.   
  7. NewLink.Text = DateTime.Now   
  8. .ToLongTimeString()   
  9. ' Set the click event handler.   
  10. AddHandler NewLink.Click,   
  11. AddressOf NewLink_Click   
  12. ' Place the button on the form.   
  13. lstLinks.Controls.Add(NewLink)   
  14. End Sub 


 如你所料,该代码开始的时候创建了一个新的LinkLabel并为其赋予了一些值。这一示例使用的是当前时间。你的代码或许能够对某一真实资源进行访问。

请注意该代码也向链接的Click事件中指定了一个处理程序。你必须使用示例中的AddHandler技巧,因为普通的Handles关键词路径不起作用。一方面,设计应用程序的时候你并不清楚控件的名称。即便你为控件指定了一个名称,你也不知道用户要创建的控件数量,所以我们没有办法清楚会有多少处理程序会被创建。处理程序的代码与控件代码类似,因此没有必要创建多个处理程序。用于这个示例的处理代码见例二。例二:处理动态控件点击事件

  1. Private Sub NewLink_Click( _   
  2. ByVal sender As System.Object, ByVal e As System.EventArgs)  
  3. ' Verify that you actually have a LinkLabel  
  4. If Not sender.GetType() Is GetType(LinkLabel) Then  
  5. MessageBox.Show("Wrong control type provided!")  
  6. Return  
  7. End If  
  8. ' Convert the input sender to a Button.  
  9. Dim ThisLink As LinkLabel = sender 
  10. ' Show that we have the correct button.  
  11. MessageBox.Show("You created this link at: " + ThisLink.Text)  
  12. End Sub 

 
你可能已经注意到例一中的事件处理器使用的是宽松代表——它没有将ByVal发送器作为System.Object,也没有将ByVal e作为System.EventArgs作为参数因为它不需要这二者。然而,当你创建一个事件处理器来动态创建控件时,通常你需要将ByVal发送器作为System.Object参数,这意味着将这两者都包含其中。

有些程序员在创建事件处理器的时候会出现一个错误,即没有检查传入控件的类型。发送器对象可能包含多选择,而如果未对事件处理器进行事件处理类型的设置,那么你就会面临更多的选择。我们的示例代码一开始就检查了传入控件对象的类型。这样以来发送器就不会像下面所展示的代码一样:      

  1. Private Sub btnTest2_Click() Handles btnTest2.Click   
  2. ' Create a link.  
  3. Dim NewButton As Button = New Button()  
  4. ' Add some properties to it.  
  5. NewButton.Text = DateTime.Now.ToLongTimeString()  
  6. ' Set the click event handler.  
  7. AddHandler NewButton.Click, AddressOf NewLink_Click  
  8. ' Place the button on the form.  
  9. lstLinks.Controls.Add(NewButton)  
  10. End Sub 


 此代码在FlowLayoutPanel中创建了一个按钮,大多数情况下这都能正常运行,除非事件处理器不按照按钮所示的进行操作。如果你打算服务多个控件类型,那么每个控件类型都需要一个独特的处理。你可以使用多事件处理器或者为某些类型提供选择标准。

NewLink_Click()事件处理器照常将传入发送器转换成指定类型,在这个示例中则是LinkLabel。该代码可以访问LinkLabel属性并能用其他方式进行互动。在我们的示例中,只显示了一个能在创建链接的时候告知我们的对话框。

使用动态代码

在运行时创建一个控件是在无法确定应用程序功能的时候采取的一种策略。但是动态创建控件并不适用于所有的情况。有些时候你必须建立可执行代码,虽然你的应用程序运行的目的是补偿不同极其之间的配置,不同用户的需求,不同的环境需求或是其他要求。当应用程序所运行的电脑不存在控件,那么通常是需要创建动态代码的。

幸运的是,.NET为我们提供了一系列动态代码选项。例如,你可以创建一个可执行的能独立运行的程序或是可以想运行中的程序加载一个DLL然后再执行。当你需要演示一个外部任务的时候可以使用选择可执行,如运行一种脚本——该DLL选项最适合扩大现有的应用程序功能。

你可以运行来自文件或内存的动态代码。当你需要不止一次地运行代码时,可以使用文件。对代码的检查可以再次运行外部文件而不需要对其进行二次编译。当你需要多次演示任务的时候,如一个安装请求,那可以使用内存图像。

当然我们也可以更改源代码。例如,你可以使用字符串来建立需要在应用程序中直接使用的代码。如果你需要代码具有高度灵活性,且代码本身不是很长时,这一方法的优势就非常显著。也可以从文件里建立代码,就如同VS一样。这一方法最适用于相对稳定且不需要复杂编码的需求。第三种选择是使用Documentation Object Model来创建代码并将其作为CodeDom树型结构的一个系列。该树型结构包括了CodeCormpileUnits。这就像是用DOM模式创建了一个XML文件。

使用动态创建代码的***方式是用示例来检查一下。例三展示了一个基本“Hello World”示例。该示例用源代码直接创建了代码因此你可以看到整个运行以及生成一个外部可执行文件的过程。

例三:动态编码示例

  1.  Private Sub btnTest3_Click() Handles btnTest3.Click   
  2. ' Create a compiler.  
  3. Dim Comp As VBCodeProvider = New VBCodeProvider()  
  4. ' Define the parameters for the code you want to compile.  
  5. Dim Parms As CompilerParameters = New CompilerParameters)  
  6.  
  7. ' We do want to create an executable, rather than a DLL.  
  8. Parms.GenerateExecutable = True 
  9. ' The compiler will create an output assembly called Output.  
  10. Parms.OutputAssembly = "Output" 
  11. ' The compiler won't treat warnings as errors.  
  12. Parms.TreatWarningsAsErrors = False 
  13. ' Add any assembly you want to reference.  
  14. Parms.ReferencedAssemblies.Add("System.Windows.Forms.dll")  
  15.  
  16. ' Define the code you want to run.  
  17.  
  18. Dim SampleCode As StringBuilder = New StringBuilder()  
  19.  
  20. SampleCode.Append("Imports System.Windows.Forms" + vbCrLf)  
  21.  
  22. SampleCode.Append("Module TestAssembly" + vbCrLf)  
  23.  
  24. SampleCode.Append("Sub Main()" + vbCrLf)  
  25.  
  26. SampleCode.Append("MessageBox.Show(" + Chr(34) + _  
  27. "Dynamically Created Code!" + _Chr(34) + ")" + vbCrLf)  
  28. SampleCode.Append("End Sub" + vbCrLf)  
  29. SampleCode.Append("End Module" + vbCrLf)  
  30. ' Define the code to run.  
  31. Dim Executable As CompilerResults = _ 
  32. Comp.CompileAssemblyFromSource(Parms, SampleCode.ToString())  
  33. ' Display error messages if there are any.  
  34. If Executable.Errors.HasErrors Then  
  35. For Each Item As CompilerError In Executable.Errors  
  36. MessageBox.Show(Item.ErrorText)  
  37. Next  
  38. Else  
  39. ' If there aren't any error messages, start the  
  40. ' executable.  
  41. Process.Start("Output")  
  42. End If  
  43. End Sub 

一开始你创建了一个使用VBCodeProvider的编译器Comp。旧一点的.NET版本使用的是不同的方法但是这里所讲的是微软推荐的一个新方法。
为了使用编译器,你必须创建能描述应用程序的参数。这些参数类似于VS中你创建的参数,只是现在你可以对它们进行定义。该代码一开始就将GenerateExecutable设置为True,这意味着你需要的是一个EXE文件而不是DLL。

Parms.OutputAssembly属性包含了输出文件的名称。你只需要在想创建文件时提供这一信息即可,而不需要生成可执行内存了。如果你ixiang生成可执行文件的内存版本,可以将Parm.GenerateInMemory属性设置为True。

使用Parm.TreatWarningsAsErrors属性来确定如何处理警告信息。默认的设置会使其为错误,这意味着你的应用程序可能无法对其进行编译。大多数程序员使用默认设置,尽管他们开发了程序,但是在开发完成的程序中却将其设置为False。

大多数应用程序需要外部DLL以正常运行。当然,你不能创建任意的没有引用外部DLL的Windows表单程序。通常,你要使用Reference文件夹来完成这一任务。不过,当你动态创建代码的时候可以依赖于Parms.ReferencedAssemblies属性。如下所示,只需添加你要的DLL即可。

现在,你已经定义了项目,接下来需要为其创建源代码。如前文所述,你可以依赖于一个外部文件或DOM模式。然后,该示例创建了代码因此你可以看到整个过程。下面是代码的原始形式:

  1. Imports System.Windows.Forms   
  2. Module TestAssembly  
  3. Sub Main()  
  4. MessageBox.Show("Dynamically Created Code!")  
  5. End Sub  
  6. End Module 

这个简单的例子显示了一个对话框。注意vbCrLf的使用。如果你不使用这一方法,那编译器会发送给你一个错误信息。vbCrLf条目在该代码中所起的作用与在程序代码中的作用相同,只是添加的方式不一样。

从这一点老说,你***会用Comp.CompileAsseblyFromSource()方法编译代码。当使用DOM模式和文件的时候可以使用这一方法。而在所有三种情况中,编译器用参数和源代码创建了你请求的输出。该运算的输出出现在Executable中,是CompilerResults类型。

编译的失败次数多于程序员的预计。无论你是在哪里使用动态编码技巧,你必须假设会出现失败的情况以及处理失败的方案。在本例中,代码寻找的是错误并在编译失败时将其展示在了信息框中。否则,代码会依赖于Process.Start()方法来启用可执行文件。

底线

动态编码技巧并不是***钥匙。当你为开发问题找到了好的静态解决方案时,当然也可以使用。但是在我们所列出的情况中没有可行的静态方案,因此要选择动态编码技巧。大多数情况下,要用动态编码技巧解决以下问题:

◆ 用户的环境会以不可预见的方式更改时;

◆ 无法控制用户电脑的安装;

◆ 用户或应用程序都添加了你要用控件执行的数据要素;

◆ 应用程序必须执行很早以前的安装任务,且这些任务与电脑,环境,网络或其他不确定因素联系紧密时;

◆ 应用程序要执行了处理级别的任务,且这些任务取决于机器连接或其他状况。

显然,还有其他一些情况能使用动态编码技巧。最重要的是记住但凡有不可预知的情况下要考虑使用动态编码技巧。通常在编码环境中出现了静态代码无法处理的情况时,我们就可以使用动态编码技巧。

【编辑推荐】

  1. C#和VB.NET类型相关知识汇总
  2. VB.NET中有用的通用对象列表
  3. VB.NET和C#的发展与动态语言运行时
  4. 用VB.NET 2008编写数据查询窗体
  5. VB.NET实现窗体图标最小化到状态栏
责任编辑:彭凡 来源: IT专家网论坛
相关推荐

2010-01-13 18:09:09

VB.NET动态生成代

2009-10-12 15:44:26

VB.NET动态编码技

2010-01-11 16:04:10

VB.NET使用wit

2009-10-12 15:41:09

VB.NET动态代码

2009-10-12 15:02:51

VB.NET动态控件

2009-11-10 13:08:13

VB.NET编程技巧

2010-01-18 18:20:49

VB.NET使用API

2010-01-12 18:00:50

VB.NET界面

2010-01-14 15:44:17

VB.NET数据绑定

2010-01-15 13:52:42

VB.NET属性设置

2010-01-20 18:51:16

VB.NET修改系统时

2010-01-18 18:50:26

VB.NET鼠标手势

2010-01-08 18:31:45

VB.NET历史菜单

2010-01-08 18:16:52

VB.NET变量

2010-01-15 19:04:09

2010-01-11 14:16:14

VB.NET生成验证码

2010-01-13 09:31:39

VB.NET窗体打印

2009-10-27 09:59:17

VB.NET动态代码

2010-01-18 16:33:57

VB.NET加密文件

2010-01-11 10:08:47

VB.NET事件通道
点赞
收藏

51CTO技术栈公众号