如何在CS文件中动态添加代码?WPF应用DLL类适配问题咨询
嘿,这个问题我太熟了——做动态代码生成的时候最容易碰到这种“依赖不确定”的坑,核心思路就是把编译时的硬依赖转化为运行时的动态判断,给你几个落地的方案:
1. 反射(Reflection):运行时动态检测类型
这是最直接的办法:编译时不直接引用那些可能缺失的类,而是在运行时用反射加载DLL,检查目标类是否存在,存在再生成对应代码。
举个简单的例子:
// 加载目标DLL Assembly targetAssembly = Assembly.LoadFrom("YourDllPath.dll"); // 尝试获取目标类型 Type targetType = targetAssembly.GetType("Namespace.YourOptionalClass"); if (targetType != null) { // 类存在,生成对应的JavaScript代码 GenerateJSCodeForType(targetType); } else { // 类不存在,跳过或生成默认逻辑 GenerateFallbackJSCode(); }
这种方式完全摆脱了编译时依赖,适合大多数动态检测场景,缺点是反射性能略低,但对代码生成工具来说基本可以忽略。
2. 条件编译符号:按需包含代码
如果那些可选类是“已知可选”的(比如你知道某些DLL版本有这个类,某些没有),可以用条件编译符号来隔离代码:
首先在项目属性的“生成”选项里,给包含目标类的DLL版本添加编译符号(比如HAS_OPTIONAL_CLASS),然后在代码里这么写:
#if HAS_OPTIONAL_CLASS // 只有定义了符号时,这段代码才会被编译 GenerateJSCodeForOptionalClass(); #else // 没有符号时执行降级逻辑 GenerateDefaultJSCode(); #endif
这个方案的优点是编译时就剔除无用代码,性能最优,但缺点是需要手动管理编译符号,适合场景固定的情况。
3. 抽象层+依赖注入:解耦依赖
如果你的代码生成逻辑比较复杂,可以给可选类的生成逻辑定义抽象接口,然后针对“有类”和“无类”的情况分别实现:
// 定义抽象接口 public interface IJSCodeGenerator { string GenerateCode(); } // 当类存在时的实现 public class OptionalClassJSGenerator : IJSCodeGenerator { public string GenerateCode() { // 基于OptionalClass生成代码 return "// 针对OptionalClass的JS代码"; } } // 类不存在时的空实现 public class FallbackJSGenerator : IJSCodeGenerator { public string GenerateCode() { // 返回默认或空代码 return "// 默认逻辑代码"; } }
然后在运行时判断DLL是否包含目标类,选择注入对应的实现:
IJSCodeGenerator generator; if (IsOptionalClassExists()) // 这里用反射判断类是否存在 { generator = new OptionalClassJSGenerator(); } else { generator = new FallbackJSGenerator(); } string jsCode = generator.GenerateCode();
这个方案适合大型项目,能很好地解耦代码,符合开闭原则。
4. 动态编译(Roslyn):把生成的代码当字符串处理
如果你的JS代码生成逻辑是基于目标类的结构(比如属性、方法),可以把生成的C#代码(或者直接JS代码)作为字符串,用Roslyn动态编译执行,这样即使引用了不存在的类,也只会在运行时报错,不会影响主程序编译:
// 比如生成一段依赖OptionalClass的代码字符串 string codeSnippet = @" using YourDllNamespace; public class TempGenerator { public static string Generate() { return ""var obj = new "" + nameof(OptionalClass) + "";""; } }"; // 用Roslyn编译这段代码 var compilation = CSharpCompilation.Create("TempAssembly") .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)) .AddReferences(MetadataReference.CreateFromFile(targetAssembly.Location)) .AddSyntaxTrees(CSharpSyntaxTree.ParseText(codeSnippet)); // 检查编译错误 var diagnostics = compilation.GetDiagnostics(); if (!diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error)) { // 编译成功,执行生成逻辑 using var ms = new MemoryStream(); compilation.Emit(ms); ms.Seek(0, SeekOrigin.Begin); var tempAssembly = Assembly.Load(ms.ToArray()); var tempType = tempAssembly.GetType("TempGenerator"); var generateMethod = tempType.GetMethod("Generate"); string jsCode = (string)generateMethod.Invoke(null, null); } else { // 编译失败(说明类不存在),执行降级逻辑 jsCode = "// 默认JS代码"; }
这个方案灵活性最高,适合需要动态生成复杂代码的场景,但实现起来稍复杂。
你可以根据自己的项目规模和需求选最合适的方案,反射和抽象层这两个是我最常用的~
内容的提问来源于stack exchange,提问作者Maulik Modi

