如何基于接收的ScheduleInfo子类执行不同操作(C#实现)
问题描述
我有如下C#数据类,希望CreateScheduledEventRequest的接收方在ScheduleInfo为RepeatingTask和OneShotTask时执行不同操作:
public record CreateScheduledEventRequest { public ScheduleInfo ScheduleInfo = new OneShotTask(); } public abstract record ScheduleInfo; public record RepeatingTask : ScheduleInfo { public TimeSpan RepeatEvery; } public record OneShotTask : ScheduleInfo { public DateTimeOffset RunAt; }
作为OOP设计练习,我不想通过类型检查(比如is/as关键字)实现该功能。我尝试为每个子类创建重载方法,但用基类调用时无法触发正确的重载。我想编写分别接收RepeatingTask和OneShotTask的方法,借助C#特性自动调度到正确实现,怀疑可用策略模式但担心过于繁琐。
解决方案
1. 访问者模式(贴合OOP开闭原则的标准方案)
访问者模式是处理这类“基类分发子类逻辑”场景的经典方案,完全无需类型检查,且符合开闭原则——新增ScheduleInfo子类时,只需扩展访问者接口和实现,无需修改原有类结构。
步骤1:改造基类与定义访问者接口
public abstract record ScheduleInfo { // 定义接受访问者的抽象方法 public abstract void Accept(IScheduleInfoVisitor visitor); } // 访问者接口:每个ScheduleInfo子类对应一个Visit方法 public interface IScheduleInfoVisitor { void Visit(RepeatingTask repeatingTask); void Visit(OneShotTask oneShotTask); }
步骤2:子类实现Accept方法
让每个子类主动调用访问者对应的方法,完成类型分发:
public record RepeatingTask : ScheduleInfo { public TimeSpan RepeatEvery; public override void Accept(IScheduleInfoVisitor visitor) { visitor.Visit(this); } } public record OneShotTask : ScheduleInfo { public DateTimeOffset RunAt; public override void Accept(IScheduleInfoVisitor visitor) { visitor.Visit(this); } }
步骤3:实现具体访问者(业务逻辑)
编写处理不同任务的专属逻辑:
public class ScheduleProcessor : IScheduleInfoVisitor { public void Visit(RepeatingTask repeatingTask) { // 重复任务的专属逻辑:比如注册定时重复触发 Console.WriteLine($"创建重复任务,执行间隔:{repeatingTask.RepeatEvery.TotalMinutes}分钟"); } public void Visit(OneShotTask oneShotTask) { // 单次任务的专属逻辑:比如注册指定时间触发 Console.WriteLine($"创建单次任务,执行时间:{oneShotTask.RunAt:yyyy-MM-dd HH:mm:ss}"); } }
使用示例
var request = new CreateScheduledEventRequest { ScheduleInfo = new RepeatingTask { RepeatEvery = TimeSpan.FromMinutes(10) } }; var processor = new ScheduleProcessor(); request.ScheduleInfo.Accept(processor); // 自动调用RepeatingTask的处理逻辑
2. 模式匹配+重载方法(轻量实用方案)
如果觉得访问者模式的结构太复杂,可以用C#的模式匹配简化分发逻辑,同样无需显式类型检查:
public class ScheduleProcessor { // 对外统一入口 public void Process(ScheduleInfo scheduleInfo) { // 模式匹配自动匹配到对应重载方法 switch (scheduleInfo) { case RepeatingTask rt: Process(rt); break; case OneShotTask ost: Process(ost); break; default: throw new ArgumentOutOfRangeException(nameof(scheduleInfo), "不支持的任务类型"); } } // 重复任务处理逻辑 public void Process(RepeatingTask repeatingTask) { Console.WriteLine($"处理重复任务,间隔:{repeatingTask.RepeatEvery}"); } // 单次任务处理逻辑 public void Process(OneShotTask oneShotTask) { Console.WriteLine($"处理单次任务,时间:{oneShotTask.RunAt}"); } }
使用示例
var processor = new ScheduleProcessor(); processor.Process(request.ScheduleInfo); // 自动分发到对应方法
3. 动态类型(最简快速方案)
利用C#的dynamic类型跳过编译时检查,运行时自动匹配重载方法,代码量最少,但存在轻微性能损耗:
public class ScheduleProcessor { public void Process(dynamic scheduleInfo) { // 运行时根据真实类型匹配对应方法 ProcessInternal(scheduleInfo); } private void ProcessInternal(RepeatingTask repeatingTask) { Console.WriteLine($"重复任务处理:{repeatingTask.RepeatEvery}"); } private void ProcessInternal(OneShotTask oneShotTask) { Console.WriteLine($"单次任务处理:{oneShotTask.RunAt}"); } }
使用示例
processor.Process(request.ScheduleInfo);
注意:该方案会失去编译时类型检查,若新增
ScheduleInfo子类但未添加对应ProcessInternal方法,会在运行时抛出异常,适合快速验证或小型项目。
内容的提问来源于stack exchange,提问作者Thomas Stå Pettersen
相关产品推荐
相关产品推荐

