在C#程序中动态编译运行外部代码遇System.Runtime引用错误
问题背景
我需要在TeacherCode.cs类中,通过反射和Roslyn动态编译并运行学生的StudentCode.cs代码。核心需求是检查学生代码中是否存在名为Print的方法,若存在则调用该方法(后续还需扩展检查递归、循环等代码结构)。但编译过程中出现以下错误:
Compilation failed:
(8,17): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
(8,9): error CS0012: The type 'Decimal' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
学生代码(StudentCode.cs)
// StudentCode.cs using System; public class StudentClass { public static void Print() { Console.WriteLine("Hello from StudentClass.Print!"); } }
教师代码(TeacherCode.cs)
// TeacherCode.cs using System; using System.IO; using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; public class Checker { public static void Main(string[] args) { // Path to the student's code file string filePath = "StudentCode.cs"; if (!File.Exists(filePath)) { Console.WriteLine("File not found: " + filePath); return; } string studentCode = File.ReadAllText(filePath); // Check if 'Print' method exists, and if so, invoke it CheckAndInvoke(studentCode, "Print"); } static void CheckAndInvoke(string code, string methodName) { var syntaxTree = CSharpSyntaxTree.ParseText(code); var compilation = CSharpCompilation.Create("StudentCode", new[] { syntaxTree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location) }, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var ms = new MemoryStream()) { var result = compilation.Emit(ms); if (!result.Success) { Console.WriteLine("Compilation failed:"); foreach (var diagnostic in result.Diagnostics) { Console.WriteLine(diagnostic.ToString()); } return; } ms.Seek(0, SeekOrigin.Begin); var assembly = Assembly.Load(ms.ToArray()); // Find the class and method var type = assembly.GetTypes().FirstOrDefault(); if (type != null) { var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public); if (method != null) { // Invoke the Print method method.Invoke(null, null); } else { Console.WriteLine("Method 'Print' not found in compiled assembly."); } } else { Console.WriteLine("No classes found in the compiled assembly."); } } } }
错误原因与解决办法
在.NET 6及以上的SDK风格项目中,object、decimal等基础类型不再定义在传统的mscorlib.dll中,而是移到了System.Runtime.dll里。当前的编译引用缺少这个关键程序集,导致类型无法解析。
只需在编译引用数组中添加System.Runtime的引用即可解决问题,有两种简洁的方式:
方式一:通过decimal类型获取引用
直接利用错误提示中提到的Decimal类型来定位程序集:
var compilation = CSharpCompilation.Create("StudentCode", new[] { syntaxTree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location), // 添加System.Runtime引用 MetadataReference.CreateFromFile(typeof(decimal).Assembly.Location) }, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
方式二:通过编译器特性类型获取引用
使用.NET中的标记类型来定位System.Runtime:
var compilation = CSharpCompilation.Create("StudentCode", new[] { syntaxTree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location), // 添加System.Runtime引用 MetadataReference.CreateFromFile(typeof(System.Runtime.CompilerServices.IsExternalInit).Assembly.Location) }, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
添加引用后,编译就能正常解析Object和Decimal类型,顺利完成编译并调用Print方法。
内容的提问来源于stack exchange,提问作者ajlakanen

