C#.NET4.0动态脚本解析大全(收集版+原创)

方法一)     

第一类:CSharpCode,例如用CODEDOM模型的那种,不过不能在Silverlight下使用。

 C#.NET4.0提供一种ScriptRuntime的机制,直接可以运行RUBY和PYTHON的开源iron版本,使用比较简单。

直接引用单元并且设置变量,EXECUTE即可,最后通过字符串变量获取即可。  详细参考《c#高级编程》

var engine = Python.CreateEngine();   
4.           var scope = engine.CreateScope();   
5.           var source = engine.CreateScriptSourceFromString(   
6.               "def adder(arg1, arg2):\n" +   
7.               "   return arg1 + arg2\n" +   
8.               "\n" +   
9.               "def fun(arg1):\n" +   
10.               "   tel = {'jack': 4098, 'sape': 4139}\n" +   
11.               "   for k, v in arg1.iteritems():\n"+   
12.               "      tel[k]=v\n"+   
13.               "   return tel\n" +   
14.               "\n" +   
15.               "class MyClass(object):\n" +   
16.               "   def __init__(self, value):\n" +   
17.               "       self.value = value\n");   
18.           source.Execute(scope);   
19.   
20.           var adder = scope.GetVariable<Func<object, object, object>>("adder");   
21.           Console.WriteLine(adder(2, 2));   
22.   
23.           var fun = scope.GetVariable<Func<object, object>>("fun");   
24.           IronPython.Runtime.PythonDictionary inputDict = new IronPython.Runtime.PythonDictionary();   
25.           inputDict["abc"] = "abc";   
26.           inputDict["def"] = 456;   
27.           object res = fun(inputDict);   
28.           IronPython.Runtime.PythonDictionary outputDict = res as IronPython.Runtime.PythonDictionary;   
29.           foreach (var k in outputDict.Keys)   
30.           {   
31.               Console.WriteLine("key:"+ k.ToString()+" val:  " + outputDict[k].ToString());   
32.           }   
33.   
34.           var myClass = scope.GetVariable<Func<object, object>>("MyClass");   
35.           var myInstance = myClass("hello");   
36.   
37.           Console.WriteLine(engine.Operations.GetMember(myInstance, "value"));   
38.       }  
本篇文章来源于 Linux公社网站(http://www.linuxidc.com/)  原文链接:http://www.linuxidc.com/Linux/2011-09/42071.htm

 思路:

    在IL中可以找到Microsoft.CSharp.RuntimeBuilder,不知道是不是可以直接使用。还有System.Runtime.CompilerService,等是否可以直接解析语法,生成执行脚本,还有一种CodeDOM编码模型的东西,网上例子很多。现在摘录如下:

1)

 方法二)

本文版权归博客园Allan.所有!此处纯粹收藏,如有再转,请按如下方式显式标明文章作者及出处,以示尊重!!!

作者:Allan.

原文题目:.NET中的动态编译

原文出处:http://www.cnblogs.com/zlgcool/archive/2008/10/12/1309616.html

 

代码的动态编译并执行是一个.NET平台提供给我们的很强大的工具用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码来扩展我们已有 的应用程序。这在很大程度上给我们提供了另外一种扩展的方式(当然这并不能算是严格意义上的扩展,但至少为我们提供了一种思路)。

 

动态代码执行可以应用在诸如模板生成,外加逻辑扩展等一些场合。一个简单的例子,为了网站那的响应速度,HTML静态页面往往是我们最好的选择,但基于数据驱动的网站往往又很难用静态页面实现,那么将动态页面生成html的工作或许就是一个很好的应用场合。另外,对于一些模板的套用,我们同样可以用它来做。另外这本身也是插件编写的方式。

 

最基本的动态编译

 

.Net为我们提供了很强大的支持来实现这一切我们可以去做的基础,主要应用的两个命名空间是:System.CodeDom.Compiler和Microsoft.CSharp或Microsoft.VisualBasic。另外还需要用到反射来动态执行你的代码。动态编译并执行代码的原理其实在于将提供的源代码交予CSharpCodeProvider来执行编译(其实和CSC没什么两样),如果没有任何编译错误,生成的IL代码会被编译成DLL存放于于内存并加载在某个应用程序域(默认为当前)内并通过反射的方式来调用其某个方法或者触发某个事件等。之所以说它是插件编写的一种方式也正是因为与此,我们可以通过预先定义好的借口来组织和扩展我们的程序并将其交还给主程序去触发。一个基本的动态编译并执行代码的步骤包括:

·         将要被编译和执行的代码读入并以字符串方式保存

·         声明CSharpCodeProvider对象实例

·         调用CSharpCodeProvider实例的CompileAssemblyFromSource方法编译

·         用反射生成被生成对象的实例(Assembly.CreateInstance)

·         调用其方法

以下代码片段包含了完整的编译和执行过程:

//get the code to compile

string strSourceCode = this.txtSource.Text;

 

// 1.Create a new CSharpCodePrivoder instance

CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();

 

// 2.Sets the runtime compiling parameters by crating a new CompilerParameters instance

CompilerParameters objCompilerParameters = new CompilerParameters();

objCompilerParameters.ReferencedAssemblies.Add("System.dll");

objCompilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");

objCompilerParameters.GenerateInMemory = true;

 

// 3.CompilerResults: Complile the code snippet by calling a method from the provider

CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(objCompilerParameters, strSourceCode);

 

if (cr.Errors.HasErrors)

{

    string strErrorMsg = cr.Errors.Count.ToString() + " Errors:";

 

    for (int x = 0; x < cr.Errors.Count; x++)

    {

        strErrorMsg = strErrorMsg + "/r/nLine: " +

                     cr.Errors[x].Line.ToString() + " - " +

                     cr.Errors[x].ErrorText;

    }

 

    this.txtResult.Text = strErrorMsg;

    MessageBox.Show("There were build erros, please modify your code.", "Compiling Error");

 

    return;

}

 

// 4. Invoke the method by using Reflection

Assembly objAssembly = cr.CompiledAssembly;

object objClass = objAssembly.CreateInstance("Dynamicly.HelloWorld");

if (objClass == null)

{

    this.txtResult.Text = "Error: " + "Couldn't load class.";

    return;

}

 

object[] objCodeParms = new object[1];

objCodeParms[0] = "Allan.";

 

string strResult = (string)objClass.GetType().InvokeMember(

           "GetTime", BindingFlags.InvokeMethod, null, objClass, objCodeParms);

 

this.txtResult.Text = strResult;

需要解释的是,这里我们在传递编译参数时设置了GenerateInMemory为true,这表明生成的DLL会被加载在内存中(随后被默认引用入当前应用程序域)。在调用GetTime方法时我们需要加入参数,传递object类型的数组并通过Reflection的InvokeMember来调用。在创建生成的Assembly中的对象实例时,需要注意用到的命名空间是你输入代码的真实命名空间。以下是我们输入的测试代码(为了方便,所有的代码都在外部输入,动态执行时不做调整):

using System;

 

namespace Dynamicly

{

    public class HelloWorld

    {

        public string GetTime(string strName)

        {

            return  "Welcome " + strName + ", Check in at " + System.DateTime.Now.ToString();

        }

    }

}

运行附件中提供的程序,可以很容易得到一下结果:

 

改进的执行过程

 

现在一切看起来很好,我们可以编译代码并把代码加载到当前应用程序域中来参与我们的活动,但你是否想过去卸载掉这段程序呢?更好的去控制程序呢?另外,当你运行这个程序很多遍的时候,你会发现占用内存很大,而且每次执行都会增大内存使用。是否需要来解决这个问题呢?当然需要,否则你会发现这个东西根本没用,我需要执行的一些大的应用会让我的服务器crzay,不堪重负而疯掉的。

要解决这个问题我们需要来了解一下应用程序域。.NET Application Domain是.NET提供的运行和承载一个活动的进程(Process)的容器,它将这个进程运行所需的代码和数据,隔离到一个小的范围内,称为Application Domain。当一个应用程序运行时,Application Domains将所有的程序集/组件集加载到当前的应用程序域中,并根据需要来调用。而对于动态生成的代码/程序集,我们看起来好像并没有办法去管理它。其实不然,我们可以用Application Domain提供的管理程序集的办法来动态加载和移除Assemblies来达到我们的提高性能的目的。具体怎么做呢,在前边的基础上增加以下步骤:

·         创建另外一个Application Domain

·         动态创建(编译)代码并保存到磁盘

·         创建一个公共的远程调用接口

·         创建远程调用接口的实例。并通过这个接口来访问其方法。

换句话来讲就是将对象加载到另外一个AppDomain中并通过远程调用的方法来调用。所谓远程调用其实也就是跨应用程序域调用,所以这个对象(动态代码)必须继承于MarshalByRefObject类。为了复用,这个接口被单独提到一个工程中,并提供一个工厂来简化每次的调用操作:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Reflection;

 

namespace RemoteAccess

{

    /// <summary>

          /// Interface that can be run over the remote AppDomain boundary.

          /// </summary>

          public interface IRemoteInterface

          {

                   object Invoke(string lcMethod,object[] Parameters);

          }

 

          /// <summary>

          /// Factory class to create objects exposing IRemoteInterface

          /// </summary>

          public class RemoteLoaderFactory : MarshalByRefObject

          {

                   private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;

 

                   public RemoteLoaderFactory() {}

 

                   public IRemoteInterface Create( string assemblyFile, string typeName, object[] constructArgs )

                   {

                             return (IRemoteInterface) Activator.CreateInstanceFrom(

                                      assemblyFile, typeName, false, bfi, null, constructArgs,

                                      null, null, null ).Unwrap();

                   }

          }       

}

 

接下来在原来基础上需要修改的是:

·         将编译成的DLL保存到磁盘中。

·         创建另外的AppDomain。

·         获得IRemoteInterface接口的引用。(将生成的DLL加载到额外的AppDomain)

·         调用InvokeMethod方法来远程调用。

·         可以通过AppDomain.Unload()方法卸载程序集。

以下是完整的代码,演示了如何应用这一方案。

//get the code to compile

string strSourceCode = this.txtSource.Text;

 

//1. Create an addtional AppDomain

AppDomainSetup objSetup = new AppDomainSetup();

objSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;

AppDomain objAppDomain = AppDomain.CreateDomain("MyAppDomain", null, objSetup);

 

// 1.Create a new CSharpCodePrivoder instance

CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();

 

// 2.Sets the runtime compiling parameters by crating a new CompilerParameters instance

CompilerParameters objCompilerParameters = new CompilerParameters();

objCompilerParameters.ReferencedAssemblies.Add("System.dll");

objCompilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");

 

// Load the remote loader interface

objCompilerParameters.ReferencedAssemblies.Add("RemoteAccess.dll");

 

// Load the resulting assembly into memory

objCompilerParameters.GenerateInMemory = false;

objCompilerParameters.OutputAssembly = "DynamicalCode.dll";

 

// 3.CompilerResults: Complile the code snippet by calling a method from the provider

CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(objCompilerParameters, strSourceCode);

 

if (cr.Errors.HasErrors)

{

    string strErrorMsg = cr.Errors.Count.ToString() + " Errors:";

 

    for (int x = 0; x < cr.Errors.Count; x++)

    {

        strErrorMsg = strErrorMsg + "/r/nLine: " +

                     cr.Errors[x].Line.ToString() + " - " +

                     cr.Errors[x].ErrorText;

    }

 

    this.txtResult.Text = strErrorMsg;

    MessageBox.Show("There were build erros, please modify your code.", "Compiling Error");

 

    return;

}

 

// 4. Invoke the method by using Reflection

RemoteLoaderFactory factory = (RemoteLoaderFactory)objAppDomain.CreateInstance("RemoteAccess","RemoteAccess.RemoteLoaderFactory").Unwrap();

 

// with help of factory, create a real 'LiveClass' instance

object objObject = factory.Create("DynamicalCode.dll", "Dynamicly.HelloWorld", null);

 

if (objObject == null)

{

    this.txtResult.Text = "Error: " + "Couldn't load class.";

    return;

}

 

// *** Cast object to remote interface, avoid loading type info

IRemoteInterface objRemote = (IRemoteInterface)objObject;

 

object[] objCodeParms = new object[1];

objCodeParms[0] = "Allan.";

 

string strResult = (string)objRemote.Invoke("GetTime", objCodeParms);

 

this.txtResult.Text = strResult;

 

//Dispose the objects and unload the generated DLLs.

objRemote = null;

AppDomain.Unload(objAppDomain);

 

System.IO.File.Delete("DynamicalCode.dll");

 

对于客户端的输入程序,我们需要继承于MarshalByRefObject类和IRemoteInterface接口,并添加对RemoteAccess程序集的引用。以下为输入:

using System;

using System.Reflection;

using RemoteAccess;

 

namespace Dynamicly

{

    public class HelloWorld : MarshalByRefObject,IRemoteInterface

    {

        public object Invoke(string strMethod,object[] Parameters)

        {

            return this.GetType().InvokeMember(strMethod, BindingFlags.InvokeMethod,null,this,Parameters);

        }

 

        public string GetTime(string strName)

        {

            return  "Welcome " + strName + ", Check in at " + System.DateTime.Now.ToString();

        }

    }

}

 

这样,你可以通过适时的编译,加载和卸载程序集来保证你的程序始终处于一个可控消耗的过程,并且达到了动态编译的目的,而且因为在不同的应用程序域中,让你的本身的程序更加安全和健壮。示例代码下载:http://files.cnblogs.com/zlgcool/DynamicCompiler.rar

 

2)方法三:

个控制台例子, 演示了如何用 C# 动态的生成代码, 编译代码, 最后执行
复制代码 代码如下:
/* 
* 使用 C# 动态编译代码和执行 
* 作者: yaob 
*/

static void Main(string[] args) 

// 编译器 
CodeDomProvider cdp = CodeDomProvider.CreateProvider("C#");

// 编译器的参数 
CompilerParameters cp = new CompilerParameters(); 
cp.ReferencedAssemblies.Add("System.dll"); 
cp.GenerateExecutable = false; 
cp.GenerateInMemory = true;

// 编译结果 
CompilerResults cr = cdp.CompileAssemblyFromSource(cp, HelloWorld());

if (cr.Errors.HasErrors) Console.WriteLine("编译出错!"); 
else 

// 编译后的程序集 
Assembly ass = cr.CompiledAssembly;

// 得到HelloWorld类中的SayHello方法 
Type type = ass.GetType("HelloWorld.HelloWorld"); 
MethodInfo mi = type.GetMethod("SayHello");

// 执行 
mi.Invoke(null, null); 

}

// 动态构建的代码 
static string HelloWorld() 

StringBuilder sbCode = new StringBuilder(); 
sbCode.AppendLine("using System;"); 
sbCode.AppendLine("namespace HelloWorld"); 
sbCode.AppendLine("{"); 
sbCode.AppendLine(" class HelloWorld"); 
sbCode.AppendLine(" {"); 
sbCode.AppendLine(" public static void SayHello()"); 
sbCode.AppendLine(" {"); 
sbCode.AppendLine(" Console.WriteLine(\"Hello~ World~!\");"); 
sbCode.AppendLine(" }"); 
sbCode.AppendLine(" }"); 
sbCode.AppendLine("}"); 
return sbCode.ToString(); 
}

详细出处参考:http://www.jb51.net/article/27144.htm

 

3)

所谓动态代码执行,和脚本引擎有点类似。就是程序执行期从字符串或者文本文件中读取一段 C# 代码,在内存中动态编译成程序集,并创建相关类型实例执行相关方法。

具体的实现可参考下面的代码。如需要生成程序集文件,可参考《使用CodeDom生成程序集》

using System;
using System.Reflection;
using System.Globalization;
using Microsoft.CSharp;
using System.CodeDom;
using System.CodeDom.Compiler;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            // 定义需要动态执行的 C# 代码字符串,当然也可从文本文件中读取。
            string code = @"
                using System;
                namespace MyNamespace
                {
                    public class MyClass
                    {
                        private string name;

                        public MyClass(string name)
                        {
                            this.name = name;
                        }

                        public void Test()
                        {
                            Console.WriteLine(""{0} - {1}"", name, DateTime.Now);
                        }
                    }
                }
            ";

            // 创建编译器对象
            CSharpCodeProvider p = new CSharpCodeProvider();
            ICodeCompiler cc = p.CreateCompiler();

            // 设置编译参数
            CompilerParameters options = new CompilerParameters();
            options.ReferencedAssemblies.Add("System.dll");
            options.GenerateInMemory = true;
            options.OutputAssembly = "MyTest";
            
            // 开始编译
            CodeSnippetCompileUnit cu = new CodeSnippetCompileUnit(code);
            CompilerResults cr = cc.CompileAssemblyFromDom(options, cu);

            // 执行动态程序集相关内容。
            Type t = cr.CompiledAssembly.GetType("MyNamespace.MyClass");
            object o = cr.CompiledAssembly.CreateInstance("MyNamespace.MyClass", false, BindingFlags.Default, 
                null, new object[] { "Tom" }, CultureInfo.CurrentCulture, null);
            t.InvokeMember("Test", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, 
                null, o, null);

        }
    }
}

C# 动态编译、动态执行、动态调试
发布:dxy 字体:[增加 减小] 类型:转载

前几天看到一篇关于.net动态编译的文章 .NET中的动态编译 ,很受启发。
在此基础上我做了一些封装,为使调用更加简单,并增加了对动态代码调试的支持,相同代码只编译一次的支持,代码改动自动重新编译,代码引用文件的自动加载和手工加载等功能。

如上图,我封装的类CSharpProvider很简单,下面说明一下一些公共成员的用法。

公共属性

AssemblyFileName:这个属性指定动态编译后生成的配件名称。

CompilerParameters:这个属性指定编译的参数

References:这个属性指定被编译代码中的引用。调用者只要调用References.Add("xxx.dll"),就可以加入自己的引用,对于System命名空间的所有引用,不需要手工加入,该类会自动加载。对于用户自己的组件,如果不手工指定引用文件,该类会自动根据名字空间名进行猜测。

SourceCodeFileEncoding:如果以文件形式编译,指定文件的编码类型。

公共方法

public bool Compile(string code)

输入代码字符串,并编译

public bool CompileFromFile(string sourceCodeFileName)

编译输入的代码文件

public object CreateInstance(string code, string typeFullName)

创建类的实例

如下面代码,可以输入 CreateInstance(code, "MyInterface.IHelloWorld"),也可以输入CreateInstance(code, "HelloWorld"),程序会根据

类型名称来自动找到符合条件的类并实例化。如果代码中有多个指定类型的类,将实例化第一个。 
复制代码 代码如下:
using System; 
using MyInterface;

[Serializable] 
public class HelloWorld : MarshalByRefObject, IHelloWorld 

public string Say() 

return "Hi"; 

}

这里需要特别指出的是由于用到了AppDomain的远程调用,所有的动态加载的代码必须继承自MarshallByRefObject 
如果仅仅声明为[Serializable] 虽然也可以执行,但主应用程序域会记录下子应用程序域的一个引用,这样导致子应用程序

域卸载后,依然无法完全释放内存,从而内存泄漏。所以这个很关键,一定要注意。

public object CreateInstanceFromFile(string fileName, string typeFullName)

从文件创建动态实例

下面再谈谈对动态代码的调试

动态创建的代码如果不能调试,就像一个黑盒子,对系统的可维护性有较大破坏。未来实现这个功能,我们需要做以下工作,

第一、编译时要生成调试信息,这个可以通过设置 CompilerParameters.IncludeDebugInformation = true;来实现

第二、我们必须告诉调试器源码对应的位置,对于从文件编译的情况,源码文件位置会被自动写入调试信息文件 *.pdb中,而对于从内存编译的情况,我还没有找到指定的方法,如果哪位朋友知道,还望赐教。所以目前如果要调试动态代码,必须从文件编译,也就是调用CompileFromFile,CreateInstanceFromFile。

第三、我们需要在代码中设置一个断点,这个可以在代码中加入 System.Diagnostics.Debugger.Break(); 来解决。

如下图所示,动态代码现在可以调试了。

 

 


应用程序域

为了避免内存泄漏,本程序封装了对应用程序域的使用,调用者基本不需要关心应用程序域的调用和卸载过程。本程序在

重新编译或者对象销毁时会自动卸载应用程序域,从而释放内存。由于做这个程序是在应用程序域上遇到了很多麻烦,所以

感觉还是有必要简单讲一下应用程序域。

 


如上图所示,应用程序与实际上有点像一个单独的进程,但这个进程是运行在当前进程里面的,当然这个比喻不够贴切。

对应用程序域的调用有点类似进程间采用 Remoting 方式的对象调用,也就是说默认应用程序域要调用其他应用程序域中的对象,

必须采用远程调用的方法,而不能直接调用,如果直接调用,默认应用程序域就会记录这个被调用的应用程序域的一个内存引用,

即使这个应用程序域执行了Unload 方法卸载后,内存依然无法释放,这也是我一开始操作应用程序域遇到的最大困扰。

另外所有暴露在两个应用程序域之间的类必须从MarshalByRefObject基础,这点非常重要,否则将导致内存无法释放。

本程序的一些缺陷

1、没有提供编译多文件的接口,其实要实现这个很简单,考虑到用于动态执行的代码脚本往往比较简单,所以偷懒没有做。

2、没有提供对动态代码中多个对象的枚举接口,以后再完善吧。
源码下载地址 http://xiazai.jb51.net/200905/yuanma/DynamiclyCompiler.zip


详细出处参考:http://www.jb51.net/article/18326.htm

 

方法四:)

     源码下载地址 http://xiazai.jb51.net/200905/yuanma/DynamiclyCompiler.zip


详细出处参考:http://www.jb51.net/article/18326.htm

方法四:)提供了CS脚本解析。

 http://www.codeproject.com/KB/cs/cs-script_for_cp.aspx

 

 

第二类:基于解析方式的EVAL,计算类,这里评估后,基本上仅能计算什么加减乘除的普通产品。能够修改到在Silverlight下使用不多

 

第三类:开源的比较成熟的包括S#语言,以及DLR,更重要的是可以在银光方式下使用。

 

S#脚本语言:“以上核心思想都差不多,收集如此,但是都不能运行在SILVERLIGHT平台,如果需要运行在SILVERLIGHT平台,,请使用S#脚本语言。

网址:

http://www.protsyk.com/scriptdotnet/wiki/index.php?title=Main_Page

 

开源的利用DLR,http://dlr.codeplex.com/

支持开源的IronRuby,IronPython,提供源代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值