如何在当前应用内从字符串/外部文本文件执行C#主代码片段?
嘿,我完全get到你的需求了——你不是要把文本文件里的C#代码当成独立的外部模块或者命名空间来跑,而是希望它能像你亲手写在主程序里的代码片段一样执行,和主程序共享上下文对吧?之前搜的Roslyn相关帖子没找到合适的方案,那我给你两个贴合需求的实现思路,都是基于Roslyn来做的:
场景1:文本代码是主方法内的代码片段(可直接访问主程序变量)
如果你的文本文件里是类似Console.WriteLine("测试");这种主方法内的代码片段,还需要访问主程序里定义的变量,那用Roslyn的脚本API就完美解决,它能让文本代码和主程序共享上下文。
步骤:
- 先安装NuGet包:
Microsoft.CodeAnalysis.CSharp.Scripting - 编写主程序代码,把变量传递给脚本并执行文本内容:
using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; using System.IO; class Program { static async Task Main(string[] args) { // 从文本文件读取代码片段 string codeSnippet = File.ReadAllText("myCode.txt"); // 主程序里的变量,让文本代码能直接访问 int appVersion = 10; string appName = "MyDynamicApp"; // 配置脚本选项:引用必要的程序集,添加常用命名空间 var scriptOptions = ScriptOptions.Default .AddReferences(typeof(Program).Assembly, typeof(Console).Assembly) .AddImports("System"); // 把主程序变量打包成全局对象,供脚本访问 var globals = new { appVersion, appName }; // 执行文本里的代码 await CSharpScript.EvaluateAsync(codeSnippet, scriptOptions, globals); } }
比如你的myCode.txt内容可以是:
Console.WriteLine($"当前应用版本:{appVersion}"); Console.WriteLine($"欢迎使用{appName}!"); int calcResult = appVersion * 2; Console.WriteLine($"版本翻倍是:{calcResult}");
运行后,这段文本代码就像直接写在Main方法里一样,完全共享主程序的变量和上下文。
场景2:文本代码包含完整类/方法(融合到主程序的命名空间)
如果你的文本文件里是完整的类定义(甚至带using语句),希望它和主程序在同一个命名空间下,而不是独立的外部命名空间,那可以用Roslyn的动态编译API,把主程序模板和文本代码拼接成完整的编译单元,再编译执行。
步骤:
- 安装NuGet包:
Microsoft.CodeAnalysis.CSharp - 编写代码完成拼接、编译、执行:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.IO; using System.Reflection; class Program { static void Main(string[] args) { // 读取文本文件里的完整代码 string textClassCode = File.ReadAllText("myClassCode.txt"); // 构建完整的代码:主程序模板 + 文本代码(同命名空间) string fullCompilationCode = $@" using System; namespace MyMainApp {{ class Program {{ static void Main(string[] args) {{ // 调用文本代码里的方法 TextEmbeddedCode.RunMyLogic(); }} }} // 把文本里的类嵌入到主命名空间下 {textClassCode} }}"; // 配置编译需要的程序集引用 var references = new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Console).Assembly.Location) }; // 解析代码语法树并编译 var syntaxTree = CSharpSyntaxTree.ParseText(fullCompilationCode); var compilation = CSharpCompilation.Create( "DynamicMainAssembly", new[] { syntaxTree }, references, new CSharpCompilationOptions(OutputKind.ConsoleApplication)); // 编译到内存流 using var memoryStream = new MemoryStream(); var compileResult = compilation.Emit(memoryStream); // 处理编译错误 if (!compileResult.Success) { var errors = compileResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error); foreach (var error in errors) { Console.WriteLine($"编译错误:{error.GetMessage()}"); } return; } // 加载编译后的程序集并执行Main方法 memoryStream.Seek(0, SeekOrigin.Begin); var assembly = Assembly.Load(memoryStream.ToArray()); var programType = assembly.GetType("MyMainApp.Program"); var mainMethod = programType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public); mainMethod.Invoke(null, new object[] { new string[0] }); } }
比如你的myClassCode.txt内容可以是:
public static class TextEmbeddedCode { public static void RunMyLogic() { Console.WriteLine("我是文本文件里的类,和主程序在同一个命名空间!"); for(int i=0; i<3; i++) { Console.WriteLine($"循环次数:{i+1}"); } } }
这样编译执行后,文本里的类就完全是主程序命名空间的一部分,和你自己写的代码没区别。
重要注意事项
- 安全风险:执行外部来源的文本代码非常危险,一定要确保文件来源绝对可信,避免被注入恶意代码。
- 性能优化:动态编译有一定开销,如果需要频繁执行同一段文本代码,可以缓存编译后的程序集,避免重复编译。
- 程序集引用:如果文本代码用到了第三方库,要在编译选项里添加对应的程序集引用。
内容的提问来源于stack exchange,提问作者BattleReloaded
相关产品推荐
相关产品推荐

