能否提取Reflection.Emit创建的程序集中的类并物理保存,无需反编译工具?
答案:当然可以!分两步实现,无需外部反编译工具
嘿,这个问题我刚好折腾过,给你捋捋清楚:
第一步:把Reflection.Emit生成的程序集保存到磁盘
其实Reflection.Emit本身就支持直接把动态程序集存到指定路径,根本不需要额外工具。关键是创建AssemblyBuilder的时候,要指定允许保存的访问模式(AssemblyBuilderAccess.RunAndSave或者Save),之后调用Save方法就能生成.dll或.exe文件了。
给你个简单的示例代码:
using System.Reflection; using System.Reflection.Emit; using System.IO; var assemblyName = new AssemblyName("MyDynamicAssembly"); // 重点:用RunAndSave模式,既可以运行也可以保存 var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); // 定义模块,第二个参数是保存后的文件名 var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, "MyDynamicAssembly.dll"); // 随便定义一个测试类和方法 var typeBuilder = moduleBuilder.DefineType("MyDynamicClass", TypeAttributes.Public); var helloMethod = typeBuilder.DefineMethod( "PrintHello", MethodAttributes.Public | MethodAttributes.Static, typeof(void), Type.EmptyTypes); var ilGenerator = helloMethod.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldstr, "Hi from my dynamic class!"); ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })!); ilGenerator.Emit(OpCodes.Ret); // 完成类型创建 typeBuilder.CreateType(); // 指定保存路径,比如当前目录下的Output文件夹 string outputDir = Path.Combine(Environment.CurrentDirectory, "DynamicProject"); Directory.CreateDirectory(outputDir); string assemblyPath = Path.Combine(outputDir, "MyDynamicAssembly.dll"); assemblyBuilder.Save(assemblyPath); Console.WriteLine($"程序集已保存到:{assemblyPath}");
第二步:提取类并生成可读代码(无需外部反编译)
这里分两种情况来处理:
情况1:你自己生成动态程序集——同步生成源代码最省心
如果你是自己用Reflection.Emit写的代码,其实完全可以在生成IL的同时,用Roslyn(.NET官方的代码分析库)同步生成对应的C#源代码,这样直接就能保存成.cs文件,比事后提取方便多了。
比如,在定义完动态类后,同时生成对应的C#类代码:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; // 用Roslyn生成C#类语法树 var classSyntax = SyntaxFactory.ClassDeclaration("MyDynamicClass") .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) .AddMembers( SyntaxFactory.MethodDeclaration(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "PrintHello") .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)) .AddBodyStatements( SyntaxFactory.ExpressionStatement( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("Console"), SyntaxFactory.IdentifierName("WriteLine"))) .AddArgumentListArguments( SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("Hi from my dynamic class!")))) ) ); // 生成代码文本 var code = classSyntax.NormalizeWhitespace().ToFullString(); // 保存到.cs文件 string codePath = Path.Combine(outputDir, "MyDynamicClass.cs"); File.WriteAllText(codePath, code);
情况2:已有的动态程序集——提取IL或用Roslyn转译
如果是已经生成好的程序集,没有同步保存源代码,.NET内置API没法直接把IL转成漂亮的C#,但你可以:
- 提取IL代码:通过
MethodBody.GetILAsByteArray()获取方法的IL字节,虽然可读性差,但也是代码的一种形式; - 用Roslyn的高级API解析程序集元数据,手动生成C#代码:这需要对Roslyn和.NET元数据有一定了解,比较繁琐,但完全可以做到,不需要外部工具。
另外,如果你想生成完整的.NET项目,只要手动创建一个.csproj文件(就是个XML),把生成的.cs文件放进去就行,比如:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> </PropertyGroup> </Project>
把这个内容保存成DynamicProject.csproj放到之前的DynamicProject文件夹里,就构成一个可以直接编译运行的项目了。
总结
- 保存动态程序集到磁盘:完全没问题,Reflection.Emit自带支持;
- 生成带代码的项目:自己生成的话同步用Roslyn写源代码最方便;已有程序集的话,不用外部工具可以提取IL,转C#需要用Roslyn做一些解析工作。
内容的提问来源于stack exchange,提问作者Youssef ab
相关产品推荐
相关产品推荐

