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

F#自定义类型提供者:DefineStaticParameters实例化函数多次调用问题

解决F# CheckedRegexProvider中InstantiationFunction多次调用的问题

我之前在开发F#类型提供者时也碰到过一模一样的问题——DefineStaticParameters里的实例化函数被反复调用,不仅浪费性能,还可能导致一些奇怪的类型重复问题。针对你的CheckedRegexProvider,咱们可以通过类型缓存来彻底解决这个问题:

核心思路

类型提供者的InstantiationFunction会在编译器需要解析类型的场景下被触发(比如IDE实时刷新、项目编译、代码中多次引用同类型时)。解决的关键就是把已经生成的类型缓存起来,相同参数请求直接返回缓存实例,避免重复创建。

具体实现步骤

1. 加入线程安全的缓存容器

在你的CheckedRegexProvider类内部,定义一个ConcurrentDictionary来缓存生成的类型(用正则表达式pattern作为key,因为不同pattern对应不同的类型):

open System.Collections.Concurrent

[<TypeProvider>]
type public CheckedRegexProvider() as this =
    inherit TypeProviderForNamespaces()
    
    // 线程安全的类型缓存:key是正则表达式pattern,value是生成的ProvidedTypeDefinition
    private let typeCache = ConcurrentDictionary<string, ProvidedTypeDefinition>()
    
    // 原有代码:获取程序集、命名空间等
    let thisAssembly = Assembly.GetExecutingAssembly()
    let rootNamespace = "Samples.FSharp.RegexTypeProvider"
    let baseTy = typeof<obj>
    let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]

2. 修改实例化函数,优先使用缓存

调整InstantiationFunction的逻辑,先检查缓存中是否存在对应pattern的类型,存在则直接返回,不存在再执行生成逻辑并加入缓存:

let instantiationFunction (typeName: string) (args: obj[]) =
    let pattern = args.[0] :?> string
    
    // 使用GetOrAdd原子操作,确保线程安全且只生成一次
    typeCache.GetOrAdd(pattern, fun targetPattern ->
        // 这里放你原来生成regexTy的代码
        let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, typeName, baseTy)
        
        // 继续添加你的类型成员、方法等逻辑(比如匹配方法、分组属性等)
        // 示例:添加一个Match方法
        let matchMethod = ProvidedMethod(
            "Match", 
            [ProvidedParameter("input", typeof<string>)], 
            typeof<bool>)
        matchMethod.InvokeCode <- fun [input] -> <@@ System.Text.RegularExpressions.Regex.IsMatch(%%input, targetPattern) @@>
        regexTy.AddMember(matchMethod)
        
        regexTy)

3. 可选优化:标准化缓存Key

如果你的场景中存在语义相同但写法不同的正则表达式(比如带无关空格、大小写差异但不影响匹配逻辑的pattern),可以对pattern做标准化处理后再作为缓存key,进一步减少重复生成:

// 示例:标准化pattern——去掉空格、统一为小写(根据你的需求调整)
let normalizePattern (pattern: string) =
    pattern.Replace(" ", "").ToLowerInvariant()

// 在GetOrAdd时使用标准化后的key
typeCache.GetOrAdd(normalizePattern pattern, fun _ ->
    // 生成类型的逻辑...
)

为什么这能解决问题?

通过缓存,同一个正则表达式pattern对应的类型只会被生成一次,后续所有对该类型的请求都会直接返回缓存中的实例,彻底避免了InstantiationFunction被重复调用的问题,同时还能提升编译器和IDE的响应速度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:58:31