F#3.0新功能TypeProvider —— 定义自己的TP

关于F#3.0的新功能Type provider,微软为我们提供了内置的6种type providers,用来处理不同数据存储结构.但是有些情况这些内置的方法并不能满足需求,因此就需要用户自己定义适合自己需求的TP。

那么在自定义TP前,应该要考虑是不是真的需要自定义TP:

  1.   是否有一不受程序逻辑影响,不经常变动的schema(比如数据库由表、记录、字段、索引等组成)
  2.   是否经常需要使用到此TP
  3.   当程序运行时,此schema是否经常变动

Ok,在定义自己的TP前,需要去F# sample pack中下载两个源文件:SampleProviders -> Shared,然后在你的F#库文件中将其加入,效果如下:

其中.fsi文件就相当于一个C#中的Interface, ProvidedTypes-head.fs就是那个.fsi文件的具体实现。

namespace Zack.Fsharp.MyTP
open System
open Samples.FSharp.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices
open Microsoft.FSharp.Quotations
open System.Reflection

[<TypeProvider>]
type MyTP() as this =
    class
        //此基类位于head.fs文件中
        inherit TypeProviderForNamespaces()
        
        //这个np(namespace)将包含此TP中定义的所有类和方法
        let np = "Zack.Fsharp.TypeProviders"
        //获取当前运行程序集
        let asm = System.Reflection.Assembly.GetExecutingAssembly()

        let baseType = Some typeof<string>
        //定义类tys,并将此类在上面定义的np中的名字定义为ZackDfd,将此类放入asm程序集
        let tys = ProvidedTypeDefinition(asm,np,"ZackDfd",baseType)

        //定义一个构造函数
        let ctor = ProvidedConstructor(
                        parameters = [],
                        //此处的InvokeCode为一个匿名函数,函数体为一个Quotation表达式,这里默认的构造函数设为基类的构造函数
                        InvokeCode = fun arg -> <@@ () @@>)
        
        //自定义一个函数
        let dfnmethod = ProvidedMethod(
                            //函数名
                            methodName = "Say",
                            //此函数接受的参数列表,此例中只接受一个string类型的参数,参数相当于 Say(sA : string)
                            parameters = [ProvidedParameter("sA",typeof<System.String>)],
                            //函数返回值类型
                            returnType = typeof<obj>,
                            //函数体为一个Quotation表达式,其中%%args.[1]即为参数列表中的第一个,当然,此例只有一个参数,而args.[0]这表示调用此方法的实例
                            InvokeCode =(fun args -> <@@ "Hi:" + ( %%args.[1] : System.String) @@>)
                           )
        //将刚才定义的构造函数付给类tys
        do tys.AddMember ctor
        //将方法加入类中
        do tys.AddMember dfnmethod
        //将类加到命名空间
        do this.AddNamespace(np,[tys])
    end
[<assembly:TypeProviderAssembly>]
do()

编译好此库,接下来就是测试一下,新建一个Script文件,如下:

向Test.fsx中加入代码:

#r @"C:\Users\ZSH\Documents\Visual Studio 2012\Projects\MyTP\MyTP\bin\Debug\MyTP.dll"
type T = Zack.Fsharp.TypeProviders.ZackDfd
let c = new T()
let str = c.Say("asdf")

上面的#r后面路径需要自己改一下,或者用.\bin....这样的,但是需要此脚本文件的位置与Bin同级

Ok,下面是测试结果(选中全部脚本中的代码,按 alt+enter):

type T = Zack.Fsharp.TypeProviders.ZackDfd
val c : T = null
val str : obj = "Hi:asdf"

Ok ,最初级阶段搞定,接下来还有很多要做的:),才能真正成为一个可用的TP。

接下来 将写些关于Quotation Expression 和更深入的TP内容。

如果你想看英文资料:http://msdn.microsoft.com/en-us/library/hh361034.aspx

posted @ 2012-11-17 18:55  ZackZhou  阅读(1653)  评论(0编辑  收藏  举报