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

为IService接口的GetDescriptorsByGroup方法实现缓存方案

解决方案

要实现GetDescriptorsByGroup的懒加载缓存,同时避免在所有IService实现类中重复编写逻辑、不使用基类,最佳方案是基于接口默认方法+ConditionalWeakTable实现实例级安全缓存,既解决内存泄漏问题,又让所有实现类自动复用缓存逻辑。

具体实现

1. 改造IService接口

替换原有的GetDescriptorsByGroup默认方法,加入缓存逻辑:

using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;

public interface IService
{
    IReadOnlyList<Descriptor> GetDescriptors();

    // 保留原有辅助方法
    IEnumerable<Descriptor> GetDescriptorsByName(string name) => GetDescriptors().Where(e => e.Name == name);

    // 带懒加载缓存的分组查询
    IEnumerable<Descriptor> GetDescriptorsByGroup(string group)
    {
        // 用ConditionalWeakTable存储每个IService实例的分组缓存,避免内存泄漏
        static readonly ConditionalWeakTable<IService, IReadOnlyDictionary<string, IEnumerable<Descriptor>>> _groupCache = new();

        // 首次调用时构建缓存,后续直接读取
        var groupMap = _groupCache.GetValue(this, service => 
            service.GetDescriptors()
                   .GroupBy(d => d.Group)
                   .ToDictionary(g => g.Key, g => (IEnumerable<Descriptor>)g.ToList(), StringComparer.OrdinalIgnoreCase));

        // 返回指定分组结果,无匹配时返回空集合
        return groupMap.TryGetValue(group, out var descriptors) ? descriptors : Enumerable.Empty<Descriptor>();
    }
}

2. 核心逻辑说明

  • ConditionalWeakTable的优势:这是.NET提供的弱引用字典,当IService实例被GC回收时,对应的缓存条目会自动清理,不会造成内存泄漏,比普通静态字典更安全。
  • 懒加载特性:只有第一次调用GetDescriptorsByGroup时,才会执行GroupBy分组操作并缓存结果,后续调用直接从缓存字典取值,完全符合"结果永不改变"的需求。
  • 无侵入性:所有已实现IService的类(包括ServiceA、ServiceB和Mock类)无需任何修改,自动继承该带缓存的方法。
  • 线程安全:ConditionalWeakTable.GetValue本身是线程安全的,多线程同时调用时只会执行一次缓存构建逻辑。

3. 调用验证

原有调用逻辑无需修改,缓存会自动生效:

internal class Program
{
    static void Main(string[] args)
    {
        IService service = new ServiceA();

        // 第一次调用:触发缓存构建
        var group1 = service.GetDescriptorsByGroup("Group1");
        // 第二次调用:直接读取缓存
        var group1Again = service.GetDescriptorsByGroup("Group1");
    }
}

备选方案:扩展方法实现缓存

如果不想修改原接口,也可以用扩展方法实现,同样搭配ConditionalWeakTable避免内存泄漏:

public static class IServiceExtensions
{
    private static readonly ConditionalWeakTable<IService, IReadOnlyDictionary<string, IEnumerable<Descriptor>>> _groupCache = new();

    public static IEnumerable<Descriptor> GetDescriptorsByGroup(this IService service, string group)
    {
        var groupMap = _groupCache.GetValue(service, s => 
            s.GetDescriptors()
              .GroupBy(d => d.Group)
              .ToDictionary(g => g.Key, g => (IEnumerable<Descriptor>)g.ToList(), StringComparer.OrdinalIgnoreCase));

        return groupMap.TryGetValue(group, out var descriptors) ? descriptors : Enumerable.Empty<Descriptor>();
    }

    // 保留原有扩展方法
    public static IEnumerable<Descriptor> GetDescriptorsCount(this IService service, int count) => service.GetDescriptors().Where(e => e.Count == count);
    public static IEnumerable<string> GetAllDependencies(this IService service) => service.GetDescriptors().SelectMany(e => e.Dependencies);
}

这种方式需要删除原接口中的GetDescriptorsByGroup默认方法,让扩展方法覆盖实现,同样无需修改任何IService实现类。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:55:17