语义API相同但无共同接口/父类的C#类,能否复用同一代码?
Absolutely! Even though ModelA and ModelB are generated as separate classes with no shared inheritance or interface, there are several solid approaches to handle them with the same code logic. Let’s break down the most practical options:
1. 使用dynamic类型快速实现
这是最直接的方式,不需要额外代码,适合快速验证或者逻辑简单的场景。利用C#的dynamic绕过编译时类型检查,直接访问两个类共有的属性:
public void ProcessSharedModel(dynamic model) { // 访问两个类都有的字段 Console.WriteLine($"ID: {model.Id}, Name: {model.Name}"); // 执行其他共同逻辑 model.LastUpdated = DateTime.Now; } // 使用时直接传入ModelA或ModelB实例 ProcessSharedModel(new ModelA { Id = 1, Name = "Test A" }); ProcessSharedModel(new ModelB { Id = 2, Name = "Test B" });
优缺点:实现快,代码简洁;但没有编译时类型检查,如果后续XSD变更导致某个类的属性名变化,只有运行时才会报错。
2. 反射实现通用处理
如果需要更严谨的控制(比如验证属性是否存在),可以用反射来统一处理。这种方式适合需要在运行时确认属性存在的场景:
public void ProcessModelWithReflection(object model) { var type = model.GetType(); // 获取共有的属性 var idProp = type.GetProperty("Id"); var nameProp = type.GetProperty("Name"); if(idProp != null && nameProp != null) { var id = idProp.GetValue(model); var name = nameProp.GetValue(model); Console.WriteLine($"ID: {id}, Name: {name}"); // 设置属性值 var lastUpdatedProp = type.GetProperty("LastUpdated"); lastUpdatedProp?.SetValue(model, DateTime.Now); } }
优缺点:可以做运行时的属性校验,兼容性强;但反射性能略低,代码相对繁琐。
3. 适配器模式(类型安全的最优解)
如果你的项目需要长期维护,推荐用适配器模式来实现类型安全的统一处理。步骤如下:
- 定义一个公共接口,包含两个类的所有共享API:
public interface ISharedModel { int Id { get; set; } string Name { get; set; } DateTime LastUpdated { get; set; } // 其他共享字段... }
- 为
ModelA和ModelB分别创建适配器类:
public class ModelAAdapter : ISharedModel { private readonly ModelA _model; public ModelAAdapter(ModelA model) => _model = model; public int Id { get => _model.Id; set => _model.Id = value; } public string Name { get => _model.Name; set => _model.Name = value; } public DateTime LastUpdated { get => _model.LastUpdated; set => _model.LastUpdated = value; } // 实现其他共享属性... } public class ModelBAdapter : ISharedModel { private readonly ModelB _model; public ModelBAdapter(ModelB model) => _model = model; public int Id { get => _model.Id; set => _model.Id = value; } public string Name { get => _model.Name; set => _model.Name = value; } public DateTime LastUpdated { get => _model.LastUpdated; set => _model.LastUpdated = value; } // 实现其他共享属性... }
- 之后就可以基于
ISharedModel编写统一的处理逻辑:
public void ProcessModel(ISharedModel model) { Console.WriteLine($"ID: {model.Id}, Name: {model.Name}"); model.LastUpdated = DateTime.Now; // 其他统一逻辑... } // 使用时包装实例 ProcessModel(new ModelAAdapter(new ModelA { Id = 1, Name = "Test A" })); ProcessModel(new ModelBAdapter(new ModelB { Id = 2, Name = "Test B" }));
优缺点:完全类型安全,编译时就能发现错误,代码可维护性高;需要编写额外的适配器代码,适合大型或长期维护的项目。
4. 映射到统一DTO
如果已经在使用对象映射库(比如AutoMapper),可以把ModelA和ModelB都映射到一个统一的DTO类,然后处理这个DTO:
// 定义统一DTO public class SharedModelDto { public int Id { get; set; } public string Name { get; set; } public DateTime LastUpdated { get; set; } } // 配置AutoMapper映射 var config = new MapperConfiguration(cfg => { cfg.CreateMap<ModelA, SharedModelDto>(); cfg.CreateMap<ModelB, SharedModelDto>(); }); var mapper = config.CreateMapper(); // 处理逻辑 public void ProcessDto(SharedModelDto dto) { Console.WriteLine($"ID: {dto.Id}, Name: {dto.Name}"); dto.LastUpdated = DateTime.Now; // 后续可以把DTO映射回原模型(如果需要) } // 使用时映射 var modelA = new ModelA { Id = 1, Name = "Test A" }; var dtoA = mapper.Map<SharedModelDto>(modelA); ProcessDto(dtoA); var modelB = new ModelB { Id = 2, Name = "Test B" }; var dtoB = mapper.Map<SharedModelDto>(modelB); ProcessDto(dtoB);
优缺点:利用现有映射库减少重复代码,适合已有映射体系的项目;需要依赖第三方库,且如果需要回写原模型,要额外处理映射。
内容的提问来源于stack exchange,提问作者Geesh_SO

