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

MEF:能否在类构造前有条件地选择导出实例?

我太懂这种重复操作的痛苦了——每个地方都写[ImportMany]再加过滤逻辑,简直是在复制粘贴垃圾代码。其实完全可以把逻辑集中起来,让业务代码只需要一个简单的[Import]就能拿到合适的调度器,下面是具体的实现方案:

第一步:给两个调度器实现添加元数据标记

首先我们定义一个元数据接口,用来区分测试和生产环境的实现,然后给两个实现加上带元数据的导出:

// 定义元数据接口,用来标记调度器类型
public interface ISchedulerProviderMetadata
{
    bool IsTest { get; }
}

// 生产环境的调度器实现
[Export(typeof(ISchedulerProvider))]
[ExportMetadata("IsTest", false)]
public class ProductionSchedulerProvider : ISchedulerProvider
{
    // 这里写你的生产环境调度器逻辑,比如用Rx原生的调度器
    public IScheduler CurrentThread => Scheduler.CurrentThread;
    public IScheduler Dispatcher => Scheduler.Dispatcher;
    // 其他ISchedulerProvider成员的实现...
}

// 测试环境的调度器实现
[Export(typeof(ISchedulerProvider))]
[ExportMetadata("IsTest", true)]
public class TestSchedulerProvider : ISchedulerProvider
{
    // 这里写测试用的调度器逻辑,比如用TestScheduler
    public IScheduler CurrentThread => new TestScheduler();
    public IScheduler Dispatcher => new TestScheduler();
    // 其他ISchedulerProvider成员的实现...
}

第二步:创建一个“自动适配”的默认调度器

我们导出一个统一的ISchedulerProvider实例,它内部会根据当前环境自动选择测试或生产实现,把所有过滤逻辑都集中在这里:

[Export(typeof(ISchedulerProvider))]
public class DefaultSchedulerProvider : ISchedulerProvider
{
    private readonly ISchedulerProvider _actualProvider;

    // 用ImportMany注入所有带元数据的调度器实现
    [ImportingConstructor]
    public DefaultSchedulerProvider([ImportMany] IEnumerable<Lazy<ISchedulerProvider, ISchedulerProviderMetadata>> allProviders)
    {
        // 判断当前是否处于测试环境,你可以根据自己的需求修改这个逻辑
        bool isTestEnv = CheckIfInTestEnvironment();
        
        // 根据元数据筛选出对应的调度器
        _actualProvider = allProviders
            .First(provider => provider.Metadata.IsTest == isTestEnv)
            .Value;
    }

    // 把ISchedulerProvider的所有方法都委托给实际的调度器实现
    public IScheduler CurrentThread => _actualProvider.CurrentThread;
    public IScheduler Dispatcher => _actualProvider.Dispatcher;
    public IScheduler Immediate => _actualProvider.Immediate;
    public IScheduler NewThread => _actualProvider.NewThread;
    public IScheduler TaskPool => _actualProvider.TaskPool;
    public IScheduler ThreadPool => _actualProvider.ThreadPool;

    // 环境判断逻辑,示例:通过环境变量判断
    private bool CheckIfInTestEnvironment()
    {
        return Environment.GetEnvironmentVariable("APP_ENV")?.Equals("Test", StringComparison.OrdinalIgnoreCase) == true;
        // 你也可以换成其他判断方式:比如检查测试程序集是否加载、读取配置文件等
    }
}

第三步:业务代码直接导入即可

现在所有需要用到ISchedulerProvider的地方,只需要一个简单的[Import],完全不用管过滤逻辑:

public class UserDataService
{
    [Import]
    public ISchedulerProvider SchedulerProvider { get; set; }

    public void LoadUserData()
    {
        // 直接用SchedulerProvider,它会自动适配当前环境
        Observable.Timer(TimeSpan.FromSeconds(1), SchedulerProvider.TaskPool)
            .Subscribe(_ => Console.WriteLine("数据加载完成"));
    }
}

为什么这个方案更好?

  • 避免重复代码:所有环境判断和元数据过滤逻辑都集中在DefaultSchedulerProvider里,业务代码完全不用关心底层细节
  • 可维护性高:以后要修改环境判断逻辑,只需要改CheckIfInTestEnvironment这一个地方
  • 符合依赖注入原则:业务代码依赖的还是抽象的ISchedulerProvider,没有耦合到具体实现

内容的提问来源于stack exchange,提问作者ket

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:29:09