如何将导入的System.Diagnostics.Process转为自定义ProcessChild子类?
问题描述
应用关闭后需要记住外部进程列表,因此将进程PID保存到文件,启动时重新导入。当使用System.Diagnostics.Process类型时,可通过Process.GetProcessById直接恢复进程实例,代码示例如下:
public List<Process> GetKnownProcesses() { int[] saved_ids = { 1, 2, 3 }; List<Process> known_processes = new List<Process>(); foreach (int id in saved_ids) { known_processes.Add(Process.GetProcessById(id)); // 为简化示例省略异常处理 } return known_processes; }
但当前需要使用Process的子类ProcessChild(用于扩展功能),直接向下转换Process实例到ProcessChild不可行,且复制父类只读属性也无法实现,代码报错如下:
public class ProcessChild : Process { // 扩展功能的代码 } public List<ProcessChild> GetKnownProcesses() { int[] saved_ids = { 1, 2, 3 }; List<ProcessChild> known_processes = new List<ProcessChild>(); foreach (int id in saved_ids) { known_processes.Add(Process.GetProcessById(id)); // 错误:需向下转换,但无法直接实现 } return known_processes; }
寻求无需自行编写完整GetProcessById逻辑的高效解决方案,是否有设置属性或调用更新方法的替代方式?
解决方案
1. 使用组合而非继承(推荐)
放弃继承Process,改为将Process作为ProcessChild的内部属性,通过封装来扩展功能。这种方式符合SOLID原则,避免继承带来的类型转换问题:
public class ProcessChild { // 封装原生Process实例 public Process UnderlyingProcess { get; } // 自定义扩展方法/属性 public string CustomInfo => $"Process {UnderlyingProcess.Id}: {UnderlyingProcess.ProcessName}"; public ProcessChild(Process process) { UnderlyingProcess = process ?? throw new ArgumentNullException(nameof(process)); } } // 导入进程的方法 public List<ProcessChild> GetKnownProcesses() { int[] saved_ids = { 1, 2, 3 }; List<ProcessChild> known_processes = new List<ProcessChild>(); foreach (int id in saved_ids) { var process = Process.GetProcessById(id); known_processes.Add(new ProcessChild(process)); } return known_processes; }
2. 通过反射复制属性(兼容继承场景)
如果必须保留继承关系,可以通过反射将Process实例的属性(包括只读属性对应的内部字段)复制到ProcessChild实例中。注意这种方式依赖.NET的内部实现细节,可能在版本更新时失效:
public class ProcessChild : Process { // 扩展功能的代码 } public static class ProcessExtensions { public static ProcessChild ToProcessChild(this Process process) { if (process == null) throw new ArgumentNullException(nameof(process)); var child = new ProcessChild(); var processType = typeof(Process); // 遍历所有实例属性(包括非公开) foreach (var prop in processType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (prop.CanWrite) { // 可写属性直接赋值 prop.SetValue(child, prop.GetValue(process)); } else { // 只读属性尝试修改对应的私有字段(命名约定为_PropertyName) var field = processType.GetField($"_{prop.Name}", BindingFlags.Instance | BindingFlags.NonPublic); field?.SetValue(child, field.GetValue(process)); } } return child; } } // 导入进程的方法 public List<ProcessChild> GetKnownProcesses() { int[] saved_ids = { 1, 2, 3 }; List<ProcessChild> known_processes = new List<ProcessChild>(); foreach (int id in saved_ids) { var process = Process.GetProcessById(id); known_processes.Add(process.ToProcessChild()); } return known_processes; }
3. 封装静态获取方法
在ProcessChild中封装静态方法,内部调用Process.GetProcessById并通过反射初始化子类实例,把转换逻辑集中在子类内部:
public class ProcessChild : Process { // 扩展功能的代码 public static new ProcessChild GetProcessById(int id) { var baseProcess = Process.GetProcessById(id); var child = new ProcessChild(); var processType = typeof(Process); // 复制属性逻辑同反射方案 foreach (var prop in processType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (prop.CanWrite) { prop.SetValue(child, prop.GetValue(baseProcess)); } else { var field = processType.GetField($"_{prop.Name}", BindingFlags.Instance | BindingFlags.NonPublic); field?.SetValue(child, field.GetValue(baseProcess)); } } return child; } } // 导入进程的方法 public List<ProcessChild> GetKnownProcesses() { int[] saved_ids = { 1, 2, 3 }; List<ProcessChild> known_processes = new List<ProcessChild>(); foreach (int id in saved_ids) { known_processes.Add(ProcessChild.GetProcessById(id)); } return known_processes; }
方法对比
- 组合方式:最稳定、易维护,无版本兼容风险,优先推荐。
- 反射方式:兼容继承场景,但依赖内部实现,维护成本高,仅在必须继承时使用。
- 封装静态方法:和反射方式类似,但逻辑集中在子类,代码更整洁,同样存在版本兼容风险。
内容的提问来源于stack exchange,提问作者mrpiggy
相关产品推荐
相关产品推荐

