You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于接收的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:33:12