知道了如果构建代码文档,知道了如何生成代码,那么编译程序集就很简单了。

CodeDomProvider 类提供了三个可以执行编译的方法:

1、CompileAssemblyFromSource——这个好懂,也好办,就是用字符串直接构建代码,然后传给这个方法,就可以把源代码编译了。

2、CompileAssemblyFromFile——这个是把一个代码文件传给方法进行编译,文件中包含源代码。

3、CompileAssemblyFromDom——这个重载版本跟我们之前所学的内容关联性最大,因为它是把 CodeCompileUnit 实例传进去来编译的。

以上几个重载,尽管代码的来源不同,但都有一个共同点:支持多个源。

咱们知道,代码文档结构的根是命名空间,然后是类型,类型下是成员。一个程序集是可以包括多个命名空间的,假设编译的代码源自文件,而每个文件的代码都包含一个命名空间,那么要将多个命名空间合到一个程序集中,就可以把多个文件同时进行编译。当然,你也可以把所有的代码都放到一个文件中,然后只编译这个文件就行了。随你怎么弄,这样做只是为了灵活。

大伙应该也发现了,这些方法都有一个参数,是 CompilerParameters 类型的,它的作用是设置编译选项。老周大概总结这么几点,以供大家参考,其他的大家不妨自己摸索,放心,不会很复杂的。

1、如果你要生成可直接运行的程序集,即.exe,那就得把GenerateExecutable属性设置为true,默认它是为false的,即生成dll文件。所有可执行文件,不管你用啥语言写,都必须有入口点的,所以,如果要生成exe,就必须设置MainClass属性,它指的是包含Main方法的类,类名必须完整,要写上命名空间的名字,如my.Program。

2、设置OutputAssembly属性,指定输出文件名,可以是绝对路径,也可以是相对路径。如dddd.exe、kkkk.dll等。当然你可以用其他名字,如comm.ft,但是,如果要生成exe,后缀必须是.exe,这样才能双击运行。如果这个属性没有指定,它会生成一个随机的文件名,并且输出临时文件目录下。注意:输出文件是设置OutputAssembly属性,不是CoreAssemblyFileName属性,千万不要弄错,CoreAssemblyFileName是设置核心类库的位置,即常见的 mscorlib.dll,主要是包含.net基本类型的程序集,一般我们不用设置它,由编译器自行选择合适的版本。

3、如果GenerateInMemory设置为true,则可以不设置OutputAssembly,因为GenerateInMemory属性表示把程序集生成到内存中,而不是文件中。

4、TempFiles设置编译时所产生的临时文件的路径,默认是临时文件夹,这个一般不用改。

5、编译过程实际上是调用.net的命令行工具的,对于VB.NET语言,调用vbc命令,对于C#语言,调用csc命令。如果要指定一些编译器选项,可以设置CompilerOptions属性,它是一个字符串。关于编译选项,可以在开发工具的命令行工具中输入csc /?或vbc /?查看。

下面,先举一个最简单的例子,就直接用代码源文件来编译。

假设我在【文档】下建了一个demo.cs文件,在里面输入了以下代码:

using System;

namespace Sample
{
public class Demo
{
// 成员列表
}
}

然后保存。

接下来咱们要在程序中动态编译这个代码文件。

            // 文件路径
string doclib = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string srccodePath = Path.Combine(doclib, "demo.cs"); CodeDomProvider provider = CodeDomProvider.CreateProvider("cs");
// 编译参数
CompilerParameters p = new CompilerParameters();
// 输出文件
p.OutputAssembly = "DemoLib.dll";
// 添加引用的程序集
// 其实我们这里用不上,只是作为演示
// mscorLib.dll是不用添加的,它是默认库
p.ReferencedAssemblies.Add("System.dll");
// 编译
CompilerResults res = provider.CompileAssemblyFromFile(p, srccodePath);
// 检查编译结果
if (res.Errors.Count == )
{
// 没有出错
Console.WriteLine("编译成功。");
// 获取刚刚编译的程序集信息
Assembly outputAss = res.CompiledAssembly;
// 全名
Console.WriteLine($"程序集全名:{outputAss.FullName}");
// 位置
Console.WriteLine($"程序集位置:{outputAss.Location}");
// 程序集中的类型
Type[] types = outputAss.GetTypes();
Console.WriteLine("----------------------\n类型列表:");
foreach (Type t in types)
{
Console.WriteLine(t.FullName);
}
}
else
{
// 如果编译出错
Console.WriteLine("发生错误,详见以下内容:");
foreach (CompilerError er in res.Errors)
{
Console.WriteLine($"行{er.Line},列{er.Column},错误号{er.ErrorNumber},错误信息:{er.ErrorText}");
}
}

代码虽长,但不难懂。注意编译完成后,会返回一个表示编译结果的CompilerResults实例,如果其中的Errors集合中没有元素,说明编译成功,如果里面有东西,表明其间发生了错误。每个错误都用CompilerError类封装。

如果成功编译,通过结果的CompiledAssembly属性就可以获取到刚刚编译的程序集信息。

示例输出结果如下图。

下面提供一个用 CodeDom 来编译的例子。

            CodeCompileUnit unit = new CodeCompileUnit();
// 命名空间
CodeNamespace ns = new CodeNamespace("MyApp");
unit.Namespaces.Add(ns);
ns.Imports.Add(new CodeNamespaceImport(nameof(System)));
ns.Imports.Add(new CodeNamespaceImport($"{nameof(System)}.{nameof(System.Windows)}.{nameof(System.Windows.Forms)}"));
// 类型
CodeTypeDeclaration typedec = new CodeTypeDeclaration("Program");
ns.Types.Add(typedec);
typedec.Attributes = MemberAttributes.Public | MemberAttributes.Final;
// 入口点
CodeEntryPointMethod main = new CodeEntryPointMethod();
typedec.Members.Add(main);
// 创建窗口实例
CodeVariableDeclarationStatement newwindow = new CodeVariableDeclarationStatement();
main.Statements.Add(newwindow);
newwindow.Name = "mainWindow";
newwindow.Type = new CodeTypeReference(nameof(System.Windows.Forms.Form));
newwindow.InitExpression = new CodeObjectCreateExpression(nameof(System.Windows.Forms.Form));
// 设置窗口标题栏
CodeAssignStatement settitle = new CodeAssignStatement();
main.Statements.Add(settitle);
settitle.Left = new CodePropertyReferenceExpression(new CodeVariableReferenceExpression(newwindow.Name), nameof(System.Windows.Forms.Form.Text));
settitle.Right = new CodePrimitiveExpression("我的应用程序");
// 调用 Application.Run 方法
CodeMethodInvokeExpression invokeexp = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(nameof(System.Windows.Forms.Application)), nameof(System.Windows.Forms.Application.Run), new CodeVariableReferenceExpression(newwindow.Name));
CodeExpressionStatement invrunstatem = new CodeExpressionStatement(invokeexp);
main.Statements.Add(invrunstatem); // 生成代码
CodeDomProvider provider = CodeDomProvider.CreateProvider("cs");
provider.GenerateCodeFromCompileUnit(unit, Console.Out, null); // 编译
CompilerParameters p = new CompilerParameters();
p.GenerateExecutable = true; //生成exe
p.CompilerOptions = "/t:winexe"; //非控制台应用程序
p.OutputAssembly = "testapp.exe";
// 包含入口点的类
p.MainClass = $"{ns.Name}.{typedec.Name}";
// 引用的程序集
p.ReferencedAssemblies.Add("System.dll");
p.ReferencedAssemblies.Add("System.Windows.Forms.dll"); CompilerResults res = provider.CompileAssemblyFromDom(p, unit);
if (res.Errors.Count == )
{
Console.WriteLine("编译成功。");
// 启动它
System.Diagnostics.Process.Start(res.CompiledAssembly.Location);
}
else
{
Console.WriteLine("错误信息:");
foreach (CompilerError er in res.Errors)
{
Console.WriteLine(er.ErrorText);
}
}

代码虽然很是TMD的长,但你别紧张,其实就做了三件事。

1、构建代码逻辑。这个示例生成一个Windows Form程序,所以,需要定义一个类,在类中必须有Main方法,在Main方法中实例化窗口类,然后用Application.Run方法显示窗口。

2、生成代码,这个大家已经熟悉,前面N篇文章中就用到多次。

3、编译。

我们重点放在编译上,请大家注意,尽管GenerateExecutable属性已经被设置为true,不过,你懂的,exe程序有两类,一类是控制台,一类是常见的win窗口,所以,这里还得借助编译器命令行选项,加一个/t:winexe,表示生成的是Windows标准窗口程序。而正因为生成的是exe文件,所以,不要忘了MainClass属性,指定我们刚刚用CodeDom构建的那个类,它包含了入口点方法。

运行之后,会在当前程序的同一目录下,生成一个.exe文件,并且执行后,显示一个空白的窗口。如下图所示。

好了,说到了编译部分,CodeDom的这一系列文章也写得差不多了,不过后面还会加一篇补充的,把一些零碎的内容过一下。

之所以前面的文章中,一些评论老周没有回复,是因为老周又发现,又有人把CodeDom和Emit搞混了,动态发出程序集是基于指令的,而CodeDom是基于代码文档,CodeDom既可用于生成代码源文件,也可用于动态编译。这个老周前面是强调过的,希望大家注意。

动态发出程序集和IL的内容比较复杂,也不算太常用,改天有空,老周也写一写动态程序集方面的内容吧。

【.net 深呼吸】细说CodeDom(9):动态编译的更多相关文章

  1. 【.net 深呼吸】细说CodeDom(2):表达式、语句

    在上一篇文章中,老周厚着脸皮给大伙介绍了代码文档的基本结构,以及一些代码对象与CodeDom类型的对应关系. 在评论中老周看到有朋友提到了 Emit,那老周就顺便提一下.严格上说,Emit并不是针对代 ...

  2. 【.net 深呼吸】细说CodeDom(1):结构大观

    CodeDom 是啥东东?Html Dom听过吧,XML Dom听过吧.DOM一般可翻译为 文档对象模型,那 Code + DOM呢,自然是指代码文档模型了.如果你从来没接触过 CodeDom,你大概 ...

  3. 重写代码生成器支持模板(多层架构,MVC),多语言c#,java;支持mysql和sqlserver,动态编译

    多年前用过李天平前辈的,自己改过,后来李老师做动软了,不给源码,修改不是很方便.加上我目前需要转java方向,于是决定自己搞.到目前为止花了整整一个星期了,看看目前的成果. 最后是代码工程文件,用c# ...

  4. 浅谈VB.Net 程序的编译和动态编译

    ---恢复内容开始--- 一般,我们都是通过Visual Studio(下面简称vs)来编写和编译vb.net应用程序的,但是,不少的人并不知道vs是通过何种方式编译程序的.今天,我们就来探讨一下编译 ...

  5. .NET中的动态编译

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

  6. .net 动态编译解决考勤计算问题

    由于公司实施SAP HR项目,但是SAP HR对考勤功能真的太弱化了,直接从考勤机上读取的原始打卡记录不能直接传输到HR系统里面,因为SAP HR不能识别那些多余的打卡记录,而且必须把打卡记录进行成组 ...

  7. c#:实现动态编译,并实现动态MultiProcess功能(来自python multiprocess的想法)

    由于之前一直遇到一些关于并行进行数据处理的时效果往往不好,不管是c#还是java程序都是一样,但是在Python中通过multiprocess实现同样的功能时,却发现确实可以提高程序运行的性能,及服务 ...

  8. 在C#中动态编译T4模板代码

    转: http://www.wxzzz.com/1438.html 资料: https://cnsmartcodegenerator.codeplex.com/SourceControl/latest ...

  9. 让C#语言充当自身脚本!——.NET中的动态编译

    原文:让C#语言充当自身脚本!--.NET中的动态编译 代码的动态编译并执行是.NET平台提供给我们的很强大的一个工具,用以灵活扩展(当然是面对内部开发人员)复杂而无法估算的逻辑,并通过一些额外的代码 ...

随机推荐

  1. Apache无法启动解决 the requested operation has failed

    Apache不能启动解决办法 原因一:80端口占用例如IIS,另外就是迅雷. 原因二:软件冲突装了某些软件会使apache无法启动如Dr.com 你打开网络连接->TcpIp属性->高级- ...

  2. windows tcp端口映射或端口转发

    windows tcp端口映射或端口转发 windows内部有一个叫netsh的玩意,可以把tcp端口进行映射或转发,可惜不支持udp.举个例子:一台windows有一个80端口,对外可以访问.另有一 ...

  3. selenium2入门 用Yaml文件进行元素管理 (五)

    比如界面有一个按钮,id号是test.如果进行对象化的话,就是test.click就可以了.不用每次都要去创建test对象.如果id号变了,我们也只需要改一下test的名称就行了. 使用Yaml需要用 ...

  4. iSwifting社区【www.iSwifting.com】招聘版主

    iSwifting社区[www.iSwifting.com]昨天找来几个主持人.我已经被任命为他们的任务领域. 他们有百度的project噢,还有cocoaChina问答区的活跃人物! 欢迎大家一起学 ...

  5. [译]在运行时内存中的Java对象是怎么样的

    (文章翻译自What do Java objects look like in memory during run-time?) 我们知道函数在内存中是作为一个活动记录栈来实现的.而且我们知道Java ...

  6. MyEclipse的真正价值——时间等于金钱

    全世界成千上万的Java开发者选择MyEclipse作为首选的Eclipse IDE,甚至超过了著名的开发工具 IBM Rational和Eclipse Java. 为什么? 很简单,MyEclips ...

  7. 在ubuntu纯字符gdb界面下来开发调试嵌入式ARM

    前面一个帖子介绍了使用eclipse来开发STM32的固件,但有的时候使用Eclipse的GDB调试器会崩溃掉,反复这样造成我们开发的效率降低,信心也会受一打击. 最近接触到的许多源码,就是在linu ...

  8. Angularjs web应用

    构建兼容浏览器的Angularjs web应用 背景 随着mvvm逐渐成熟,现在使用jQuery构建web应用已经显得过时了,而且使用jQuery需要编写更多的代码去控制dom的取值.赋值.绑定事件等 ...

  9. 学习Sass之安装Sass

    学习Sass之安装Sass 为什么使用Sass 作为前端(html.javascript.css)的三大马车之一的css,一直以静态语言存在,HTML5火遍大江南北了.javascript由于NODE ...

  10. C语言中的内存对齐

    最近看了好多,也编了好多C语言的浩强哥书后的题,总觉的很不爽,真的真的好怀念linux驱动的代码,好怀念那下划线,那结构体,虽然自己还很菜. 同时看了一遍陈正冲老师的C语言深度剖析,收益很多,又把唐老 ...