C#外部代码运行时编译报错:找不到动态生成程序集文件
问题诊断与修复方案
问题根源
磁盘临时文件引发的加载异常
你将options.GenerateInMemory设为false,这会让编译器把编译后的程序集写入磁盘临时文件。但这类临时文件的生命周期不受控——可能被系统后台自动清理,或者因权限问题无法访问,所以每次报错的文件名都不固定。参数替换未加引号,直接导致编译失败
传递的字符串参数(比如"AAAA")直接替换占位符后,生成的代码会变成string strPar1 = AAAA;,编译器会把AAAA识别为未定义变量,直接编译失败。后续尝试加载未成功生成的程序集时,就会抛出“找不到文件”的错误(本质是没有有效程序集可加载)。未检查编译错误
代码中没有判断编译是否成功,不管编译结果如何都直接执行反射操作,会引发连锁异常。
修复后的完整代码
public static string CreateCSharpFunction(string strCode, string strPar1 = null, string strPar2 = null) { // 使用独特占位符,避免替换时出现冲突 var finalCode = @" using System; public class Function { public string Execute() // 避免用Main,这是程序入口方法,易引发混淆 { string strPar1 = {{strPar1}}; string strPar2 = {{strPar2}}; {{userCode}} } }"; // 替换参数时添加双引号,并转义字符串内的引号 finalCode = finalCode.Replace("{{strPar1}}", strPar1 != null ? $"\"{EscapeQuotes(strPar1)}\"" : "string.Empty"); finalCode = finalCode.Replace("{{strPar2}}", strPar2 != null ? $"\"{EscapeQuotes(strPar2)}\"" : "string.Empty"); finalCode = finalCode.Replace("{{userCode}}", strCode); var options = new CompilerParameters(); options.GenerateExecutable = false; options.GenerateInMemory = true; // 改为内存生成,彻底绕开磁盘文件问题 options.ReferencedAssemblies.Add("System.dll"); // 添加必要程序集引用,保证基础类型可用 var provider = new CSharpCodeProvider(); var compileResult = provider.CompileAssemblyFromSource(options, finalCode); // 先检查编译错误,有错误直接抛出详细信息 if (compileResult.Errors.HasErrors) { var errorMsg = string.Join("\n", compileResult.Errors.Cast<CompilerError>().Select(e => $"[{e.ErrorNumber}] {e.ErrorText}")); throw new InvalidOperationException($"编译失败:\n{errorMsg}"); } var type = compileResult.CompiledAssembly.GetType("Function"); var instance = Activator.CreateInstance(type); var method = type.GetMethod("Execute"); var result = method.Invoke(instance, null); return result?.ToString() ?? string.Empty; } // 辅助方法:转义字符串中的双引号,避免破坏代码结构 private static string EscapeQuotes(string input) { return input?.Replace("\"", "\\\"") ?? string.Empty; }
关键修复点说明
- 内存生成程序集:将
GenerateInMemory设为true,编译后的程序集直接存在内存中,无需写入磁盘,彻底解决文件找不到的问题。 - 参数正确转义:用
EscapeQuotes处理参数内的双引号,替换时为参数包裹双引号,保证生成的代码语法合法,不会出现未定义变量的错误。 - 添加必要引用:加入
using System;并引用System.dll,确保string、string.Empty等基础类型和成员能正常使用。 - 编译错误检查:编译完成后先判断是否有错误,有错直接抛出详细信息,方便调试。
- 方法重命名:把
Main改为Execute,避免与程序入口方法冲突。
调用示例
// 从外部文件读取的代码示例 var strCode = @" string strResult = strPar1 + ""ABC""; return strResult;"; var result = Helper.CreateCSharpFunction(strCode, "AAAA"); // 输出结果:AAAAABC
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

