F#类型提供者报错:非泛型类型BasicProvider不可传入类型参数
看起来你踩了一个Type Provider入门阶段的常见坑——你混淆了类型提供器的实现类和它生成的可被业务代码调用的类型。
错误原因拆解
报错提示BasicProvider是非泛型类型、不能传入类型参数,这个提示完全准确:BasicProvider是继承自TypeProviderForNamespaces的类型提供器实现类,它的作用是生成供用户使用的类型,本身并不是可直接实例化的泛型类型。你真正需要调用的是在createTypes函数里定义的StringProvider,这才是带有静态参数(也就是你要传入的字符串)的目标类型。
具体修正步骤
修正测试脚本
把测试代码里的BasicProvider<"test">替换为StringProvider<"test">,同时注意命名空间的匹配——你原本把StringProvider放在了SimpleStringProvider.StringProvider子命名空间下,所以测试代码需要调整:#r @".\src\bin\Debug\netstandard2.0\SimpleStringProvider.dll" open SimpleStringProvider.StringProvider // 打开正确的命名空间 type X = StringProvider<"test"> // 使用生成的目标类型(可选)简化命名空间调用
如果你希望调用更简洁,可以调整类型提供器里的命名空间定义,把StringProvider直接放在SimpleStringProvider根命名空间下:
在类型提供器代码中,将let ns = "SimpleStringProvider.StringProvider"改为let ns = "SimpleStringProvider",这样测试脚本可以简化为:#r @".\src\bin\Debug\netstandard2.0\SimpleStringProvider.dll" open SimpleStringProvider type X = StringProvider<"test">
完整修正后的类型提供器代码(保留原核心逻辑)
module SimpleStringProvider open ProviderImplementation open ProviderImplementation.ProvidedTypes open Microsoft.FSharp.Core.CompilerServices open System.Reflection [<TypeProvider>] type BasicProvider (config : TypeProviderConfig) as this = inherit TypeProviderForNamespaces (config) let ns = "SimpleStringProvider" // 简化命名空间,方便业务代码调用 let asm = Assembly.GetExecutingAssembly() let staticParams = [ProvidedStaticParameter("value", typeof<string>)] let createTypes () = let myType = ProvidedTypeDefinition(asm, ns, "StringProvider", Some typeof<obj>) let instantiationFunction _ paramValues = let typeName = (paramValues |> Array.head).ToString() ProvidedTypeDefinition( asm, ns, typeName, Some typeof<obj> ) do myType.DefineStaticParameters( parameters = staticParams, instantiationFunction = instantiationFunction ) myType do this.AddNamespace(ns, [createTypes()]) [<assembly:TypeProviderAssembly>] do ()
验证效果
重新编译类型提供器后,运行修正后的测试脚本,报错就会消失。此时type X = StringProvider<"test">会生成一个名为test的类型,你可以继续给这个生成的类型添加属性、方法来丰富功能(比如你注释掉的MyProperty就可以重新启用)。
内容的提问来源于stack exchange,提问作者jkone27

