重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Visual Basic .NET支持动态属性,其动态属性储存在一个应用程序配置文件中,该文件在应用程序执行时将会被读到。使用动态属性可以在不重新编译应用程序的情况下改变一些属性值。你可以使用它们保存数据库连接字符串、应用程序日志信息或者服务器连接信息。
创新互联主营绥化网站建设的网络公司,主营网站建设方案,手机APP定制开发,绥化h5成都小程序开发搭建,绥化网站营销推广欢迎绥化等地区企业咨询
要看动态属性的例子,首先在Visual Basic中创建一个Windows应用程序。添加一个TextBox到默认窗体中;确认TextBox是被选中的,然后在属性窗口中定位Dynamic属性。
点击Advanced标签旁边的省略号按钮,打开一个对话框,该对话框中列出了TextBox所有的属性,选择Text属性,键映射框将会被激活。键映射决定了键储存在配置文件中的值。点击确定关闭对话框。
Dim OraSession As Object '会话对象
Dim OraDatabase As Object '数据库对象
Dim OraDynaset As Object'动态集对象
Dim OraDatabase As Object’字段集对象
Set OraSession =CreateObject("OracleInProcServer.XOraSession")
Set OraDatabase=OraSession.CreateDatabase
("Demo","scott/tiger",0)'建立数据库连接
Set OraDynaset=OraDatabase.CreateDynaset("select*from
student",0)'初始化动态集对象
’添加记录
'Begin an AddNew
OraDynaset.DbAddNew
OraDynaset.Fields("ID").value="1000"
’ID为列名
OraDynaset.DbUpdate'更新数据到表
’删除
OraDynaset.DbDelete
'修改
OraDynaset.DbEdit
OraDynaset.Fields("ID").value="101210"
OraDynaset.DbUpdate
'把图片的声明放在外面,类里面过程外面,而且要用As,不然等会儿没法用
Dim myPicture As New System.Windows.Forms.PictureBox()
'动态生成的控件,加一行
Private Sub UserControl1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Me.Panel3.Controls.Add(myPicture)
myPicture.Size =New System.Drawing.Size(115, 160)
myPicture.TabStop =False
myPicture.Name ="p"
myPicture.Cursor = Cursors.Hand
AddHandler myPicture.Click, AddressOf mypic_Click '添加事件
AddHandler returnid, AddressOf idReturned '绑定事件和过程
End Sub
'再来个事件,放在声明的地方,就是类里面、过程外面
Private Event returnid(ByVal id As String) '这个事件可以传递值哦~
'普通的单击事件
Private Sub mypic_Click()
RaiseEvent returnid(myPicture.Name)
End Sub
'会传值的事件过程
Private Sub idReturned(ByVal id As String)
Dim bookInfo As New BookInfo
bookInfo.Show()
Me.Parent.Enabled = False
'用id变量做点什么吧
End Sub
如果要改事件的签名(参数一类的)的话,得重写控件。你可以这样自己弄一个事件。
NET 本身提供了强大的脚本引擎,可以直接使用.NET CLR的任何编程语言作为脚本语言,如VB.NET、C#、JScript, J#等等。使用脚本引擎,我们可以动态生成任意表达式、或动态导入任意脚本文件,并在任意时候执行。
经实践发现,我们可以使用至少两种不同的方式在.NET中使用脚本引擎:VsaEngine和CodeDom。
其实,CodeDom不能算是真正的脚本引擎,它实际上是编译器。但是我们完全可以利用CodeDom来模拟脚本引擎。
使用Emit方法也能达到动态生成可执行代码的目的,而且Emit生成的代码不需要编译,因此速度更快。但是Emit插入的实际上是汇编代码,不能算是脚本语言。
本文介绍如何以CodeDom方式来动态生成可执行代码。
如何在.NET中实现脚本引擎 (CodeDom篇) 沐枫网志
1. 构造一个编译器
设置编译参数
编译参数需要在CompilerParameters设置:
CompilerOptions 用于设置编译器命令行参数
IncludeDebugInformation 用于指示是否在内存在生成Assembly
GenerateInMemory 用于指示是否在内存在生成Assembly
GenerateExecutable 用于指示生成的Assembly类型是exe还是dll
OutputAssembly 用于指示生成的程序文件名(仅在GenerateInMemory为false的情况)
ReferencedAssemblies 用于添加引用Assembly
例如:
theParameters.ReferencedAssemblies.Add("System.dll");
创建指定语言的编译器
编译需要由指定语言的CodeDomProvider生成。
这里列举一些.NET的CodeDomProvider:
vb.net Microsoft.VisualBasic.VBCodeProvider
C# Microsoft.CSharp.CSharpCodeProvider
jscript Microsoft.JScript.JScriptCodeProvider
J# Microsoft.VJSharp.VJSharpCodeProvider
以C#为例,要创建C#编译器,代码如下:
//.NET 1.1/1.0
ICodeCompiler compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler();
//.NET 2.0
ICodeCompiler compiler = (ICodeCompiler) new Microsoft.CSharp.CSharpCodeProvider();
下面是完整的创建编译器的例子:
/// summary
/// 创建相应脚本语言的编译器
/// /summary
private void createCompiler(string strLanguage, bool debugMode, string strAssemblyFileName)
{
this.theParameters = new CompilerParameters();
this.theParameters.OutputAssembly = System.IO.Path.Combine(System.IO.Path.GetTempPath(), strAssemblyFileName + ".dll");
this.theParameters.GenerateExecutable = false;
this.theParameters.GenerateInMemory = true;
if(debugMode)
{
this.theParameters.IncludeDebugInformation = true;
this.theParameters.CompilerOptions += "/define:TRACE=1 /define:DEBUG=1 ";
}
else
{
this.theParameters.IncludeDebugInformation = false;
this.theParameters.CompilerOptions += "/define:TRACE=1 ";
}
AddReference("System.dll");
AddReference("System.Data.dll");
AddReference("System.Xml.dll");
strLanguage = strLanguage.ToLower();
CodeDomProvider theProvider;
if("visualbasic" == strLanguage || "vb" == strLanguage)
{
theProvider = new Microsoft.VisualBasic.VBCodeProvider();
if(debugMode)
theParameters.CompilerOptions += "/debug:full /optimize- /optionexplicit+ /optionstrict+ /optioncompare:text /imports:Microsoft.VisualBasic,System,System.Collections,System.Diagnostics ";
else
theParameters.CompilerOptions += "/optimize /optionexplicit+ /optionstrict+ /optioncompare:text /imports:Microsoft.VisualBasic,System,System.Collections,System.Diagnostics ";
AddReference("Microsoft.VisualBasic.dll");
}
else if("jscript" == strLanguage || "js" == strLanguage)
{
theProvider = new Microsoft.JScript.JScriptCodeProvider();
AddReference("Microsoft.JScript.dll");
}
else if("csharp" == strLanguage || "cs" == strLanguage || "c#" == strLanguage)
{
theProvider = new Microsoft.CSharp.CSharpCodeProvider();
if(!debugMode)
theParameters.CompilerOptions += "/optimize ";
}
// else if("jsharp" == strLanguage || "vj" == strLanguage || "j#" == strLanguage)
// {
// theProvider = new Microsoft.VJSharp.VJSharpCodeProvider();
// if(!debugMode)
// theParameters.CompilerOptions += "/optimize ";
// }
else
throw new System.Exception("指定的脚本语言不被支持。");
this.theCompiler = theProvider.CreateCompiler();
}
/// summary
/// 添加引用对象。
/// /summary
/// param name="__strAssemblyName"引用的文件名/param
public void AddReference(string __strAssemblyName)
{
theParameters.ReferencedAssemblies.Add(__strAssemblyName);
}
注:
在.NET Framework 2.0中,由于CreateCompiler方法被标记作废。为避免产生编译警告,可直接返回CodeDomProvider作为编译器:
this.theCompiler = (ICodeCompiler)theProvider;
2. 编译源代码
编译源代码相当简单,只需一条语句就搞定了:
CompilerResults compilerResults = compiler.CompileAssemblyFromSource(this.theParameters, this.SourceText);
执行后,可以从compilerResults取得以下内容:
NativeCompilerReturnValue 编译结果,用于检查是否成功
Errors 编译时产生的错误和警告信息
CompiledAssembly 如果编译成功,则返回编译生成的Assembly
示例函数:
/// summary
/// 编译脚本。编译前将清空以前的编译信息。
/// CompilerInfo将包含编译时产生的错误信息。
/// /summary
/// returns成功时返回True。不成功为False。/returns
public bool Compile()
{
this.theCompilerInfo = "";
this.isCompiled = false;
this.theCompiledAssembly = null;
this.theCompilerResults = this.theCompiler.CompileAssemblyFromSource(this.theParameters, this.SourceText);
if(this.theCompilerResults.NativeCompilerReturnValue == 0)
{
this.isCompiled = true;
this.theCompiledAssembly = this.theCompilerResults.CompiledAssembly;
}
System.Text.StringBuilder compilerInfo = new System.Text.StringBuilder();
foreach(CompilerError err in this.theCompilerResults.Errors)
{
compilerInfo.Append(err.ToString());
compilerInfo.Append("/r/n");
}
theCompilerInfo = compilerInfo.ToString();
return isCompiled;
}
3. 执行代码
使用Reflection机制就可以很方便的执行Assembly中的代码。
我们假设编译时使用的脚本代码 this.SourceText 内容如下:
namespace test
{
public class script
{
static public void Main()
{
MessageBox.Show("Hello");
}
}
}
则相应的执行代码为:
scriptEngine.Invoke("test.script", "Main", null);
Invoke函数内容:
/// summary
/// 执行指定的脚本函数(Method)。
/// 如果指定的类或模块名,以及函数(Method)、或参数不正确,将会产生VsaException/VshException例外。
/// /summary
/// param name="__strModule"类或模块名/param
/// param name="__strMethod"要执行的函数(Method)名字/param
/// param name="__Arguments"参数(数组)/param
/// returns返回执行的结果/returns
public object Invoke(string __strModule, string __strMethod, object[] __Arguments)
{
if(!this.IsCompiled || this.theCompiledAssembly == null)
throw new System.Exception("脚本还没有成功编译");
Type __ModuleType = this.theCompiledAssembly.GetType(__strModule);
if(null == __ModuleType)
throw new System.Exception(string.Format("指定的类或模块 ({0}) 未定义。", __strModule));
MethodInfo __MethodInfo = __ModuleType.GetMethod(__strMethod);
if(null == __MethodInfo)
throw new System.Exception(string.Format("指定的方法 ({0}::{1}) 未定义。", __strModule, __strMethod));
try
{
return __MethodInfo.Invoke(null, __Arguments);
}
catch( TargetParameterCountException )
{
throw new System.Exception(string.Format("指定的方法 ({0}:{1}) 参数错误。", __strModule, __strMethod));
}
catch(System.Exception e)
{
System.Diagnostics.Trace.WriteLine(string.Format("执行({0}:{1})错误: {2}", __strModule, __strMethod, e.ToString()));
return null;
}
}
总结:
CodeDom可以很方便的随时编译源代码,并动态执行。虽然作为脚本引擎,它没有VsaEngine正规和方便,但作为一般应用,也够用了。并且结合Reflection机制,它的功能比VsaEngine更强大:它可以编译任何提供CompilerProvider的CLR语言(目前.NET自带的语言中都有)。
当然,它也有一些缺点:它生成的Assembly不能动态卸载。这在一般情况下不成问题,因为一个源代码只需编译一次,并载入执行,并不需要动态卸载。
假如你需要做脚本编辑器时,就要考虑这个问题,因为有可能一个脚本会因为修修改改而不停的重新编译,从而造成不停的产生新的Assembly,最后将导致内存被大量占用。要解决这个问题,需要将编译器加载到独立的AppDomain中,通过卸载AppDomain达到卸载所需的Assembly的目的。