Výuka: Vytváření typ zprostředkovatele (F#)

Mechanismus poskytovatele typu v jazyce F# 3.0 je významnou součástí jeho podpory pro informačně rozsáhlé programování.Tento kurz vysvětluje, jak vytvořit vlastní poskytovatele typu, pomocí vývoje několika jednoduchých poskytovatelů typu ke znázornění základních pojmů.Další informace o mechanismu poskytovatele typu v jazyce F# naleznete v části Typ zprostředkovatele.

Jazyk F# 3.0 obsahuje několik předdefinovaných poskytovatelů typu pro běžně používané služby Internetu a podnikové datové služby.Tito poskytovatelé typu umožňují jednoduchý a pravidelný přístup k relačním databázím SQL a službám OData a WSDL založeným na síti.Tito poskytovatelé také podporují použití dotazů F# LINQ proti těmto zdrojům dat.

Případně lze vytvořit vlastní poskytovatele typu nebo odkázat na poskytovatele typu vytvořené jinými uživateli.Organizace by mohla mít například datovou službu, která poskytuje velký a rostoucí počet pojmenovaných souborů dat, každý s vlastním stabilním schématem dat.Lze vytvořit poskytovatele typu, který čte schémata a pevně daným způsobem prezentuje programátorovi aktuální soubory dat.

Než začnete:

Mechanismus poskytovatele typu je primárně určen pro vnesení stabilních dat a prostorů informací služby do programovací zkušenosti jazyka F#.

Tento mechanismus není určen pro vnesení informačních prostorů, jejichž schéma se mění při provádění programu způsoby, které jsou relevantní pro programovou logiku.Mechanismus také není určen pro vnitřní jazyk meta programování, přestože tato oblast obsahuje některá platná použití.Tento mechanismus by měl být použit pouze v případě potřeby a tam, kde vývoj poskytovatele typu přináší velmi vysokou hodnotu.

Je třeba vyhnout se psaní poskytovatele typu tam, kde není k dispozici schéma.Podobně je třeba vyhnout se psaní poskytovatele typu tam, kde by postačovala běžná (nebo i existující) knihovna technologie .NET.

Než začnete, mohli byste se ptát na následující otázky:

  • Máte schéma zdroje informací?Pokud ano, co je mapování do systému typu jazyka F# a technologie .NET?

  • Lze použít existující (dynamicky zadávané) rozhraní API jako výchozí bod implementace?

  • Budete vy a vaše organizace poskytovatele typu dostatečně využívat, aby se jeho napsání vyplatilo?Vyhovovala by vašim potřebám běžná knihovna technologie .NET?

  • Nakolik se změní schéma?

  • Změní se během kódování?

  • Změní se mezi relacemi kódování?

  • Změní se při provádění programu?

Poskytovatelé typu jsou nejvhodnější pro situace, kdy je schéma při běhu a během doby existence zkompilovaného kódu stabilní.

Poskytovatel jednoduchého typu

Tato ukázka je Samples.HelloWorldTypeProvider v adresáři SampleProviders\Providers z Ukázkového balíku jazyka F# 3.0 na webových stránkách Codeplex.Poskytovatel zpřístupní "prostor typu" obsahující 100 vymazaných typů, jak ukazuje následující kód, použitím syntaxe podpisu jazyka F# a vynecháním podrobností o všem kromě Type1.Další informace o vymazaných typech naleznete v části Podrobnosti o vymazaných poskytovaných typech dále v tomto tématu.

namespace Samples.HelloWorldTypeProvider

    type Type1 =
        /// This is a static property.
        static member StaticProperty : string

        /// This constructor takes no arguments.
        new : unit -> Type1

        /// This constructor takes one argument.
        new : data:string -> Type1

        /// This is an instance property.
        member InstanceProperty : int

        /// This is an instance method.
        member InstanceMethod : x:int -> char

        /// This is an instance property.
        nested type NestedType = 
            /// This is StaticProperty1 on NestedType.
            static member StaticProperty1 : string
            …
            /// This is StaticProperty100 on NestedType.
            static member StaticProperty100 : string

    type Type2 =
        …
    …

    type Type100 =
        …

Všimněte si, že sada poskytovaných typů a členů je staticky známa.V tomto příkladu není využita schopnost poskytovatelů poskytnout typy, které jsou závislé na schématu.Implementace poskytovatele typu je nastíněna v následujícím kódu a podrobnosti jsou uvedeny v dalších oddílech tohoto tématu.

Poznámka k upozorněníUpozornění

Mezi tímto kódem a online ukázkami mohou být některé drobné rozdíly v pojmenování.

namespace Samples.FSharp.HelloWorldTypeProvider

open System
open System.Reflection
open Samples.FSharp.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices
open Microsoft.FSharp.Quotations

// This type defines the type provider. When compiled to a DLL, it can be added
// as a reference to an F# command-line compilation, script, or project.
[<TypeProvider>]
type SampleTypeProvider(config: TypeProviderConfig) as this = 

    // Inheriting from this type provides implementations of ITypeProvider 
    // in terms of the provided types below.
    inherit TypeProviderForNamespaces()

    let namespaceName = "Samples.HelloWorldTypeProvider"
    let thisAssembly = Assembly.GetExecutingAssembly()

    // Make one provided type, called TypeN.
    let makeOneProvidedType (n:int) = 
        …
    // Now generate 100 types
    let types = [ for i in 1 .. 100 -> makeOneProvidedType i ] 

    // And add them to the namespace
    do this.AddNamespace(namespaceName, types)

[<assembly:TypeProviderAssembly>] 
do()

Pro použití tohoto poskytovatele je nutné otevřít samostatnou instanci z Visual Studio 2012, vytvořit skript jazyka F# a pak přidat pomocí #r odkaz na poskytovatele ze skriptu, jak ukazuje následující kód:

#r @".\bin\Debug\Samples.HelloWorldTypeProvider.dll"

let obj1 = Samples.HelloWorldTypeProvider.Type1("some data")

let obj2 = Samples.HelloWorldTypeProvider.Type1("some other data")

obj1.InstanceProperty
obj2.InstanceProperty

[ for index in 0 .. obj1.InstanceProperty-1 -> obj1.InstanceMethod(index) ]
[ for index in 0 .. obj2.InstanceProperty-1 -> obj2.InstanceMethod(index) ]

let data1 = Samples.HelloWorldTypeProvider.Type1.NestedType.StaticProperty35

Pak vyhledejte v oboru názvů Samples.HelloWorldTypeProvider typy, které vygeneroval poskytovatel typu.

Před novou kompilací poskytovatele se ujistěte, že jsou zavřeny všechny instance systému Visual Studio a komponenty F# Interactive, které používají poskytovatele DLL.Jinak dojde k chybě sestavení, protože výstup DLL bude uzamčen.

K ladění poskytovatele pomocí příkazů pro tisk vytvořte skript, který odhalí problém s poskytovatelem, a pak použijte následující kód:

fsc.exe -r:bin\Debug\HelloWorldTypeProvider.dll script.fsx

K ladění poskytovatele pomocí systému Visual Studio otevřete příkazový řádek systému Visual Studio s pověřeními pro správu a spusťte následující příkaz:

devenv.exe /debugexe fsc.exe -r:bin\Debug\HelloWorldTypeProvider.dll script.fsx

Případně otevřete systém Visual Studio, otevřete nabídku Ladění, zvolte Ladit/Připojit k procesu... a připojte k jinému procesu devenv, kde je upravován skript.Pomocí této metody lze snadněji vybrat konkrétní logiku v poskytovateli typu, a to interaktivním zadáváním výrazů do druhé instance (s plnou podporou technologie IntelliSense a dalšími funkcemi).

Pro lepší identifikaci chyb v generovaném kódu lze zakázat ladění Pouze můj kód.Informace o tom, jak povolit nebo zakázat tento prvek, naleznete v tématu Jak: Vstoupit do Pouze můj kód.Lze také nastavit zachycení výjimky na první pokus otevřením nabídky Ladění a následným zvolením Výjimky nebo zvolením klávesové zkratky Ctrl+Alt+E k otevření dialogového okna Výjimky.V tomto dialogovém okně pod Výjimky modulu Common Language Runtime zvolte zaškrtávací políčko Vyvolaný.

Hh361034.collapse_all(cs-cz,VS.110).gifImplementace poskytovatele typu

Tato část prochází hlavní části implementace poskytovatele typu.Nejprve definujte typ pro samotného vlastního poskytovatele typu:

[<TypeProvider>]
type SampleTypeProvider(config: TypeProviderConfig) as this =

Tento typ musí být veřejný a je třeba jej označit atributem TypeProvider, tak aby kompilátor rozpoznal poskytovatele typu, když samostatný projekt jazyka F# odkazuje na sestavení, které typ obsahuje.Parametr config je nepovinný, a pokud je přítomen, obsahuje informace o kontextové konfiguraci pro instanci poskytovatele typu, kterou kompilátor jazyka F# vytvoří.

Následuje implementace rozhraní ITypeProvider.V tomto případě se typ TypeProviderForNamespaces z rozhraní API ProvidedTypes použije jako základní typ.Tento typ pomocník může poskytnout omezenou kolekci nedočkavě poskytovaných oborů názvů, z nichž každý přímo obsahuje konečný počet pevných, nedočkavě poskytovaných typů.V této souvislosti poskytovatel nedočkavě generuje typy, i když nejsou potřebné nebo využité.

inherit TypeProviderForNamespaces()

Dále definujte místní privátní hodnoty, které určují obor názvů poskytovaných typů, a najděte sestavení poskytovatele typu samotného.Toto sestavení později slouží jako logicky nadřazený typ vymazaných typů, které jsou poskytovány.

let namespaceName = "Samples.HelloWorldTypeProvider"
let thisAssembly = Assembly.GetExecutingAssembly()

Dále vytvořte funkci pro poskytnutí každého z typů Typ1...Typ100.Tato funkce je podrobněji vysvětlena dále v tomto tématu.

let makeOneProvidedType (n:int) = …

Dále vygenerujte 100 poskytovaných typů:

let types = [ for i in 1 .. 100 -> makeOneProvidedType i ]

Dále přidejte typy jako poskytovaný obor názvů:

do this.AddNamespace(namespaceName, types)

Nakonec přidejte atribut sestavení, který indikuje, že vytváříte poskytovatele typu DLL:

[<assembly:TypeProviderAssembly>] 
do()

Hh361034.collapse_all(cs-cz,VS.110).gifPoskytování jednoho typu a jeho členů

Funkce makeOneProvidedType dělá skutečnou práci poskytování jednoho z typů.

let makeOneProvidedType (n:int) = 
        …

Tento krok vysvětluje implementaci této funkce.Nejprve vytvořte poskytovaný typ (například Typ1, pokud n = 1 nebo Type57, pokud n = 57).

    // This is the provided type. It is an erased provided type and, in compiled code, 
    // will appear as type 'obj'.
    let t = ProvidedTypeDefinition(thisAssembly,namespaceName,
                                       "Type" + string n,
                                       baseType = Some typeof<obj>)



Všimněte si následujících bodů:

  • Tento poskytovaný typ je vymazán.Protože je indikováno, že základní typ je obj, instance se zobrazí jako hodnoty typu obj ve zkompilovaném kódu.

  • Při zadávání nevnořeného typu je třeba určit sestavení a obor názvů.Pro vymazané typy by sestavení mělo být samotným sestavením poskytovatele typu.

Dále typu přidejte dokumentaci XML.Tato dokumentace je opožděna, tj. počítána na vyžádání, pokud to hostitelský kompilátor potřebuje.

t.AddXmlDocDelayed (fun () -> sprintf "This provided type %s" ("Type" + string n))

Dále typu přidejte poskytovanou statickou vlastnost:

let staticProp = ProvidedProperty(propertyName = "StaticProperty", 
                                  propertyType = typeof<string>, 
                                  IsStatic=true,
                                  GetterCode= (fun args -> <@@ "Hello!" @@>))

Získání této vlastnosti bude vždy vyhodnoceno na řetězec "Ahoj!".GetterCode používá pro vlastnost nabídku jazyka F#, která představuje kód, který hostitelský kompilátor generuje pro získání vlastnosti.Další informace o nabídkách naleznete v části Nabídky kódu (F#).

Přidejte vlastnosti dokumentaci XML.

staticProp.AddXmlDocDelayed(fun () -> "This is a static property")

Nyní připojte poskytovanou vlastnost k poskytovanému typu.Poskytovaný člen je nutné připojit pouze k jednomu jedinému typu.Jinak nebude člen nikdy přístupný.

t.AddMember staticProp

Nyní vytvořte poskytovaný konstruktor, který nepoužívá žádné parametry.

let ctor = ProvidedConstructor(parameters = [ ], 
                               InvokeCode= (fun args -> <@@ "The object data" :> obj @@>))

InvokeCode vrátí pro konstruktor nabídku jazyka F#, která představuje kód, který hostitelský kompilátor generuje při volání konstruktoru.Lze například použít následující konstruktor:

new Type10()

Instance poskytovaného typu bude vytvořena s podkladovými daty "Data objektu".Nabízený kód zahrnuje převod na obj, protože tento typ je výmazem tohoto poskytovaného typu (jak bylo zadáno při deklaraci poskytovaného typu).

Přidejte dokumentaci XML do konstruktoru a poskytovaný konstruktor přidejte do poskytovaného typu:

ctor.AddXmlDocDelayed(fun () -> "This is a constructor")

t.AddMember ctor

Vytvořte druhý poskytovaný konstruktor, který přijímá jeden parametr:

let ctor2 = 
    ProvidedConstructor(parameters = [ ProvidedParameter("data",typeof<string>) ], 
                        InvokeCode= (fun args -> <@@ (%%(args.[0]) : string) :> obj @@>))

InvokeCode pro konstruktor znovu vrátí nabídku jazyka F#, která představuje kód generovaný hostitelským kompilátorem pro volání do metody.Lze například použít následující konstruktor:

     new Type10("ten")

Instance poskytovaného typu je vytvořena s podkladovými daty "deset".Možná jste si již všimli, že funkce InvokeCode vrátí nabídku.Vstupem do této funkce je seznam výrazů, jeden na každý parametr konstruktoru.V tomto případě je výraz, který představuje hodnotu jednoho parametru, dostupný v args.[0].Kód volání do konstruktoru převede vrácenou hodnotu na vymazaný typ obj.Přidáním druhého poskytovaného konstruktoru typu je vytvořena vlastnost poskytované instance:

let instanceProp = 
    ProvidedProperty(propertyName = "InstanceProperty", 
                     propertyType = typeof<int>, 
                     GetterCode= (fun args -> 
                                       <@@ ((%%(args.[0]) : obj) :?> string).Length @@>))
instanceProp.AddXmlDocDelayed(fun () -> "This is an instance property")
t.AddMember instanceProp

Získání této vlastnosti vrátí délku řetězce, který je objektem reprezentace.Vlastnost GetterCode vrátí nabídku jazyka F#, která určuje kód, který hostitelský kompilátor vygeneruje k získání vlastnosti.Stejně jako InvokeCode, funkce GetterCode vrátí nabídku.Hostitelský kompilátor zavolá tuto funkci se seznamem argumentů.V tomto případě argumenty zahrnují pouze jeden výraz představující instanci, na které je volána metoda typu Get, ke které lze přistupovat pomocí args.[0]. Implementace GetterCode se pak spojí do výsledné nabídky na vymazaném typu obj a osazení se použije k přesvědčení mechanismu kompilátoru kontroly typů, že objektem je řetězec.Další část z makeOneProvidedType poskytuje metodě instance jeden parametr.

let instanceMeth = 
    ProvidedMethod(methodName = "InstanceMethod", 
                   parameters = [ProvidedParameter("x",typeof<int>)], 
                   returnType = typeof<char>, 
                   InvokeCode = (fun args -> 
                       <@@ ((%%(args.[0]) : obj) :?> string).Chars(%%(args.[1]) : int) @@>))

instanceMeth.AddXmlDocDelayed(fun () -> "This is an instance method")
// Add the instance method to the type.
t.AddMember instanceMeth 

Nakonec vytvořte vnořený typ, který obsahuje 100 vnořených vlastností.Vytvoření tohoto vnořeného typu a jeho vlastností je opožděno, tj. počítáno na vyžádání.

t.AddMembersDelayed(fun () -> 
    let nestedType = ProvidedTypeDefinition("NestedType",
                                            Some typeof<obj>

)

    nestedType.AddMembersDelayed (fun () -> 
        let staticPropsInNestedType = 
            [ for i in 1 .. 100 do
                 let valueOfTheProperty = "I am string "  + string i

                 let p = ProvidedProperty(propertyName = "StaticProperty" + string i, 
                                          propertyType = typeof<string>, 
                                          IsStatic=true,
                                          GetterCode= (fun args -> <@@ valueOfTheProperty @@>))

                 p.AddXmlDocDelayed(fun () -> 
                     sprintf "This is StaticProperty%d on NestedType" i)

                 yield p ]
        staticPropsInNestedType)

    [nestedType])

// The result of makeOneProvidedType is the type.
t

Hh361034.collapse_all(cs-cz,VS.110).gifPodrobnosti o vymazaných poskytovaných typech

Příklad v této části poskytuje pouze vymazané poskytované typy, které jsou užitečné v následujících situacích:

  • Při psaní poskytovatele pro informační prostor, který obsahuje pouze data a metody.

  • Při psaní poskytovatele, kde pro praktické využití informačního prostoru není rozhodující přesná sémantika typu modulu runtime.

  • Při psaní poskytovatele informačního prostoru, který je tak velký a vzájemně propojený, že není technicky možné vygenerovat skutečné typy technologie .NET pro informační prostor.

V tomto příkladu je každý poskytovaný typ vymazán na typ obj a všechna použití typu se zobrazí jako typ obj v zkompilovaném kódu.V těchto příkladech jsou skrytými objekty ve skutečnosti řetězce, ale typ se zobrazí jako Object v zkompilovaném kódu technologie .NET.Jako u všech použití vymazání typu, lze použít explicitní zabalení, vybalení a přetypování k rozvrácení vymazaných typů.V tomto případě může výjimka přetypování, která není platná, dojít při použití objektu.Modul runtime poskytovatele může definovat svůj vlastní typ reprezentace pro pomoc s ochranou proti nepravdivým reprezentacím.Nelze definovat vymazané typy v jazyce F# samém.Vymazány mohou být pouze poskytované typy.Je třeba pochopit důsledky, jak praktické tak sémantické, použití buď vymazaných typů pro poskytovatele typu, nebo poskytovatele, který poskytuje vymazané typy.Vymazaný typ nemá žádný skutečný typ technologie .NET.Proto nelze provést přesný odraz nad typem a při použití přetypování modulu runtime a jiných technik, které spoléhají na přesnou sémantiku typu modulu runtime, může dojít k rozvrácení vymazaných typů.Rozvrácení vymazaných typů často skončí výjimkami přetypování typu v modulu runtime.

Hh361034.collapse_all(cs-cz,VS.110).gifVybírání reprezentací pro vymazané poskytované typy

Pro některá použití vymazaných poskytovaných typů není požadována žádná reprezentace.Například vymazaný poskytovaný typ může obsahovat pouze statické vlastnosti a členy a žádné konstruktory, žádné metody nebo vlastnosti by nevrátily instanci typu.Pokud lze dosáhnout instance vymazaného poskytovaného typu, je nutné zvážit následující otázky:

  • Co je vymazání poskytovaného typu?

    • Vymazání poskytovaného typu je zobrazení typu ve zkompilovaném kódu technologie .NET.

    • Vymazání poskytovaného vymazaného typu třídy je vždy první nevymazaný základní typ v řetězci dědičnosti typu.

    • Vymazání poskytovaného vymazaného typu rozhraní je vždy Object.

  • Co jsou reprezentace poskytovaného typu?

    • Sada možných objektů pro vymazaný poskytovaný typ se nazývá jeho reprezentace.V příkladu v tomto dokumentu jsou reprezentace všech vymazaných poskytovaných typů Type1..Type100 vždy objekty řetězce.

Všechny reprezentace poskytovaného typu musí být kompatibilní s vymazáním poskytovaného typu.(Jinak buď kompilátor jazyka F# skončí chybou pro použití poskytovatele typu, nebo bude generován kód technologie .NET, který nelze ověřit a který není platný.Poskytovatel typu není platný, pokud vrátí kód, který poskytuje reprezentaci, která není platná.)

Reprezentaci pro poskytované objekty lze vybrat pomocí některého z následujících přístupů, z nichž oba jsou velmi časté:

  • Pokud je na existující typ technologie .NET jednoduše poskytován pevně daný obal, často je dobré vymazat typ do toho typu, použít instance toho typu jako reprezentace nebo obojí.Tento přístup je vhodný, když většina stávajících metod na tomto typu dává při použití pevně dané verze stále smysl.

  • Pro vytvoření rozhraní API, které se významně odlišuje od jakéhokoliv existujícího rozhraní API technologie .NET, má smysl vytvořit typy modulu runtime, které budou vymazáním typu a reprezentacemi pro poskytované typy.

Příklad v tomto dokumentu používá řetězce jako reprezentace poskytovaných objektů.Často může být vhodné použít pro reprezentace jiné objekty.Slovník lze například použít jako balík vlastností:

   ProvidedConstructor(parameters = [], 
                       InvokeCode= (fun args -> <@@ (new Dictionary<string,obj>()) :> obj @@>))

Případně lze typ definovat v poskytovateli typu, který bude použit při běhu k vytvoření reprezentace spolu s dalšími běhovými operacemi:

type DataObject() =
    let data = Dictionary<string,obj>()
    member x.RuntimeOperation() = data.Count

Poskytovaní členové pak mohou vytvořit instance tohoto typu objektu:

   ProvidedConstructor(parameters = [], 
                       InvokeCode= (fun args -> <@@ (new DataObject()) :> obj @@>))

V tomto případě lze (volitelně) tento typ použít jako vymazání typu zadáním tohoto typu jako baseType při vytváření ProvidedTypeDefinition:

   ProvidedTypeDefinition(…, baseType = Some typeof<DataObject> )
   …
   ProvidedConstructor(…, InvokeCode = (fun args -> <@@ new DataObject() @@>), …)

Klíčové závěry

V předchozí části bylo vysvětleno, jak vytvořit jednoduchého poskytovatele vymazání typu, který poskytuje škálu typů, vlastností a metod.Tato část také vysvětlila koncept vymazání typu, včetně některých výhod a nevýhod poskytování vymazaných typů z poskytovatele typu a zabývala se reprezentacemi vymazaných typů.

Poskytovatel typu, který používá statické parametry

Schopnost parametrizovat poskytovatele typu pomocí statických dat přináší mnoho zajímavých možností i v případech, kdy poskytovatel nepotřebuje přístup k žádným místním nebo vzdáleným datům.V této části se dozvíte některé základní postupy pro sestavení takovéhoto poskytovatele.

Hh361034.collapse_all(cs-cz,VS.110).gifTypově ověřený poskytovatel regulárního výrazu

Pro implementaci poskytovatele typu pro regulární výrazy, který obtéká knihovny Regex technologie .NET v rozhraní, které poskytuje následující záruky kompilace:

  • Ověření, zda je regulární výraz platný.

  • Poskytování pojmenovaných vlastností na shody, které jsou založeny na jakýchkoli jménech skupiny v regulárním výrazu.

Tato část ukazuje, jak pomocí poskytovatelů typu vytvořit typ RegExProviderType, který vzor regulárního výrazu parametrizuje k poskytnutí těchto výhod.Pokud není dodaný vzor platný, kompilátor ohlásí chybu a poskytovatel typu může získat skupiny ze vzoru tak, aby byly přístupné pomocí pojmenovaných vlastností na shodách.Při návrhu poskytovatele typu je třeba zvážit, jak by mělo vypadat jeho vystavené rozhraní API pro koncové uživatele a jak bude tento návrh přeložen do kódu technologie .NET.Následující příklad ukazuje použití tohoto rozhraní API k získání součástí kódu oblasti:

type T = RegexTyped< @"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)">
let reg = T()
let result = T.IsMatch("425-555-2345")
let r = reg.Match("425-555-2345").Group_AreaCode.Value //r equals "425"

Následující příklad ukazuje, jak poskytovatel typu převádí tato volání:

let reg = new Regex(@"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)")
let result = reg.IsMatch("425-123-2345")
let r = reg.Match("425-123-2345").Groups.["AreaCode"].Value //r equals "425"

Všimněte si následujících bodů:

  • Standardní typ Regex představuje parametrizovaný typ RegexTyped.

  • Konstruktor RegexTyped skončí voláním konstruktoru Regex a přejde v argument statického typu pro vzor.

  • Výsledky metody Match jsou představovány standardním typem Match.

  • Každá pojmenovaná skupina vede k poskytované vlastnosti a přístup k vlastnosti skončí použitím indexeru na kolekci Groups shody.

Následující kód je základem logiky pro implementaci takovéhoto poskytovatele a tento příklad vynechá přidání všech členů do poskytovaného typu.Informace o jednotlivých přidaných členech naleznete v příslušné části dále v tomto tématu.Pro úplný kód je možné stáhnout ukázku z Ukázkového balíku jazyka F# 3.0 na webu Codeplex.

namespace Samples.FSharp.RegexTypeProvider

open System.Reflection
open Microsoft.FSharp.Core.CompilerServices
open Samples.FSharp.ProvidedTypes
open System.Text.RegularExpressions

[<TypeProvider>]
type public CheckedRegexProvider() as this =
    inherit TypeProviderForNamespaces()

    // Get the assembly and namespace used to house the provided types
    let thisAssembly = Assembly.GetExecutingAssembly()
    let rootNamespace = "Samples.FSharp.RegexTypeProvider"
    let baseTy = typeof<obj>
    let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]

    let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, "RegexTyped", Some baseTy)

    do regexTy.DefineStaticParameters(
        parameters=staticParams, 
        instantiationFunction=(fun typeName parameterValues ->

          match parameterValues with 
          | [| :? string as pattern|] -> 
            // Create an instance of the regular expression. 
            //
            // This will fail with System.ArgumentException if the regular expression is not valid. 
            // The exception will escape the type provider and be reported in client code.
            let r = System.Text.RegularExpressions.Regex(pattern)            

            // Declare the typed regex provided type.
            // The type erasure of this type is 'obj', even though the representation will always be a Regex
            // This, combined with hiding the object methods, makes the IntelliSense experience simpler.
            let ty = ProvidedTypeDefinition(
                        thisAssembly, 
                        rootNamespace, 
                        typeName, 
                        baseType = Some baseTy)

            ...
            
            ty
          | _ -> failwith "unexpected parameter values")) 

    do this.AddNamespace(rootNamespace, [regexTy])

[<TypeProviderAssembly>]
do ()

Všimněte si následujících bodů:

  • Poskytovatel typu přebírá dva statické parametry: pattern, který je povinný, a options, který je volitelný (protože je zadána výchozí hodnota).

  • Jakmile jsou dodány statické parametry, je vytvořena instance regulárního výrazu.Pokud je regulární výraz poškozený, tato instance vyvolá výjimku a chyba bude hlášena uživatelům.

  • V rámci zpětného volání DefineStaticParameters je definován typ, který bude vrácen po dodání argumentů.

  • Tento kód nastaví HideObjectMethods na hodnotu true, takže zkušenosti s technologií IntelliSense zůstanou efektivní.Tento atribut způsobí, že budou členy Equals, GetHashCode, Finalize a GetType potlačeny ze seznamů IntelliSense pro poskytovaný objekt.

  • obj se používá jako základní typ metody, ale jako reprezentace běhu tohoto typu bude použit objekt Regex , jak ukazuje následující příklad.

  • Volání do konstruktoru Regex vyvolá ArgumentException, pokud není regulární výraz platný.Kompilátor zachytí tuto výjimku a ohlásí chybovou zprávu uživateli při kompilaci nebo v editoru systému Visual Studio.Tato výjimka umožňuje ověření regulárních výrazů bez spuštění aplikace.

Typ definovaný výše ještě nesplňuje účel, protože neobsahuje žádné smysluplné metody nebo vlastnosti.Nejprve přidejte statickou metodu IsMatch:

let isMatch = ProvidedMethod(
                methodName = "IsMatch", 
                parameters = [ProvidedParameter("input", typeof<string>)], 
                returnType = typeof<bool>, 
                IsStaticMethod = true,
                InvokeCode = fun args -> <@@ Regex.IsMatch(%%args.[0], pattern) @@>) 

isMatch.AddXmlDoc "Indicates whether the regular expression finds a match in the specified input string." 
ty.AddMember isMatch

Předchozí kód definuje metodu IsMatch, která přebírá řetězec jako vstup a vrátí bool.Jediná komplikovaná část je použití argumentu args v rámci definice InvokeCode.V tomto příkladu je args seznam nabídek, který představuje argumenty této metody.Jestliže je metoda metodou instance, první argument představuje argument this.Avšak pro statickou metodu jsou všechny argumenty pouze explicitní argumenty metody.Všimněte si, že typ nabízené hodnoty by měl odpovídat zadanému návratovému typu (v tomto případě bool).Také si všimněte, že tento kód používá metodu AddXmlDoc, aby se ujistil, že poskytovaná metoda má také užitečnou dokumentaci, kterou lze dodat pomocí technologie IntelliSense.

Dále přidejte metodu instance Match.Tato metoda by však měla vracet hodnotu poskytovaného typu Match tak, aby skupiny mohly být dosaženy pevně daným způsobem.Proto je nejprve deklarován typ Match.Jelikož tento typ závisí na vzoru, který byl zadán jako statický argument, musí být tento typ vnořen uvnitř definice parametrizovaného typu:

let matchTy = ProvidedTypeDefinition(
                "MatchType", 
                baseType = Some baseTy, 
                HideObjectMethods = true)

ty.AddMember matchTy

Potom je přidána jedna vlastnost typu Match pro každou skupinu.Za běhu je shoda reprezentována jako hodnota Match, takže nabídka, která definuje vlastnost, musí použít indexovanou vlastnost Groups k získání příslušné skupiny.

for group in r.GetGroupNames() do
    // Ignore the group named 0, which represents all input.
    if group <> "0" then
        let prop = ProvidedProperty(
                    propertyName = group, 
                    propertyType = typeof<Group>, 
                    GetterCode = fun args -> <@@ ((%%args.[0]:obj) :?> Match).Groups.[group] @@>)
        prop.AddXmlDoc(sprintf @"Gets the ""%s"" group from this match" group)
        matchTy.AddMember prop

Znovu si všimněte, že k poskytované vlastnosti přidáváte dokumentaci XML.Také si všimněte, že vlastnost lze číst, pokud je poskytnuta funkce GetterCode, a zapsat vlastnost je možné, pokud je poskytnuta funkce SetterCode, takže výsledná vlastnost je jen pro čtení.

Nyní lze vytvořit metodu instance, která vrací hodnotu tohoto typu Match:

let matchMethod = 
    ProvidedMethod(
        methodName = "Match", 
        parameters = [ProvidedParameter("input", typeof<string>)], 
        returnType = matchTy, 
        InvokeCode = fun args -> <@@ ((%%args.[0]:obj) :?> Regex).Match(%%args.[1]) :> obj @@>)
matchMeth.AddXmlDoc "Searches the specified input string for the first occurrence of this regular expression" 

ty.AddMember matchMeth

Jelikož vytváříte metodu instance, args.[0] představuje instanci RegexTyped, na které je volána metoda, a args.[1] je vstupním argumentem.

Nakonec poskytněte konstruktor, aby mohly být vytvořeny instance poskytovaného typu.

let ctor = ProvidedConstructor(
            parameters = [], 
            InvokeCode = fun args -> <@@ Regex(pattern, options) :> obj @@>)
ctor.AddXmlDoc("Initializes a regular expression instance.")

ty.AddMember ctor

Konstruktor vymaže pouze k vytvoření standardní instance Regex technologie .NET, která je znovu zabalena k objektu, protože obj je vymazání poskytovaného typu.Ukázka použití rozhraní API uvedená výše v tématu s touto změnou pracuje podle očekávání.Následující kód je úplný a konečný:

namespace Samples.FSharp.RegexTypeProvider

open System.Reflection
open Microsoft.FSharp.Core.CompilerServices
open Samples.FSharp.ProvidedTypes
open System.Text.RegularExpressions

[<TypeProvider>]
type public CheckedRegexProvider() as this =
    inherit TypeProviderForNamespaces()

    // Get the assembly and namespace used to house the provided types.
    let thisAssembly = Assembly.GetExecutingAssembly()
    let rootNamespace = "Samples.FSharp.RegexTypeProvider"
    let baseTy = typeof<obj>
    let staticParams = [ProvidedStaticParameter("pattern", typeof<string>)]

    let regexTy = ProvidedTypeDefinition(thisAssembly, rootNamespace, "RegexTyped", Some baseTy)

    do regexTy.DefineStaticParameters(
        parameters=staticParams, 
        instantiationFunction=(fun typeName parameterValues ->

          match parameterValues with 
          | [| :? string as pattern|] -> 
            // Create an instance of the regular expression. 




            let r = System.Text.RegularExpressions.Regex(pattern)            

            // Declare the typed regex provided type.



            let ty = ProvidedTypeDefinition(
                        thisAssembly, 
                        rootNamespace, 
                        typeName, 
                        baseType = Some baseTy)

            ty.AddXmlDoc "A strongly typed interface to the regular expression '%s'"

            // Provide strongly typed version of Regex.IsMatch static method.
            let isMatch = ProvidedMethod(
                            methodName = "IsMatch", 
                            parameters = [ProvidedParameter("input", typeof<string>)], 
                            returnType = typeof<bool>, 
                            IsStaticMethod = true,
                            InvokeCode = fun args -> <@@ Regex.IsMatch(%%args.[0], pattern) @@>) 

            isMatch.AddXmlDoc "Indicates whether the regular expression finds a match in the specified input string"

            ty.AddMember isMatch

            // Provided type for matches
            // Again, erase to obj even though the representation will always be a Match
            let matchTy = ProvidedTypeDefinition(
                            "MatchType", 
                            baseType = Some baseTy, 
                            HideObjectMethods = true)

            // Nest the match type within parameterized Regex type.
            ty.AddMember matchTy
        
            // Add group properties to match type
            for group in r.GetGroupNames() do
                // Ignore the group named 0, which represents all input.
                if group <> "0" then
                    let prop = ProvidedProperty(
                                propertyName = group, 
                                propertyType = typeof<Group>, 
                                GetterCode = fun args -> <@@ ((%%args.[0]:obj) :?> Match).Groups.[group] @@>)
                    prop.AddXmlDoc(sprintf @"Gets the ""%s"" group from this match" group)
                    matchTy.AddMember(prop)

            // Provide strongly typed version of Regex.Match instance method.
            let matchMeth = ProvidedMethod(
                                methodName = "Match", 
                                parameters = [ProvidedParameter("input", typeof<string>)], 
                                returnType = matchTy, 
                                InvokeCode = fun args -> <@@ ((%%args.[0]:obj) :?> Regex).Match(%%args.[1]) :> obj @@>)
            matchMeth.AddXmlDoc "Searches the specified input string for the first occurence of this regular expression"
            
            ty.AddMember matchMeth
            
            // Declare a constructor.
            let ctor = ProvidedConstructor(
                        parameters = [], 
                        InvokeCode = fun args -> <@@ Regex(pattern) :> obj @@>)

            // Add documentation to the constructor.
            ctor.AddXmlDoc "Initializes a regular expression instance"

            ty.AddMember ctor
            
            ty
          | _ -> failwith "unexpected parameter values")) 

    do this.AddNamespace(rootNamespace, [regexTy])

[<TypeProviderAssembly>]
do ()

Klíčové závěry

V této části bylo vysvětleno, jak vytvořit poskytovatele typu, který pracuje na svých statických parametrech.Poskytovatel zkontroluje statický parametr a na základě jeho hodnoty poskytne operace.

Poskytovatel typu, který je zálohován místními daty

Často může být požadováno, aby poskytovatelé typu předložili rozhraní API založené nikoli pouze na statických parametrech, ale také informacích z místních nebo vzdálených systémů.Tato část popisuje poskytovatele typu, kteří jsou založeni na místních datech, například místních datových souborech.

Hh361034.collapse_all(cs-cz,VS.110).gifJednoduchý poskytovatel souboru CSV

Jako jednoduchý příklad si představte poskytovatele typu pro přístup k vědeckých datům ve formátu hodnot oddělených čárkami (CSV).Tato část vychází z toho, že soubory CSV obsahují řádek záhlaví následovaný daty s plovoucí desetinnou čárkou, jak ukazuje následující tabulka:

Vzdálenost (metr)

Čas (sekundy)

50.0

3.7

100.0

5.2

150.0

6.4

Tato část ukazuje, jak poskytnout typ, který lze použít k získání řádků s vlastností Distance typu float<meter> a vlastností Time typu float<second>.Pro zjednodušení se vychází z následujících předpokladů:

  • Názvy záhlaví jsou buď bezjednotkové nebo mají tvar "Název (jednotka)" a neobsahují čárky.

  • Všechny jednotky jsou jednotkami soustavy SI (SI), jak definuje modul Microsoft.FSharp.Data.UnitSystems.SI.UnitNames modul (F#).

  • Všechny jednotky jsou spíše jednoduché (například metr) než složené (například metr za sekundu).

  • Všechny sloupce obsahují data plovoucí desetinné čárky.

Úplnější poskytovatel by tato omezení zmírnil.

Prvním krokem je znovu zvážit, jak by mělo vypadat rozhraní API.Vzhledem info.csv soubor s obsahem z předchozí tabulky (ve formátu oddělené čárkami) poskytovatele by mělo mít uživatelé psát kód, který se podobá následujícímu příkladu:

let info = new MiniCsv<"info.csv">()
for row in info.Data do
    let time = row.Time
    printfn "%f" (float time)

V tomto případě by měl kompilátor převést tato volání na něco jako v následujícím příkladu:

let info = new MiniCsvFile("info.csv")
for row in info.Data do
    let (time:float) = row.[1]
    printfn "%f" (float time)

Optimální překlad bude vyžadovat, aby poskytovatel typu definoval reálný typ CsvFile v sestavení poskytovatele typu.Poskytovatelé typu často k obalení důležité logiky spoléhají na několik typů pomocníka a metody.Protože opatření jsou vymazána za běhu, lze použít float[] jako vymazaný typ pro řádek.Kompilátor bude s různými sloupci zacházet tak, jako kdyby měly různé typy opatření.Například první sloupec v příkladu má typ float<meter> a druhý má float<second>.Vymazaná reprezentace však může zůstat poměrně jednoduchá.

Následující kód ukazuje základ implementace.

// Simple type wrapping CSV data
type CsvFile(filename) =
    // Cache the sequence of all data lines (all lines but the first)
    let data = 
        seq { for line in File.ReadAllLines(filename) |> Seq.skip 1 do
                yield line.Split(',') |> Array.map float }
        |> Seq.cache
    member __.Data = data

[<TypeProvider>]
type public MiniCsvProvider(cfg:TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces()

    // Get the assembly and namespace used to house the provided types.
    let asm = System.Reflection.Assembly.GetExecutingAssembly()
    let ns = "Samples.FSharp.MiniCsvProvider"

    // Create the main provided type.
    let csvTy = ProvidedTypeDefinition(asm, ns, "MiniCsv", Some(typeof<obj>))

    // Parameterize the type by the file to use as a template.
    let filename = ProvidedStaticParameter("filename", typeof<string>)
    do csvTy.DefineStaticParameters([filename], fun tyName [| :? string as filename |] ->

        // Resolve the filename relative to the resolution folder.
        let resolvedFilename = Path.Combine(cfg.ResolutionFolder, filename)
        
        // Get the first line from the file.
        let headerLine = File.ReadLines(resolvedFilename) |> Seq.head

        // Define a provided type for each row, erasing to a float[].
        let rowTy = ProvidedTypeDefinition("Row", Some(typeof<float[]>))

        // Extract header names from the file, splitting on commas.
        // use Regex matching to get the position in the row at which the field occurs
        let headers = Regex.Matches(headerLine, "[^,]+")

        // Add one property per CSV field.
        for i in 0 .. headers.Count - 1 do
            let headerText = headers.[i].Value
            
            // Try to decompose this header into a name and unit.
            let fieldName, fieldTy =
                let m = Regex.Match(headerText, @"(?<field>.+) \((?<unit>.+)\)")
                if m.Success then


                    let unitName = m.Groups.["unit"].Value
                    let units = ProvidedMeasureBuilder.Default.SI unitName
                    m.Groups.["field"].Value, ProvidedMeasureBuilder.Default.AnnotateType(typeof<float>,[units])


                else
                    // no units, just treat it as a normal float
                    headerText, typeof<float>

            let prop = ProvidedProperty(fieldName, fieldTy, 
                                             GetterCode = fun [row] -> <@@ (%%row:float[]).[i] @@>)

            // Add metadata that defines the property's location in the referenced file.
            prop.AddDefinitionLocation(1, headers.[i].Index + 1, filename)
            rowTy.AddMember(prop) 
                
        // Define the provided type, erasing to CsvFile.
        let ty = ProvidedTypeDefinition(asm, ns, tyName, Some(typeof<CsvFile>))

        // Add a parameterless constructor that loads the file that was used to define the schema.
        let ctor0 = ProvidedConstructor([], 
                                        InvokeCode = fun [] -> <@@ CsvFile(resolvedFilename) @@>)
        ty.AddMember ctor0

        // Add a constructor that takes the file name to load.
        let ctor1 = ProvidedConstructor([ProvidedParameter("filename", typeof<string>)], 
                                        InvokeCode = fun [filename] -> <@@ CsvFile(%%filename) @@>)
        ty.AddMember ctor1
        
        // Add a more strongly typed Data property, which uses the existing property at runtime.
        let prop = ProvidedProperty("Data", typedefof<seq<_>>.MakeGenericType(rowTy), 
                                    GetterCode = fun [csvFile] -> <@@ (%%csvFile:CsvFile).Data @@>)
        ty.AddMember prop

        // Add the row type as a nested type.
        ty.AddMember rowTy
        ty)

    // Add the type to the namespace.
    do this.AddNamespace(ns, [csvTy])

Všimněte si následujících bodů o implementaci:

  • Přetížené konstruktory povolí buď původní soubor nebo ten, který má stejné schéma ke čtení.Tento vzor je běžný při psaní poskytovatele typu pro místní nebo vzdálené zdroje dat a dále umožňuje, aby byl místní soubor použit jako šablona pro vzdálená data.

    Lze použít hodnotu TypeProviderConfig, která přešla v konstruktor poskytovatele typu k řešení relativních názvů souborů.

  • K definování umístění poskytovaných vlastností lze použít metodu AddDefinitionLocation.Proto když je použito Přejít na definici na poskytované vlastnosti, soubor CSV se otevře v systému Visual Studio.

  • Lze použít typ ProvidedMeasureBuilder k vyhledání jednotek SI a vygenerování příslušných typů float<_>.

Klíčové závěry

V této části bylo vysvětleno, jak vytvořit poskytovatele typu pro místní datový zdroj s jednoduchým schématem, který je obsažen v samotném datovém zdroji.

Další

Následujících části obsahují návrhy k dalšímu studiu.

Hh361034.collapse_all(cs-cz,VS.110).gifPohled na zkompilovaný kód pro vymazané typy

Pro získání představy, jak použití poskytovatele typu odpovídá kódu, který je vyslán, se podívejte na následující funkce pomocí HelloWorldTypeProvider použitého dříve v tomto tématu.

let function1 () = 
    let obj1 = Samples.HelloWorldTypeProvider.Type1("some data")
    obj1.InstanceProperty

Zde je obrázek výsledného kódu dekompilovaného pomocí ildasm.exe:

.class public abstract auto ansi sealed Module1
       extends [mscorlib]System.Object
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAtt
ribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags)
= ( 01 00 07 00 00 00 00 00 )
  .method public static int32  function1() cil managed
  {
    // Code size       24 (0x18)
    .maxstack  3
    .locals init ([0] object obj1)
    IL_0000:  nop
    IL_0001:  ldstr      "some data"
    IL_0006:  unbox.any  [mscorlib]System.Object
    IL_000b:  stloc.0
    IL_000c:  ldloc.0
    IL_000d:  call       !!0 [FSharp.Core_2]Microsoft.FSharp.Core.LanguagePrimit
ives/IntrinsicFunctions::UnboxGeneric<string>(object)
    IL_0012:  callvirt   instance int32 [mscorlib_3]System.String::get_Length()
    IL_0017:  ret
  } // end of method Module1::function1

} // end of class Module1

Jak ukazuje příklad, všechny zmínky o typu Type1 a vlastnosti InstanceProperty byly vymazány, ponechány byly pouze operace na zahrnutých typech modulu runtime.

Hh361034.collapse_all(cs-cz,VS.110).gifNávrh a pojmenování zásad pro poskytovatele typu

Při vytváření poskytovatelů typu respektujte následující zásady.

  • Poskytovatelé pro protokoly připojení

    Obecně by měly názvy většiny knihoven DLL poskytovatele pro data a služby protokolů připojení, například OData nebo připojení SQL, končit TypeProvider nebo TypeProviders.Použijte například název knihovny DLL, která se podobá následujícímu řetězci:

    Fabrikam.Management.BasicTypeProviders.dll
    

    Ujistěte se, že poskytované typy jsou členy odpovídajícího oboru názvů a indikují protokol připojení, který je implementován:

    Fabrikam.Management.BasicTypeProviders.WmiConnection<…>
    Fabrikam.Management.BasicTypeProviders.DataProtocolConnection<…>
    
  • Poskytovatelé nástroje pro obecné kódování

    Pro poskytovatele typu nástroje, například pro regulární výrazy, může být poskytovatel typu součástí základní knihovny, jak ukazuje následující příklad:

    #r "Fabrikam.Core.Text.Utilities.dll"
    

    V tomto případě by se poskytovaný typ zobrazil v odpovídajícím bodu podle běžných zásad návrhu technologie .NET:

    open Fabrikam.Core.Text.RegexTyped
    
    let regex = new RegexTyped<"a+b+a+b+">()
    
  • Zdroje dat Singleton

    Někteří poskytovatelé typu se připojí k jedinému vyhrazenému zdroji dat a poskytnou pouze data.V tomto případě by se měla vynechat přípona TypeProvider a použít běžné zásady pro pojmenování technologie .NET:

    #r "Fabrikam.Data.Freebase.dll"
    
    let data = Fabrikam.Data.Freebase.Astronomy.Asteroids
    

    Další informace naleznete v zásadách návrhu GetConnection, které jsou popsány dále v tomto tématu.

Hh361034.collapse_all(cs-cz,VS.110).gifVzory návrhu pro poskytovatele typu

Následující části popisují vzory návrhu, které lze použít při vytváření poskytovatele typu.

Hh361034.collapse_all(cs-cz,VS.110).gifVzor návrhu GetConnection

Většina poskytovatelů typu by měla být napsána k použití vzoru GetConnection, který je použit poskytovateli typu ve FSharp.Data.TypeProviders.dll, jak ukazuje následující příklad:

#r "Fabrikam.Data.WebDataStore.dll"

type Service = Fabrikam.Data.WebDataStore<…static connection parameters…>

let connection = Service.GetConnection(…dynamic connection parameters…)

let data = connection.Astronomy.Asteroids

Hh361034.collapse_all(cs-cz,VS.110).gifPoskytovatelé podporovaní vzdálenými daty a službami

Před vytvořením poskytovatele typu, který je podporován vzdálenými daty a službami, je nutné zvážit okruh otázek, které jsou spojeny se souvisejícím programováním.Tyto otázky zahrnují následující skutečnosti:

  • mapování schématu

  • živost a neplatnost v případě změny schématu

  • ukládání schématu do mezipaměti

  • asynchronní implementace operací přístupu k datům

  • podpora dotazů, včetně dotazů LINQ

  • pověření a ověření

Toto téma se těmito otázkami dále nezabývá.

Hh361034.collapse_all(cs-cz,VS.110).gifDalší vývojové techniky

Při psaní vlastních poskytovatelů typu je možné použít další následující techniky.

  • Vytváření typů a členů na vyžádání

    Rozhraní API ProvidedType zdrželo verze AddMember.

    type ProvidedType =
        member AddMemberDelayed  : (unit -> MemberInfo)      -> unit
        member AddMembersDelayed : (unit -> MemberInfo list) -> unit
    

    Tyto verze se používají k vytvoření prostorů typů na vyžádání.

  • Poskytování typů Array, ByRef a Ukazatele

    Jsou vytvářeny poskytované členy (jejichž signatury zahrnují typy polí, typy byref a instance obecných typů) pomocí běžných MakeArrayType, MakePointerType a MakeGenericType na všech instancích System.Type, včetně ProvidedTypeDefinitions.

  • Poskytování poznámek měrné jednotky

    Rozhraní API ProvidedTypes poskytuje pomocníky pro poskytování poznámek míry.Například k poskytnutí typu float<kg> použijte následující kód:

    let measures = ProvidedMeasureBuilder.Default
    let kg = measures.SI "kilogram"
    let m = measures.SI "meter"
    let float_kg = measures.AnnotateType(typeof<float>,[kg])
    

    Pro poskytnutí typu Nullable<decimal<kg/m^2>> použijte následující kód:

    let kgpm2 = measures.Ratio(kg, measures.Square m)
    let dkgpm2 = measures.AnnotateType(typeof<decimal>,[kgpm2])
    let nullableDecimal_kgpm2 = typedefof<System.Nullable<_>>.MakeGenericType [|dkgpm2 |]
    
  • Přístup k prostředkům místního projektu a místního skriptu

    Každá instance poskytovatele typu může během vytváření dostat hodnotu TypeProviderConfig.Tato hodnota obsahuje "složku řešení" pro poskytovatele (to znamená složku projektu kompilace nebo adresář, který obsahuje skript), seznam odkazovaných sestavení a další informace.

  • Neplatnost

    Poskytovatelé mohou vyvolat signály neplatnosti k upozornění služby jazyka F#, že předpoklady schématu mohly být změněny.Dojde-li k neplatnosti, typecheck je proveden znovu, pokud je poskytovatel hostován v systému Visual Studio.Tento signál bude ignorován, pokud je poskytovatel hostován v komponentě F# Interactive nebo Kompilátorem jazyka F# (fsc.exe).

  • Ukládání informací o schématu do mezipaměti

    Poskytovatelé musí často ukládat do mezipaměti informace o přístupu ke schématu.Data uložená v mezipaměti se skladují pomocí názvu souboru, který je uveden jako statický parametr nebo data uživatele.Příkladem ukládání schématu do mezipaměti je parametr LocalSchemaFile v poskytovateli typu v sestavení FSharp.Data.TypeProviders.Při implementaci těchto poskytovatelů směruje statický parametr poskytovatele typu k použití informací o schématu v zadaném místním souboru, namísto přístupu zdroje dat přes síť.K použití informací o schématu uložených v mezipaměti je nutné také nastavit statický parametr ForceUpdate na false.K povolení online a offline přístupu k datům lze použít podobné techniky.

  • Podpora sestavení

    Při kompilaci souboru .dll nebo .exe je podpůrný soubor .dll pro generované typy staticky propojen do výsledného sestavení.Tento odkaz je vytvořen kopírováním definic typu Intermediate Language (IL) a všech spravovaných prostředků z podpůrného sestavení do konečného sestavení.Při použití komponenty F# Interactive není podpůrný soubor .dll zkopírován a místo toho je načten přímo do procesu komponenty F# Interactive.

  • Výjimky a diagnostika z poskytovatelů typu

    Všechna použití všech členů z poskytovaných typů mohou vyvolat výjimky.Ve všech případech, kdy poskytovatel typu vyvolá výjimku, hostitelský kompilátor přiřadí chybu danému poskytovateli typu.

    • Výjimky poskytovatele typu by nikdy neměly skončit jako chyby vnitřního kompilátoru.

    • Poskytovatelé typu nemohu ohlásit upozornění.

    • Pokud je poskytovatel typu hostován v kompilátoru jazyka F#, vývojovém prostředí jazyka F# nebo komponentě F# Interactive, jsou zachyceny všechny výjimky z tohoto poskytovatele.Vlastností zprávy je vždy text chyby a nezobrazí se žádné trasování zásobníku.Pokud hodláte vyvolat výjimku, lze vyvolat následující příklady:

Hh361034.collapse_all(cs-cz,VS.110).gifPoskytování generovaných typů

Doposud tento dokument vysvětloval, jak poskytnout vymazané typy.K poskytnutí generovaných typů, které jsou do uživatelského programu přidány jako skutečné definice typu technologie .NET, lze také použít mechanismus poskytovatele typu v jazyce F#.Je nutné odkázat na poskytované generované typy pomocí definice typu.

open Microsoft.FSharp.TypeProviders 

type Service = ODataService<" http://services.odata.org/Northwind/Northwind.svc/">

Kód pomocníka ProvidedTypes-0.2, který je součástí 3.0 vydání jazyka F#, má pouze limitovanou podporu pro poskytování generovaných typů.Následující příkazy musí být splněny pro definici generovaného typu:

  • IsErased musí být nastaveno na false.

  • Poskytovatel musí mít sestavení, které má skutečný podpůrný soubor .dll technologie .NET s odpovídajícím souborem .dll na disku.

Je nutné také zavolat typ poskytovaný na kořeni ConvertToGenerated, jehož vnořené typy tvoří uzavřenou sadu generovaných typů.Toto volání vydá do sestavení stanovenou definici poskytovaného typu a jeho definice vnořeného typu a upraví vlastnost Assembly všech definic poskytnutého typu k vrácení tohoto sestavení.Toto sestavení je vydáno pouze při prvním přístupu k Vlastnosti sestavení na typu kořene.Hostitelský kompilátor jazyka F# získá k této vlastnosti přístup, když pro typ zpracuje prohlášení generativního typu.

Pravidla a omezení

Při psaní poskytovatele typu pamatujte na následující pravidla a omezení.

  • Poskytované typy musí být dostupné.

    Všechny poskytované typy by měly být dostupné z nevnořených typů.Nevnořené typy jsou uvedeny ve volání konstruktoru TypeProviderForNamespaces nebo volání AddNamespace.Například pokud poskytovatel poskytne typ StaticClass.P : T, je nutné zajistit, že T je buď nevnořený typ nebo vnořený pod jiným.

    Někteří poskytovatelé mají například statickou třídu jako je DataTypes obsahující tyto typy T1, T2, T3, ....Jinak chyba říká, že byl nalezen odkaz na typ T v sestavení A, ale typ nebyl v tomto sestavení nalezen.Pokud se tato chyba zobrazí, ověřte, že všechny podtypy jsou dostupné z typů poskytovatele.Poznámka: Tyto typy T1, T2, T3... jsou označovány jako typy on-the-fly.Nezapomeňte je vložit do přístupného oboru názvů nebo nadřazeného typu.

  • Omezení mechanismu poskytovatele typu

    Mechanismus poskytovatele typu v jazyce F# má následující omezení:

    • Základní infrastruktura pro poskytovatele typu v jazyce F# nepodporuje poskytované obecné typy nebo poskytované obecné metody.

    • Mechanismus nepodporuje vnořené typy se statickými parametry.

  • Omezení kódu podpory ProvidedTypes

    Kód podpory ProvidedTypes má následující pravidla a omezení:

    1. Poskytované vlastnosti s indexovanými mechanismy získání a nastavení nejsou implementovány.

    2. Poskytované události nejsou implementovány.

    3. Poskytované typy a informační objekty by měly být používány pouze pro mechanismus poskytovatele typu v jazyce F#.Nejsou obecně použitelné jako objekty System.Type.

    4. Konstrukce, které lze použít v nabídkách definujících implementaci metody, mají několik omezení.Pro zjištění, které konstrukce jsou v nabídkách podporovány, je možné odkázat na zdrojový kód pro ProvidedTypes-Verze.

  • Typ poskytovatele musí vygenerovat výstup sestavení, které jsou soubory DLL, nikoli soubory .exe.

Tipy pro vývoj

Následující tipy mohou být užitečné během procesu vývoje.

  • Spusťte dvě instance systému Visual Studio. Je možné v jedné instanci vyvíjet poskytovatele typu a v jiné poskytovatele testovat, protože test IDE umístí na soubor .dll zámek, který zabrání tomu, aby byl poskytovatel typu znovu sestaven.Zatímco je tedy poskytovatel sestavován v první instanci, je nutné zavřít druhou instanci systému Visual Studio a poté, co je poskytovatel sestaven, znovu druhou instanci otevřít.

  • Laďte poskytovatele typu pomocí vyvolání fsc.exe. Poskytovatele typu lze vyvolat pomocí následujících nástrojů:

    • fsc.exe (kompilátor příkazového řádku jazyka F#)

    • fsi.exe (kompilátor komponenty F# Interactive)

    • devenv.exe (Visual Studio)

    Často lze poskytovatele typu nejsnadněji ladit pomocí fsc.exe na souboru testovacího skriptu (například script.fsx).Spustit ladicí program lze z příkazového řádku.

    devenv /debugexe fsc.exe script.fsx
    

    Lze použít protokolování print-to-stdout.

Viz také

Další zdroje

Typ zprostředkovatele