`
leonardleonard
  • 浏览: 801364 次
社区版块
存档分类
最新评论

.NET下的动态代码编译探索

阅读更多
不能确定动态代码编译在什么地方是有意义的?一个普通情况就应该可以帮助阐明这个问题。假如你不得不从一个数据库中取出数据并将它放入另一个数据库。你应该只需使用一个SQL语句从源数据库中选取数据并插入目标数据库中,这只是小菜一碟,对不对?如果你正在拷贝生产数据以生成测试数据并需要改变数据以确保目标数据在以后开发中使用是安全的又将如何?你可能会构建一个数据传输系统(DTS)或某个其它传输机制,但是如果你这样做超过足够多的数据,这就会变成你每次为拷贝数据建立数据-擦除(data-scrubbing)机制而消耗时间。你可以写一个应用程序来加工并生成测试数据,但是每次你在一个不同的应用程序上用它时你都将不得不改动(应用程序)并创建新的算法。

  走进动态代码编译。胜于不停地写一些一次性的代码,你可以创建一个有特定内部运作机制的应用程序来传送数据并在传送时运用代码段来改变数据。该代码段将代理每个你需要在数据上要做的动作。它们将被作为原始文本被储存在一个数据库中或某个它们可以很容易被修改的其它位置。代码段将被编译并在执行时同时应用到数据。这将允许你获得一个完全是不同的代码段的数据库,使得你可以很容易地恢复、修改并应用它而不用每次都要改变你的应用程序的根本。

  这是个相当复杂的情况,但是它应该帮助你理解一些可能性。现在,让我们看看如何实现它。

  CodeCompileUnit(代码编译单元)

  为了动态编译一个类,从System.CodeDom命名空间的一个CodeCompileUnit开始。CodeCompileUnit包含一个程序图形。为了构建代码,你要创建一些支撑对象并将它们添加到CodeCompileUnit实例中去。这些对象:代表应该已经在你的代码中的,就像你准备在设计时要创建它的普通对象。

  . CodeNamespace―代表指定的命名空间

  . CodeTypeDeclaration―代表类型声明

  . CodeMemberMethod―代表一个方法

  一个HelloWorld例子

  你可以使用下面的示例代码来生成包含一个接收单个参数并返回一个值的SayHello方法的代码。scriptBody方法的参数值成为SayHello方法的实体(body)。你将你的代码包含在接收影响结果的参数的一个static(静态)类中以创建CodeCompileUnit。

public static CodeCompileUnit CreateExecutionClass(string typeNamespace,
string typeName,
string scriptBody)
{
 // 创建CodeCompileUnit以包含代码
 CodeCompileUnit ccu = new CodeCompileUnit(); // 分配需要的命名空间
 CodeNamespace cns = new CodeNamespace(typeNamespace);
 cns.Imports.Add(new CodeNamespaceImport("System"));
 ccu.Namespaces.Add(cns); // 创建新的类声明
 CodeTypeDeclaration parentClass = new CodeTypeDeclaration(typeName);
 cns.Types.Add(parentClass); // 创建获得一个参数并返回一个字符串的SayHello方法
 CodeMemberMethod method = new CodeMemberMethod();
 method.Name = "SayHello";
 method.Attributes = MemberAttributes.Public;
 CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression(typeof(string), "inputMessage");
 method.Parameters.Add(arg);
 method.ReturnType = new CodeTypeReference(typeof(string)); // 添加方法实体需要的代码
 CodeSnippetStatement methodBody =new CodeSnippetStatement(scriptBody);
 method.Statements.Add(methodBody);
 parentClass.Members.Add(method); return ccu;
}
CodeProvider(代码提供者)

  现在你已经创建了一个CodeCompileUnit包含你的代码段,使用它来生成被编译到你的动态程序集中去的全部源代码。下面的静态方法首先从前面的例子中调用方法并且同时使用CSharpCodeProvider生成全部代码:

public static string GenerateCode(string typeNamespace,
string typeName,
string scriptBody)
{
 // 调用我们前面的方法创建CodeCompileUnit
 CodeCompileUnit ccu = CreateExecutionClass(typeNamespace,
 typeName, scriptBody); CSharpCodeProvider provider = new CSharpCodeProvider();
 CodeGeneratorOptions options = new CodeGeneratorOptions();
 options.BlankLinesBetweenMembers = false;
 options.IndentString = "\t"; StringWriter sw = new StringWriter();
 try
 {
  provider.GenerateCodeFromCompileUnit(ccu, sw, options);
  sw.Flush();
 }
 finally
 {
  sw.Close();
 } return sw.GetStringBuilder().ToString();
}

  作为一个例子,用输入值:"CodeGuru.DynamicCode","ScriptType",和"return inputMessage;"调用GenerateCode方法得出以下输出:

//---------------------------------------------------------------
// <auto-generated>
// 该代码是由工具生成的。
// 运行时版本:2.0.50630.0
// 更改这个文件可能导致不正确的(程序)动作并且如果代码被再次生成时将会丢掉这些更改。
// </auto-generated>
//---------------------------------------------------------------
namespace CodeGuru.DynamicCode {
 using System;
 public class ScriptType {
  public virtual string SayHello(string inputMessage) {
   return inputMessage;
  }
 }
}
  在内存中编译

  最后一步是获得生成的源代码并将它编译到一个当前的程序集中去。对于这个例子,你是将这个例子装入内存而不是一个物理文件。通过特定的编程语言提供者执行当前编译动作,在这个例程中就是CSharpCodeProvider。你设定任何预定的编译选项并从源代码编译这个程序集。

  下面的示例代码从你已构建的代码中生成了一个程序集:

static Assembly CompileInMemory(string code)
{
 CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters options = new  CompilerParameters();
 options.IncludeDebugInformation = false;
 options.GenerateExecutable = false;
 options.GenerateInMemory = true;
 CompilerResults results =provider.CompileAssemblyFromSource(options, code);
 provider.Dispose();
 Assembly generatedAssembly = null;
 if (results.Errors.Count == 0)
 {
  generatedAssembly = results.CompiledAssembly;
 }
 return generatedAssembly;
}

  如Assembly a = CompileInMemory(GenerateCode(typeNamespace, typeName, "return inputMessage;"));的调用将会生成一个新的程序集。你可能会用任何你想要的方法实体代替"return inputMessage;"来创建预定的变量作些并发调用。

  创建一个实例

  你已经动态生成了一个程序集并将其编译到内存中。下一个任务就是从程序集中创建一个类的实例。这实际上比听起来更加复杂。你已经创建的程序集存在于内存中。对它的存在没有任何参考信息,因此你不能简单的创建一个新的实例,因为它们不会解决问题。创建一个类以拥有所有已编译程序集作为一个工作区。你将不顾类型决定事件,所以当一个类型需要时你可以使用你的类型中的一个。 ExecutionHost示例代码

  下面的代码定义了一个名为ExecutionHost的类,它追踪了你所有的动态编译程序集:

using System;
using System.Collections;
using System.Reflection;
namespace CodeGuru.CodeDomSample
{
 class ExecutionHost
 {
  private Hashtable assemblies = null;
  public ExecutionHost()
  {
   assemblies = new Hashtable();
   // 响应类型解析事件(the type resolution event)要求以截取它并找到我们类型
   AppDomain.CurrentDomain.TypeResolve += new ResolveEventHandler(CurrentDomain_TypeResolve);
  }
  private Assembly CurrentDomain_TypeResolve(object sender,ResolveEventArgs args)
  {
   // 为预定的类型找出我们程序集
   Assembly a = null;
   if (assemblies.ContainsKey(args.Name))
   {
    a = (Assembly)assemblies[args.Name];
   }
   return a;
  } public void AddAssembly(string fullTypeName, Assembly a)
  {
   assemblies.Add(fullTypeName, a);
  } public string Execute(string typeFullName, string msg)
  {
   // 尝试创建触发事件所需要的类型
   Type targetType = Type.GetType(typeFullName, true, true);
   object target =targetType.Assembly.CreateInstance(typeFullName);
   IExecutableModule m = (IExecutableModule)target; return m.SayHello(msg);
  }
 }
}
namespace CodeGuru.CodeDomSample
{
 public interface IExecutableModule
 {
  string SayHello(string inputMessage);
 }
}
public static CodeCompileUnit CreateExecutionClass(string typeNamespace,string typeName,string scriptBody)
{
 // 创建CodeCompileUnit以存放代码
 CodeCompileUnit ccu = new CodeCompileUnit(); // 分配给预期的命名空间
 CodeNamespace cns = new CodeNamespace(typeNamespace);
 cns.Imports.Add(new CodeNamespaceImport("System"));
 ccu.Namespaces.Add(cns);
 // 创建类
 CodeTypeDeclaration parentClass = new CodeTypeDeclaration(typeName);
 cns.Types.Add(parentClass);
 // 新行-为IExecutableModule接口添加一个实现
 parentClass.BaseTypes.Add(typeof(CodeGuru.CodeDomSample.IExecutableModule));
 // 创建获得一个参数并返回一个字符串的SayHello方法
 CodeMemberMethod method = new CodeMemberMethod();
 method.Name = "SayHello";
 method.Attributes = MemberAttributes.Public;
 CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression(typeof(string),
"inputMessage");
 method.Parameters.Add(arg);
 method.ReturnType = new CodeTypeReference(typeof(string));
 // 添加预期代码到方法实体
 CodeSnippetStatement methodBody = new CodeSnippetStatement(scriptBody);
 method.Statements.Add(methodBody);
 parentClass.Members.Add(method);
 return ccu;
}

  注意Execute方法。它用反射来创建预定类型的一个实例。这将触发_TypeResolve事件并允许你的程序集中的一个被返回,如果该被返回程序集通过AddAssembly方法已被添加到ExecutionHost中了。

  你也要注意在你的动态生成代码中添加的接口实现。没有它,你将不知道如何调用预期的方法。为了你的生成代码,IExecutableModule接口与作为一个基类添加的附加接口的CreateExecutionClass方法的一个最新副本一同被提供。

  另外,因为你增添了一个现在需要在CodeGuru.DynamicCode程序集内部使用的接口,你必须给含有IExecutableModule 声明的CodeGuru.CodeDomSample添加一个接口。请看下面最新的CompileInMemory副本:

static Assembly CompileInMemory(string code)
{
 CSharpCodeProvider provider = new CSharpCodeProvider();
 CompilerParameters options = new CompilerParameters();
 options.IncludeDebugInformation = false;
 options.GenerateExecutable = false;
 options.GenerateInMemory = true;
 // 新行-添加一个接口到需要的程序集
 options.ReferencedAssemblies.Add("CodeGuru.CodeDomSample.exe");
 CompilerResults results = provider.CompileAssemblyFromSource(options, code);
 provider.Dispose(); Assembly generatedAssembly = null;
 if (results.Errors.Count == 0)
 {
  generatedAssembly = results.CompiledAssembly;
 } return generatedAssembly;
}

  现在,你可以用下面的测试代码来测试动态生成一个程序集然后对方法做一个调用的端到端(end-to-end)过程:

string typeNamespace = "CodeGuru.DynamicCode";
string typeName = "ScriptType" + Guid.NewGuid().ToString("N");
Assembly a = CompileInMemory(GenerateCode(typeNamespace, typeName,"return inputMessage;"));
ExecutionHost host = new ExecutionHost();
string fullTypeName = typeNamespace + "." + typeName;
host.AddAssembly(fullTypeName, a);
string test = host.Execute(fullTypeName, "Hello World!");

  每次在你生成代码时使用Guid生成唯一对象名称。

  后记

  你已看完了一个非常基本的例子,它描述了一个复杂的主题及完成这个任务所需要的代码。在类型名称上添加Guid是为了确保其唯一性,因此你可以随心所欲地编译并使用各种不同的类型而不会在名称上发生冲突。你可以自由改变“return inputMessage”方法实体成为任何你喜欢的代码并试用之。你可以改变它,以使得所有关于方法实体的代码被存储在一个数据库中并在运行时重新获得。
分享到:
评论

相关推荐

    .net 4.0 反编译编译工具

    ILASM与ILDASM相反,它是用来将IL代码编译回.NET程序集的工具。这对于验证IL代码的正确性或者创建自定义的.NET程序集非常有用。使用ILASM,开发者可以直接编写IL代码,然后将其编译为可执行文件或动态链接库。命令行...

    NET下的动态代码编译探索

    在.NET框架中,动态代码编译是一项强大的功能,允许程序员在运行时生成和编译代码,从而提高了灵活性和效率。这项技术尤其适用于那些需要根据运行时环境或用户需求定制行为的场景。以下将详细探讨.NET动态代码编译的...

    .net 反编译工具

    《.NET 反编译工具:揭示C#代码的秘密》 在.NET开发环境中,程序员们经常需要查看已编译的二进制文件内部的源代码,以便于学习、调试或分析他人的软件。这时,.NET反编译工具就显得尤为重要。本文将深入探讨这一...

    .NET Reflector(.NET反编译软件)

    标题中的".NET Reflector(.NET反编译软件)"指的是这款能够将已编译的.NET程序集反编译回源代码的工具。它的工作原理是读取由编译器生成的中间语言(IL)代码,并将其转换为可读性强的源代码形式。这一过程对于开发者...

    .NET程序反编译工具 - ILSpy ver 1.0.0.1000

    总的来说,ILSpy是一款强大的开发工具,它为.NET开发者提供了透明度,使得他们可以深入探索.NET程序的内部工作,从而更好地学习、调试和优化代码。其开源的性质也促进了社区的参与和改进,使得工具本身不断进化,...

    asp.net示例源代码

    这个标题暗示了我们可能会看到一些实际的ASP.NET代码示例,用于展示如何在实践中运用该技术。 【描述】虽然描述中的内容看似与主题无关,提及的是"郭德纲的歌的歌的歌的个哥哥哥哥哥哥哥哥个哥哥哥哥哥哥哥哥",这...

    经典ASP.NET项目源代码

    ASP.NET是一种由微软开发的服务器端Web应用程序框架,用于构建动态网站、 web 应用程序和 web 服务。这个经典ASP.NET项目源代码的压缩包很可能是包含了一系列使用早期版本ASP.NET(可能是ASP.NET 1.x到4.x)编写的...

    Reflector 超级牛B .Net反编译工具 C# VB.net

    Reflector是一款由Lutz Roeder开发的.NET框架反编译工具,它能够将已编译的.NET程序集(dll或exe文件)转换回源代码形式,这其中包括C#、VB.NET等多种编程语言。对于开发者来说,这是一项极其宝贵的资源,尤其是当...

    反编译net2.0程序

    标题提及的"反编译net2.0程序"指的是利用特定的工具来查看.NET 2.0框架下编译后的程序集(assembly)的源代码逻辑。这个过程对于学习、调试或者逆向工程都是至关重要的。其中,"Reflector.exe"是一款经典的.NET反...

    .NET Reflector 5.0 Release dll文件返编译工具

    总而言之,.NET Reflector是一款强大的工具,它通过反编译和解析DLL文件,为.NET开发者提供了一种深入探索代码世界的方式,极大地提高了代码理解和问题解决的效率。无论你是学习新的.NET库,还是进行代码维护,它都...

    vs.net反编译工具Reflector

    Reflector是一款著名的.NET框架反编译工具,由Lutz Roeder开发,后来被Red Gate Software...通过掌握如何使用Reflector,开发者可以提高代码理解和调试的效率,同时也能在遵守法律的前提下探索.NET世界更深层次的奥秘。

    paint.net.4.2.10_C#_paint.net.4.2.10_反编译后修改_

    《深入探索:C#编程与paint.net.4.2.10反编译修改实践》 在IT领域,软件开发是一项复杂而精细的工作,而深入理解软件内部运作机制更是技术高手们热衷的挑战。本篇文章将围绕"paint.net.4.2.10"这一图像编辑软件,...

    一款非常好用的.net反编译工具

    Reflector Pro是Red Gate Software公司开发的一款专业.NET反编译器,它提供了一个全面的解决方案来探索、分析和理解.NET代码。以下是对这款工具及其功能的详细说明: 1. **多版本.NET支持**:Reflector Pro支持多种...

    JustDecompile新的免费的.NET 浏览和反编译的开发者效率工具

    JustDecompile新的免费的.NET 浏览和反编译的开发者效率工具, JustDecompile基于Telerik的多年的代码分析和...JustDecompile让你轻松地探索和分析已经编译过的.NET程序集,你只需要简单地点击一个按钮就能反编译代码。

    .net Framework源代码分析

    本篇文章将深入探讨.NET Framework的源代码,尤其是System命名空间下的内容,帮助读者更好地理解其内部机制。 1. **CLR(Common Language Runtime)** .NET Framework的核心是CLR,它是.NET应用程序的运行环境。...

    Reflector(.Net的Dll反编译工具) v6.8.2.5绿色英文版

    Reflector是一款著名的.NET框架下DLL反编译工具,由Lutz Roeder开发,它能够将.NET编译后的IL(中间语言)代码还原成易于理解的C#、VB.NET或其他.NET支持的语言源代码。Reflector v6.8.2.5是其的一个绿色英文版本,...

    Visual Studio Code 源代码(.net).7z

    《深入探索Visual Studio Code与.NET Framework 4.6.1源代码》 Visual Studio Code(简称VS Code)是一款由微软开发的免费、跨平台、开源的代码编辑器,支持多种编程语言,拥有丰富的插件生态,深受开发者喜爱。在...

    反编译工具.NET Reflector 6.6

    .NET Reflector是一款强大的反编译工具,由Lokad公司开发,主要用于查看、理解和学习.NET框架下编译的代码。它的6.6版本是该工具的一个历史版本,提供了对.NET程序集的深入洞察,帮助开发者了解类库的工作原理,逆向...

    .net反编译工具

    .NET反编译工具是开发人员和逆向工程师常用的软件,主要用于查看、理解和分析.NET框架编译后的IL(中间语言)代码。这些工具可以帮助开发者研究已有的.NET程序,学习其内部工作原理,或者在没有源代码的情况下调试和...

    .NET平台反编译工具ILSpy

    它能够将已编译的.NET程序集(如DLL或EXE文件)反编译回源代码,这对于学习和研究他人代码、调试问题、分析库的功能,甚至在丢失源代码的情况下恢复源码具有极大的价值。ILSpy支持C#和VB.NET的源代码输出,使得不同...

Global site tag (gtag.js) - Google Analytics