前两天翻看MSDN玩,发现一个挺有趣的功能,可以直接使用代码控制C#程序的编译。有这么个东西,要是有些程序需要实现即时修改代码再编译成程序集的功能就比较方便了。...话说,ASP.Net该不会是用这个实现的吧?

先上一段代码,功能是编译参数中引用的文件中包含的代码,如果不带参数则编译一段自带的代码:


 1:  using System;
 2:  using System.CodeDom;
 3:  using System.CodeDom.Compiler;
 4:  using Microsoft.CSharp;
 5:  using System.IO;
 6:   
 7:  namespace CSharpComplierControl
 8:  {
 9:      class Program
10:      {
11:          static void Main(string[] args)
12:          {
13:              string code = @"
14:  using System;
15:  namespace Application{
16:      class App{
17:          public static void Main(string[] args){
18:              Console.WriteLine(" + "\"Hello,haha\"" + @");
19:          }
20:      }
21:  }";
22:              bool noInput = false;
23:              FileInfo sourceCode = null;
24:              if (args.Length == 0)
25:              {
26:                  noInput = true;
27:              }
28:              else
29:              {
30:                  sourceCode = new FileInfo(args[0]);
31:                  if (!sourceCode.Exists)
32:                  {
33:                      noInput = true;
34:                  }
35:              }
36:   
37:              string objectExecutive = "test.exe";
38:              CompilerParameters compilerParameters = new CompilerParameters();
39:              compilerParameters.GenerateExecutable = true;
40:              compilerParameters.OutputAssembly = objectExecutive;
41:              compilerParameters.IncludeDebugInformation = true;
42:              compilerParameters.GenerateInMemory = false;
43:              compilerParameters.TreatWarningsAsErrors = false;
44:   
45:              CompilerResults compilerResults = null;
46:              if (noInput)
47:              {
48:                  compilerResults = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromSource(compilerParameters, code);
49:              }
50:              else
51:              {
52:                  compilerResults = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromFile(compilerParameters, sourceCode.FullName);
53:              }
54:              if (compilerResults.Errors.Count > 0)
55:              {
56:                  Console.WriteLine("Errors:");
57:                  foreach (CompilerError ce in compilerResults.Errors)
58:                  {
59:                      Console.WriteLine("    {0}", ce.ToString());
60:                  }
61:              }
62:              else
63:              {
64:                  Console.WriteLine("Compiler Completed.");
65:              }
66:          }
67:      }
68:  }

需要注意的是,这段代码中没有设置引用的程序集,因此如果需要编译的代码使用了默认包含的程序集中所没有的功能,上面的这个程序就只能无奈地输出一堆Errors了-_-|||毕竟只是个示例代码,姑且饶了我吧...

从上面的代码很容易看出来,控制编译的功能主要是通过System.CodeDom.Compiler.CodeDomProvider类完成的。通过调用CodeDomProvider的工厂方法CreateProvider(string),派生于CodeDomProvider的类被创建并负责执行生成程序集的工作——本例中这个类是Microsoft.CSharp.CSharpCodeProvider,毕竟要编译的是C#代码嘛,如果需要编译VisualBasic代码的话,直接给上述工厂方法传入"VisualBasic"就可以了.

vs2019编译tesseract 源码 vs编译代码_c#

CodeDomProvider类(当然CSharpCodeProvider也是一样的)提供了很多编译相关的成员,其中名字为CompileAssemblyFromXxxx的都是用于生成程序集的,三个方法都将System.CodeDom.Compiler.CompilerResults作为返回值,都需要接受一个System.CodeDom.Compiler.CompilerParameters作为参数,其主要的差别就在于输入的不同.上面的示例代码演示的是使用文件输入和使用代码字符串输入的版本.

---

vs2019编译tesseract 源码 vs编译代码_System_02

System.CodeDom.Compiler.CompilerParameters提供了编译所需要使用的配置选项.微软的库命名就是漂亮,光是看看属性的名字就知道属性是干什么的了^^

这里要特别说明的有三个,一个是CompilerOptions属性.它的定义是:



1:  public string CompilerOptions { get; set; }



请将它想象成直接在命令行中使用csc编译器时输入的编译器参数.

MainClass属性:



1:  public string MainClass { get; set; }



在这里可以指定包含入口点的类的名字.

还有压轴的VIP,ReferencedAssemblies属性:



1:  public StringCollection ReferencedAssemblies { get; }



在这个字符串集合中输入需要编译的代码引用的程序集的完全限定路径(当然包含在GAC当中的就不用了).我们的示例代码就是没有设置这个,所以只能用于编译只是用默认引用的程序集中功能的代码.

---

System.CodeDom.Compiler.CompilerResults

vs2019编译tesseract 源码 vs编译代码_c#_03

编译的返回值CompilerResults类结构相对简单得多。我们的例子中通过枚举CompilerResults.Errors来检测和罗列编译器的出错信息.通过其他属性,我们还能够获得刚刚生成的程序集的实例,路径,编译器的所有输出和返回值,以及涉及到的临时文件等.

 

玩得愉快!

-示例代码-


话说虽然咱不讨厌朝鲜,也不是葡萄牙控,不过7:0那场比赛还真不是一般的赏心悦目啊,下半场超级漂亮的说...

那不是一颗葡萄在战斗!那是一串葡萄在战斗!!!