一个动态编译并运行C#或VB.NET代码的工具[翻译]


原文地址
  源码下载

简介
这个程序在内存中动态编译代码片段并且运行它,所以你在测试C#或VB.NET代码时不必再创建新的工程或解决方案了。

背景
通常我们需要测试一些代码片段(C#或VB.NET),在.NET环境中,我们用"CSC"或"VBC"命令;在VS.NET中,我们创建新的工程或解决方案,并且创建很多其他文件。这两种方法都是沉闷乏味的,特别是当我们测试一个很简单的程序时。jconwell创作了一种方法:"Dot Net Script",他使用XML文件(命名为"dnml")来包涵C#或VB代码。在这种方法里,dnml文件被解析并且代码在内存中被编译,这是个好方法!但是,它仍然有些小麻烦,因为它需要创建一个新文件。现在我们来做一个基于他的工作之上的工具。这个工具具有以下特点:
1、可以加载C#或VB代码文件,在内存中编译代码,然后通过一个静态方法("入口点")来运行这些代码。
2、若为VB.NET代码,并且它是一个"Form"类,工具将会添加一个"Main()"方法来运行这个窗体。
3、通过一个新的线程来运行代码。
4、有一个可视化的界面(如下面的截图)。

使用这个工具
像下面截图那样,点击按钮"Open..."可以打开一个C#或VB.NET文件;点击按钮"Paste"可以从剪贴板中粘贴代码到文本区;复选框"C#"用来标记代码是不是C#语言;那个文本框用来输入入口点(在C#中为静态方法,在VB.NET中为公共的Sub或Function);点击按钮"Run!"将会编译并运行代码。
DynamicCompileAndRun.gif

工作原理
这个程序的主要的类是CompileEngine,它有个构造函数:

1 None.gif public  CompileEngine( string  code, anguageType language,  string  entry)
2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
3InBlock.gif   this.SourceCode = code;
4InBlock.gif   this.Language = language;
5InBlock.gif   this.EntryPoint = entry;
6ExpandedBlockEnd.gif}

Run是一个很重要的方法,它包含三个工作步骤:PrepareRealSourceCode,CreateAssembly和CallEntry。

 1 None.gif public   void  Run()
 2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 3InBlock.gif   ClearErrMsgs();
 4InBlock.gif
 5InBlock.gif   string strRealSourceCode = PrepareRealSourceCode();
 6InBlock.gif
 7InBlock.gif   Assembly assembly = CreateAssembly( strRealSourceCode );
 8InBlock.gif
 9InBlock.gif   CallEntry( assembly, EntryPoint );
10InBlock.gif
11InBlock.gif   DisplayErrorMsg();
12InBlock.gif
13ExpandedBlockEnd.gif}

PrepareRealSourceCode有两个功能:添加引用声明和为VB.NET窗体添加"Sub Main"。

 1 None.gif private   string  PrepareRealSourceCode()
 2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 3InBlock.gif
 4InBlock.gif    string strRealSourceCode = "";
 5InBlock.gif
 6InBlock.gif    // add some using(Imports) statements
 7InBlock.gif    string strUsingStatement = "";
 8InBlock.gif
 9InBlock.gif    if( Language == LanguageType.VB )
10ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
11ExpandedSubBlockStart.gifContractedSubBlock.gif        string [] basicImportsStatement = dot.gif{
12InBlock.gif                                "Imports Microsoft.VisualBasic",
13InBlock.gif                                "Imports System",
14InBlock.gif                                "Imports System.Windows.Forms",
15InBlock.gif                                "Imports System.Drawing",
16InBlock.gif                                "Imports System.Data",
17InBlock.gif                                "Imports System.Threading",
18InBlock.gif                                "Imports System.Xml",
19InBlock.gif                                "Imports System.Collections",
20InBlock.gif                                "Imports System.Diagnostics",      
21ExpandedSubBlockEnd.gif        }
;
22InBlock.gif        foreachstring si in basicImportsStatement )
23ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
24InBlock.gif            if( SourceCode.IndexOf( si ) < 0 )
25InBlock.gif                strUsingStatement += si + "\r\n";
26ExpandedSubBlockEnd.gif        }

27ExpandedSubBlockEnd.gif    }

28InBlock.gif
29InBlock.gif    strRealSourceCode = strUsingStatement + SourceCode;
30InBlock.gif
31InBlock.gif    // for VB Prog, Add Main(), So We Can Run It
32InBlock.gif    if( Language == LanguageType.VB && EntryPoint == "Main" &&
33InBlock.gif        strRealSourceCode.IndexOf( "Sub Main("< 0 ) 
34ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
35InBlock.gif        try
36ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
37InBlock.gif            int posClass = strRealSourceCode.IndexOf( "Public Class ")+ 
38InBlock.gif                "Public Class ".Length;
39InBlock.gif            int posClassEnd = strRealSourceCode.IndexOf( "\r\n", posClass );
40InBlock.gif            string className = strRealSourceCode.Substring( posClass, 
41InBlock.gif                posClassEnd - posClass );
42InBlock.gif            int pos = strRealSourceCode.LastIndexOf( "End Class");
43InBlock.gif            if( pos > 0 )
44InBlock.gif                strRealSourceCode = strRealSourceCode.Substring( 0, pos ) + @"
45InBlock.gifPrivate Shared Sub Main()
46InBlock.gif" + "Dim frm As New " + className + "()" + @"
47InBlock.gif        If TypeOf frm Is Form Then frm.ShowDialog()
48InBlock.gifEnd Sub
49InBlock.gif" + strRealSourceCode.Substring( pos );
50ExpandedSubBlockEnd.gif        }

51ExpandedSubBlockStart.gifContractedSubBlock.gif        catchdot.gif{}
52ExpandedSubBlockEnd.gif    }

53InBlock.gif
54InBlock.gif    return strRealSourceCode;
55ExpandedBlockEnd.gif}

CreateAssembly在内存中编译代码并生成汇编集。

 1 None.gif //  compile the source, and create assembly in memory
 2 None.gif //  this method code is mainly from jconwell, 
 3 None.gif //  see  http://www.codeproject.com/dotnet/DotNetScript.asp
 4 None.gif private  Assembly CreateAssembly( string  strRealSourceCode)
 5 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 6InBlock.gif    //Create an instance whichever code provider that is needed
 7InBlock.gif    CodeDomProvider codeProvider = null;
 8InBlock.gif    if (Language == LanguageType.CSharp )
 9InBlock.gif        codeProvider = new CSharpCodeProvider();
10InBlock.gif    else if( Language == LanguageType.VB )
11InBlock.gif        codeProvider = new VBCodeProvider();
12InBlock.gif
13InBlock.gif    //create the language specific code compiler
14InBlock.gif    ICodeCompiler compiler = codeProvider.CreateCompiler();
15InBlock.gif
16InBlock.gif    //add compiler parameters
17InBlock.gif    CompilerParameters compilerParams = new CompilerParameters();
18InBlock.gif    compilerParams.CompilerOptions = "/target:library"
19InBlock.gif    // you can add /optimize
20InBlock.gif    compilerParams.GenerateExecutable = false;
21InBlock.gif    compilerParams.GenerateInMemory = true;            
22InBlock.gif    compilerParams.IncludeDebugInformation = false;
23InBlock.gif
24InBlock.gif    // add some basic references
25InBlock.gif    compilerParams.ReferencedAssemblies.Add( "mscorlib.dll");
26InBlock.gif    compilerParams.ReferencedAssemblies.Add( "System.dll");            
27InBlock.gif    compilerParams.ReferencedAssemblies.Add( "System.Data.dll" );
28InBlock.gif    compilerParams.ReferencedAssemblies.Add( "System.Drawing.dll" );
29InBlock.gif    compilerParams.ReferencedAssemblies.Add( "System.Xml.dll" );
30InBlock.gif    compilerParams.ReferencedAssemblies.Add( 
31InBlock.gif        "System.Windows.Forms.dll" );
32InBlock.gif
33InBlock.gif    if( Language == LanguageType.VB )
34ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
35InBlock.gif        compilerParams.ReferencedAssemblies.Add( 
36InBlock.gif            "Microsoft.VisualBasic.dll" );
37ExpandedSubBlockEnd.gif    }

38InBlock.gif
39InBlock.gif    //actually compile the code
40InBlock.gif    CompilerResults results = compiler.CompileAssemblyFromSource(
41InBlock.gif        compilerParams, 
42InBlock.gif        strRealSourceCode );
43InBlock.gif
44InBlock.gif    //get a hold of the actual assembly that was generated
45InBlock.gif    Assembly generatedAssembly = results.CompiledAssembly;
46InBlock.gif
47InBlock.gif    //return the assembly
48InBlock.gif    return generatedAssembly;
49ExpandedBlockEnd.gif}

CallEntry(Assembly...)用来通过映射调用入口点。

 1 None.gif //  invoke the entry method
 2 None.gif //  this method code is mainly from jconwell, 
 3 None.gif //  see  http://www.codeproject.com/dotnet/DotNetScript.asp
 4 None.gif private   void  CallEntry(Assembly assembly,  string  entryPoint)
 5 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 6InBlock.gif    try
 7ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 8InBlock.gif        //Use reflection to call the static Main function
 9InBlock.gif        Module[] mods = assembly.GetModules(false);
10InBlock.gif        Type[] types = mods[0].GetTypes();
11InBlock.gif
12InBlock.gif        foreach (Type type in types)
13ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
14InBlock.gif            MethodInfo mi = type.GetMethod(entryPoint, 
15InBlock.gif                BindingFlags.Public | BindingFlags.NonPublic 
16InBlock.gif
17InBlock.gif                | BindingFlags.Static);  
18InBlock.gif            if (mi != null)
19ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
20InBlock.gif                if( mi.GetParameters().Length == 1 )  
21ExpandedSubBlockStart.gifContractedSubBlock.gif                dot.gif{
22InBlock.gif                    if
23InBlock.gif
24InBlock.gif                        mi.GetParameters()[0].ParameterType.IsArray )
25ExpandedSubBlockStart.gifContractedSubBlock.gif                    dot.gif{
26InBlock.gif                        string [] par = new string[1]; // if 
27InBlock.gif
28InBlock.gif                        Main has string [] arguments
29InBlock.gif                                    mi.Invoke(null, par);
30ExpandedSubBlockEnd.gif                    }

31ExpandedSubBlockEnd.gif                }

32InBlock.gif                else
33ExpandedSubBlockStart.gifContractedSubBlock.gif                dot.gif{
34InBlock.gif                    mi.Invoke(nullnull);
35ExpandedSubBlockEnd.gif                }

36InBlock.gif                return;
37ExpandedSubBlockEnd.gif            }

38ExpandedSubBlockEnd.gif        }

39ExpandedSubBlockEnd.gif    }

40InBlock.gif    catch (Exception ex)
41ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
42ExpandedSubBlockEnd.gif    }

43ExpandedBlockEnd.gif}

在FormMain里创建一个新线程来创建一个CompileEngine实例然后调用Run。

None.gif private   void  btnRun_Click( object  sender, System.EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
// we use a new thread to run it
InBlock.gif
    new Thread( new ThreadStart( RunTheProg ) ).Start();
ExpandedBlockEnd.gif}

None.gif
None.gif
private   void  RunTheProg()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
string code = txtSource.Text.Trim();
InBlock.gif    LanguageType language 
= chkIsCSharp.Checked ? 
InBlock.gif        LanguageType.CSharp : LanguageType.VB;
InBlock.gif    
string entry = txtEntry.Text.Trim();
InBlock.gif
InBlock.gif    
if( code == "" ) return;
InBlock.gif    
if( entry == "" ) entry = "Main";
InBlock.gif
InBlock.gif    CompileEngine engine 
= new CompileEngine( code, language, entry );
InBlock.gif
InBlock.gif    engine.Run();
ExpandedBlockEnd.gif}

一些可以改进的地方
在PrepareRealSourceCode,用正则表达式来查找类名会更好一些。

感谢
感谢jconwell,VictorV,George Orwell,Eric Astor和其他朋友,他们在他们的主题里展示了很多优秀的作品,比如Dot Net Script,SnippetCompiler,Runtime Compilation。

关于dstang 2000
一个专业的开发人员。他使用Java,C#,VB,C和一些其他语言。

转载于:https://www.cnblogs.com/srw962/archive/2005/10/08/250557.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值