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
相关产品推荐
相关产品推荐

