一、简介
CSharpCodeProvider 是 .NET 提供的一个强大工具,它允许开发人员在应用程序运行时动态地生成和执行 C# 代码。这一特性为后端开发带来了前所未有的灵活性和动态性,特别是在处理那些需要高度定制化或难以在编译时确定逻辑的场景时,尤为有用。
基本概念
动态编译是指程序在运行时能够接收源代码,并将其编译成可执行代码的过程。与传统的静态编译相比,动态编译不需要提前将源代码编译成程序集,而是可以在程序运行时即时完成这一过程。CSharpCodeProvider 正是这一概念的实现者之一,它利用 System.CodeDom.Compiler 和 Microsoft.CSharp 命名空间中的类来完成这一任务。
主要优势
- 灵活性:使用 CSharpCodeProvider,开发人员可以根据运行时需求动态生成和执行代码,实现高度定制化的功能。这特别适用于需要根据用户输入或外部数据源动态生成代码的场景。
- 插件系统:CSharpCodeProvider 可以用于开发插件系统,允许用户在应用程序运行时动态添加、加载和执行插件。这极大地提高了应用程序的扩展性和可维护性。
- 执行速度:尽管动态编译涉及到运行时的编译过程,但一旦代码被编译成程序集,其执行速度将接近静态编译的代码。这是因为编译后的代码以程序集的形式存在,可以被 .NET 运行时高效执行。
- 脚本引擎:通过 CSharpCodeProvider,开发人员可以实现一个简单的脚本引擎,允许用户在运行时编写和执行脚本代码。这为需要快速原型开发或脚本自动化功能的应用程序提供了便利。
效果:
二、实现效果
在网上的许多案例中,就用字符串写了一个 Hello World 就完事了,但实际项目中,这种案例几乎没有什么意义,所以这里我直接编译一整个项目,即使如此,也只是 CSharpCodeProvider 所有功能中的凤毛麟角,也只能做参考,项目结构如下:
需要编译的项目就是 WindowsFormsApp1,这名字我用的默认名字,为了防止一些没用的 .cs 文件也编译到其中,我添加了黑名单文件夹和黑名单文件的功能
新建一个 Winform 项目,项目名:动态编译,这里只是演示,所以名字我起的比较随意
代码:
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
namespace 动态编译
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 黑名单文件
/// </summary>
private List<string> BlacklistFiles = new List<string>();
/// <summary>
/// 黑名单文件夹
/// </summary>
private List<string> BlacklistFolders = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
BlacklistFolders.Add("Properties");
BlacklistFolders.Add("bin");
BlacklistFolders.Add("obj");
BlacklistFiles.Add("Program.cs");
string startupPath = Application.StartupPath;
string path = Path.GetFullPath(Path.Combine(startupPath, @"..\..\..\WindowsFormsApp1"));
string mainFormName = "WindowsFormsApp1.Form1";
CompileAndRunWinForm(path, mainFormName);
}
private void CompileAndRunWinForm(string sourceDirectory, string mainFormName)
{
var (sourceFiles, resourceFiles) = GetFiles(sourceDirectory);
var codeProvider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
GenerateExecutable = false,
OutputAssembly = $"{Application.StartupPath}\\DynamicAssembly.dll",
GenerateInMemory = false,
TreatWarningsAsErrors = false
};
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
parameters.ReferencedAssemblies.Add("System.Drawing.dll");
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
foreach (var resx in resourceFiles)
{
parameters.EmbeddedResources.Add(resx);
}
var results = codeProvider.CompileAssemblyFromFile(parameters, sourceFiles.ToArray());
if (results.Errors.HasErrors)
{
foreach (CompilerError error in results.Errors)
{
Console.WriteLine($"Error {error.ErrorNumber}: {error.ErrorText}");
}
return;
}
Console.WriteLine($"编译DLL路径: {results.PathToAssembly}");
if (string.IsNullOrEmpty(results.PathToAssembly))
{
Console.WriteLine("编译DLL路径为空");
return;
}
Assembly assembly = Assembly.LoadFile(results.PathToAssembly);
Type mainFormType = assembly.GetType(mainFormName);
if (mainFormType == null)
{
Console.WriteLine("未找到 {0} 对应的 Type", mainFormType);
return;
}
Form mainForm = (Form)Activator.CreateInstance(mainFormType);
mainForm.Show();
}
private (List<string>, List<string>) GetFiles(string folderPath)
{
List<string> csFiles = new List<string>();
List<string> resxFiles = new List<string>();
var allFiles = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories)
.Where(file => file.EndsWith(".cs") || file.EndsWith(".resx"));
foreach (var file in allFiles)
{
string fileDirectory = Path.GetDirectoryName(file);
bool isInBlacklist = BlacklistFiles.Any(blacklistedFile =>
file.EndsWith(blacklistedFile, StringComparison.OrdinalIgnoreCase));
bool isInBlacklistFolder = BlacklistFolders.Any(blacklistedFolder =>
fileDirectory.Split(Path.DirectorySeparatorChar).Contains(blacklistedFolder, StringComparer.OrdinalIgnoreCase));
if (!isInBlacklist && !isInBlacklistFolder)
{
if (file.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
csFiles.Add(file);
else if (file.EndsWith(".resx", StringComparison.OrdinalIgnoreCase))
resxFiles.Add(file);
}
}
return (csFiles, resxFiles);
}
}
}
新建一个 Winform 项目,项目名: WindowsFormsApp1,这里只是演示,希望你不要将这种项目名用在工作中,默认的界面 Form1 界面如下:
代码:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("这是 button1 的点击事件");
Form2 form2 = new Form2();
form2.Show();
}
}
}
在按钮中,添加了显示 Form2 的代码,没有其他功能了
Form2 界面如下,没有任何代码
运行后,动态编译项目的 Debug 目录中就会多出一个编译的 DLL,如下:
源码:
https://download.csdn.net/download/qq_38693757/89771252
效果:
结束
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言
end