Tutorial: Creación de un proveedor de tiposTutorial: Create a Type Provider

El mecanismo de proveedores de F# tipos de es una parte importante de su compatibilidad con la programación enriquecida de información.The type provider mechanism in F# is a significant part of its support for information rich programming. Este tutorial le explica cómo crear proveedores de tipos, a la vez que le guía en el desarrollo de varios proveedores de tipo simples para ilustrar los conceptos básicos.This tutorial explains how to create your own type providers by walking you through the development of several simple type providers to illustrate the basic concepts. Para obtener más información sobre el mecanismo de proveedores F#de tipos en, vea proveedores de tipos.For more information about the type provider mechanism in F#, see Type Providers.

El F# ecosistema contiene un intervalo de proveedores de tipo para servicios de datos empresariales y de Internet de uso frecuente.The F# ecosystem contains a range of type providers for commonly used Internet and enterprise data services. Por ejemplo:For example:

  • FSharp. Data incluye proveedores de tipos para los formatos de documento JSON, XML, csv y HTML.FSharp.Data includes type providers for JSON, XML, CSV and HTML document formats.

  • SQLProvider proporciona acceso fuertemente tipado a las bases de datos SQL a través de una asignación F# de objetos y consultas LINQ en estos orígenes de datos.SQLProvider provides strongly-typed access to SQL databases through a object mapping and F# LINQ queries against these data sources.

  • FSharp. Data. SqlClient tiene un conjunto de proveedores de tipo para la incrustación comprobada en tiempo de compilación F#de T-SQL en.FSharp.Data.SqlClient has a set of type providers for compile-time checked embedding of T-SQL in F#.

  • FSharp. Data. TypeProviders es un conjunto anterior de proveedores de tipo para su uso solo con .NET Framework programación para tener acceso a los servicios de datos SQL, Entity Framework, ODATA y WSDL.FSharp.Data.TypeProviders is an older set of type providers for use only with .NET Framework programming for accessing SQL, Entity Framework, OData and WSDL data services.

En caso necesario, se pueden crear proveedores de tipos personalizados o se puede hacer referencia a proveedores de tipos creados por otros.Where necessary, you can create custom type providers, or you can reference type providers that others have created. Por ejemplo, una organización podría tener un servicio de datos que proporcionara un número elevado y creciente de conjuntos de datos con nombre, cada uno con su propio esquema de datos estable.For example, your organization could have a data service that provides a large and growing number of named data sets, each with its own stable data schema. Se puede crear un proveedor de tipos que lea los esquemas y presente los conjuntos de datos actuales al programador de una manera fuertemente tipada.You can create a type provider that reads the schemas and presents the current data sets to the programmer in a strongly typed way.

Antes de empezarBefore You Start

El mecanismo de proveedores de tipo está diseñado principalmente para insertar espacios de información de servicios y datos estables en la experiencia de programación de F#.The type provider mechanism is primarily designed for injecting stable data and service information spaces into the F# programming experience.

Este mecanismo no está diseñado para insertar espacios de información cuyo esquema cambie durante la ejecución del programa de forma relevante para la lógica del programa.This mechanism isn’t designed for injecting information spaces whose schema changes during program execution in ways that are relevant to program logic. El mecanismo tampoco está diseñado para la metaprogramación dentro del lenguaje, aunque ese dominio contenga algunas aplicaciones válidas.Also, the mechanism isn't designed for intra-language meta-programming, even though that domain contains some valid uses. Debe utilizar este mecanismo solo en caso necesario y cuando el desarrollo de un proveedor de tipos produzca un valor muy alto.You should use this mechanism only where necessary and where the development of a type provider yields very high value.

Debe evitar escribir un proveedor de tipos cuando no hay un esquema disponible.You should avoid writing a type provider where a schema isn't available. Igualmente, debe evitar escribir un proveedor de tipo cuando una biblioteca de .NET normal (o incluso una existente) sería suficiente.Likewise, you should avoid writing a type provider where an ordinary (or even an existing) .NET library would suffice.

Antes de comenzar, debería hacerse las siguientes preguntas:Before you start, you might ask the following questions:

  • ¿Tiene un esquema para su fuente de información?Do you have a schema for your information source? Si lo tiene, ¿cuál es la correspondencia entre los sistemas de tipos de F# y .NET?If so, what’s the mapping into the F# and .NET type system?

  • ¿Puede utilizar una API existente (dinámicamente tipada) como punto de partida para su implementación?Can you use an existing (dynamically typed) API as a starting point for your implementation?

  • ¿Usarán usted y su organización el proveedor de tipos lo suficiente como para hacer que valga la pena escribirlo?Will you and your organization have enough uses of the type provider to make writing it worthwhile? ¿Cubriría una biblioteca normal de .NET sus necesidades?Would a normal .NET library meet your needs?

  • ¿Cuánto cambiará el esquema?How much will your schema change?

  • ¿Cambiará durante la escritura del código?Will it change during coding?

  • ¿Cambiará entre las sesiones de escritura de código?Will it change between coding sessions?

  • ¿Cambiará durante la ejecución del programa?Will it change during program execution?

Los proveedores de tipos son más adecuados en situaciones en las que el esquema es estable en runtime y durante el tiempo de vida del código compilado.Type providers are best suited to situations where the schema is stable at runtime and during the lifetime of compiled code.

Un proveedor de tipos simpleA Simple Type Provider

Este ejemplo es samples. HelloWorldTypeProvider, similar a los ejemplos examples del directorio del SDK del proveedor de F# tipos.This sample is Samples.HelloWorldTypeProvider, similar to the samples in the examples directory of the F# Type Provider SDK. El proveedor hace que esté disponible un "espacio de tipos" que contiene 100 tipos borrados, como muestra el código siguiente, en el que se usa la sintaxis de signatura de F# y se omiten los detalles de todos los tipos excepto Type1.The provider makes available a "type space" that contains 100 erased types, as the following code shows by using F# signature syntax and omitting the details for all except Type1. Para obtener más información sobre los tipos borrados, consulte detalles sobre los tipos de borrados proporcionados más adelante en este tema.For more information about erased types, see Details About Erased Provided Types later in this topic.

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

    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 =
…

Observe que el conjunto de tipos y miembros proporcionados se conoce de forma estática.Note that the set of types and members provided is statically known. Este ejemplo no aprovecha la capacidad de los proveedores para proporcionar tipos que dependen de un esquema.This example doesn't leverage the ability of providers to provide types that depend on a schema. La implementación del proveedor de tipo se muestra en el código siguiente y sus detalles se tratan en secciones posteriores de este tema.The implementation of the type provider is outlined in the following code, and the details are covered in later sections of this topic.

Advertencia

Puede haber diferencias entre este código y los ejemplos en línea.There may be differences between this code and the online samples.

namespace Samples.FSharp.HelloWorldTypeProvider

open System
open System.Reflection
open ProviderImplementation.ProvidedTypes
open FSharp.Core.CompilerServices
open 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(config)

  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()

Para usar este proveedor, abra una instancia independiente de Visual Studio, cree un F# script y, a continuación, agregue una referencia al proveedor desde el script mediante #r como se muestra en el código siguiente:To use this provider, open a separate instance of Visual Studio, create an F# script, and then add a reference to the provider from your script by using #r as the following code shows:

#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

A continuación, busque los tipos en el espacio de nombres Samples.HelloWorldTypeProvider generado por el proveedor de tipos.Then look for the types under the Samples.HelloWorldTypeProvider namespace that the type provider generated.

Antes de volver a compilar el proveedor, asegúrese de que se han cerrado todas las instancias de Visual Studio y de F# Interactive que están utilizando la DLL del proveedor.Before you recompile the provider, make sure that you have closed all instances of Visual Studio and F# Interactive that are using the provider DLL. De lo contrario, aparecerá un error de compilación porque la DLL de salida estará bloqueada.Otherwise, a build error will occur because the output DLL will be locked.

Para depurar este proveedor mediante instrucciones de impresión, cree un script que exponga un problema con el proveedor y, a continuación, utilice el código siguiente:To debug this provider by using print statements, make a script that exposes a problem with the provider, and then use the following code:

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

Para depurar este proveedor mediante Visual Studio, abra el Símbolo del sistema para desarrolladores de Visual Studio con credenciales administrativas y ejecute el siguiente comando:To debug this provider by using Visual Studio, open the Developer Command Prompt for Visual Studio with administrative credentials, and run the following command:

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

Como alternativa, abra Visual Studio, abra el menú Depurar, Debug/Attach to process…elija y asocie a otro devenv proceso en el que esté editando el script.As an alternative, open Visual Studio, open the Debug menu, choose Debug/Attach to process…, and attach to another devenv process where you’re editing your script. Con este método, le resultará más fácil centrarse en la lógica particular del proveedor de tipo escribiendo interactivamente expresiones en la segunda instancia (con IntelliSense completo y otras características).By using this method, you can more easily target particular logic in the type provider by interactively typing expressions into the second instance (with full IntelliSense and other features).

Puede deshabilitar la depuración "Solo mi código" para identificar mejor los errores en el código generado.You can disable Just My Code debugging to better identify errors in generated code. Para obtener información sobre cómo habilitar o deshabilitar esta característica, vea navegar por el código con el depurador.For information about how to enable or disable this feature, see Navigating through Code with the Debugger. Además, también puede establecer la detección de excepciones de primera oportunidad abriendo el Debug menú y, a continuación, eligiendo Exceptions las teclas Ctrl + Alt + E para abrir el Exceptions cuadro de diálogo.Also, you can also set first-chance exception catching by opening the Debug menu and then choosing Exceptions or by choosing the Ctrl+Alt+E keys to open the Exceptions dialog box. En ese cuadro de diálogo, Common Language Runtime Exceptionsen, Active Thrown la casilla.In that dialog box, under Common Language Runtime Exceptions, select the Thrown check box.

Implementación del proveedor de tiposImplementation of the Type Provider

En esta sección se muestran las etapas principales de la implementación del proveedor de tipos.This section walks you through the principal sections of the type provider implementation. En primer lugar, defina el tipo del proveedor de tipos personalizado:First, you define the type for the custom type provider itself:

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

Este tipo debe ser público y debe marcarse con el atributo TypeProvider para que el compilador reconozca el proveedor de tipos cuando un F# proyecto independiente haga referencia al ensamblado que contiene el tipo.This type must be public, and you must mark it with the TypeProvider attribute so that the compiler will recognize the type provider when a separate F# project references the assembly that contains the type. El parámetro de configuración es opcional y, si está presente, contiene información de configuración contextual para la instancia del F# proveedor de tipo que crea el compilador.The config parameter is optional, and, if present, contains contextual configuration information for the type provider instance that the F# compiler creates.

A continuación, implemente la interfaz ITypeProvider .Next, you implement the ITypeProvider interface. En este caso, puede utilizar el tipo TypeProviderForNamespaces de la API ProvidedTypes como tipo base.In this case, you use the TypeProviderForNamespaces type from the ProvidedTypes API as a base type. Este tipo del asistente puede proporcionar una colección finita de espacios de nombres proporcionados anticipadamente, cada uno de los cuales contiene directamente un número finito de tipos fijos proporcionados anticipadamente.This helper type can provide a finite collection of eagerly provided namespaces, each of which directly contains a finite number of fixed, eagerly provided types. En este contexto, el proveedor genera diligentemente tipos incluso si no son necesarios o se usan.In this context, the provider eagerly generates types even if they aren't needed or used.

inherit TypeProviderForNamespaces(config)

A continuación, defina los valores privados locales que especifican el espacio de nombres para los tipos proporcionados y busque el ensamblado propio del proveedor de tipos.Next, define local private values that specify the namespace for the provided types, and find the type provider assembly itself. Este ensamblado se utiliza más adelante como tipo primario lógico de los tipos borrados proporcionados.This assembly is used later as the logical parent type of the erased types that are provided.

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

A continuación, cree una función para proporcionar cada uno de los tipos Type1… Type100.Next, create a function to provide each of the types Type1…Type100. Esta función se explica con más detalle más adelante en este tema.This function is explained in more detail later in this topic.

let makeOneProvidedType (n:int) = …

A continuación, genere los 100 tipos proporcionados:Next, generate the 100 provided types:

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

Después, agregue los tipos como un espacio de nombres proporcionado:Next, add the types as a provided namespace:

do this.AddNamespace(namespaceName, types)

Finalmente, agregue un atributo de ensamblado que indique que está creando una DLL de proveedor de tipos:Finally, add an assembly attribute that indicates that you are creating a type provider DLL:

[<assembly:TypeProviderAssembly>]
do()

Proporcionar un tipo y sus miembrosProviding One Type And Its Members

La función makeOneProvidedType hace el trabajo real de proporcionar uno de los tipos.The makeOneProvidedType function does the real work of providing one of the types.

let makeOneProvidedType (n:int) =
…

En este paso se explica la implementación de esta función.This step explains the implementation of this function. En primer lugar, cree el tipo proporcionado (por ejemplo, Type1, cuando n = 1, o Type57, cuando n = 57).First, create the provided type (for example, Type1, when n = 1, or Type57, when 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>)

Debe tener en cuenta los puntos siguientes:You should note the following points:

  • Este tipo proporcionado es un tipo borrado.This provided type is erased. Dado que indica que el tipo base es obj, las instancias aparecerán como valores de tipo obj en código compilado.Because you indicate that the base type is obj, instances will appear as values of type obj in compiled code.

  • Cuando especifique un tipo no anidado, deberá especificar también el ensamblado y el espacio de nombres.When you specify a non-nested type, you must specify the assembly and namespace. Para los tipos borrados, el ensamblado deberá ser el propio ensamblado del proveedor de tipos.For erased types, the assembly should be the type provider assembly itself.

A continuación, agregue la documentación XML al tipo.Next, add XML documentation to the type. Esta documentación se demora, es decir, se calcula a petición si el compilador host la necesita.This documentation is delayed, that is, computed on-demand if the host compiler needs it.

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

A continuación, agregue una propiedad estática proporcionada al tipo:Next you add a provided static property to the type:

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

Al obtener esta propiedad, siempre se evaluará como la cadena "Hello!".Getting this property will always evaluate to the string "Hello!". La función GetterCode de la propiedad utiliza una expresión de código delimitada de F# que representa el código que genera el compilador host para obtener la propiedad.The GetterCode for the property uses an F# quotation, which represents the code that the host compiler generates for getting the property. Para obtener más información sobre las citas, vea expresiones de códigoF#delimitadas ().For more information about quotations, see Code Quotations (F#).

Agregue la documentación XML a la propiedad.Add XML documentation to the property.

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

Ahora asocie la propiedad proporcionada al tipo proporcionado.Now attach the provided property to the provided type. Debe asociar un miembro proporcionado a un único tipo.You must attach a provided member to one and only one type. De lo contrario, el miembro nunca será accesible.Otherwise, the member will never be accessible.

t.AddMember staticProp

Ahora cree un constructor proporcionado que no reciba ningún parámetro.Now create a provided constructor that takes no parameters.

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

La función InvokeCode del constructor devuelve una expresión de código delimitada de F# que representa el código que el compilador host genera cuando se llama al constructor.The InvokeCode for the constructor returns an F# quotation, which represents the code that the host compiler generates when the constructor is called. Por ejemplo, puede usar el constructor siguiente:For example, you can use the following constructor:

new Type10()

Se creará una instancia del tipo proporcionado con los datos subyacentes "The object data".An instance of the provided type will be created with underlying data "The object data". El código entre comillas incluye una conversión a obj porque ese tipo es el borrado de este tipo proporcionado (como se especificó al declarar el tipo proporcionado).The quoted code includes a conversion to obj because that type is the erasure of this provided type (as you specified when you declared the provided type).

Agregue la documentación XML al constructor y agregue el constructor proporcionado al tipo proporcionado:Add XML documentation to the constructor, and add the provided constructor to the provided type:

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

t.AddMember ctor

Cree un segundo constructor proporcionado que tome un parámetro:Create a second provided constructor that takes one parameter:

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

La función InvokeCode del constructor devuelve de nuevo una expresión de código delimitada de F# que representa el código que el compilador host generó para una llamada al método.The InvokeCode for the constructor again returns an F# quotation, which represents the code that the host compiler generated for a call to the method. Por ejemplo, puede usar el constructor siguiente:For example, you can use the following constructor:

new Type10("ten")

Se crea una instancia del tipo proporcionado con los datos subyacentes "ten".An instance of the provided type is created with underlying data "ten". Es posible que haya notado que la función InvokeCode devuelve una expresión de código delimitada.You may have already noticed that the InvokeCode function returns a quotation. La entrada de esta función es una lista de expresiones, una por cada parámetro del constructor.The input to this function is a list of expressions, one per constructor parameter. En este caso, una expresión que representa el valor del único parámetro está disponible en args.[0].In this case, an expression that represents the single parameter value is available in args.[0]. El código de una llamada al constructor convierte el valor devuelto en el tipo borrado obj.The code for a call to the constructor coerces the return value to the erased type obj. Después de agregar el segundo constructor proporcionado al tipo, cree una propiedad de instancia proporcionada:After you add the second provided constructor to the type, you create a provided instance property:

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

Al obtener esta propiedad, se devuelve la longitud de la cadena, que es el objeto de representación.Getting this property will return the length of the string, which is the representation object. La propiedad GetterCode devuelve una expresión de código delimitada de F# que especifica el código que genera el compilador host para obtener la propiedad.The GetterCode property returns an F# quotation that specifies the code that the host compiler generates to get the property. Al igual que la función InvokeCode, la función GetterCode devuelve una expresión de código delimitada.Like InvokeCode, the GetterCode function returns a quotation. El compilador host llama a esta función con una lista de argumentos.The host compiler calls this function with a list of arguments. En este caso, los argumentos incluyen solo la expresión única que representa la instancia en args.[0]la que se llama al captador, a la que se puede tener acceso mediante.In this case, the arguments include just the single expression that represents the instance upon which the getter is being called, which you can access by using args.[0]. La implementación de GetterCode después se inserta en el presupuesto de resultados en el tipo objborrado y se usa una conversión para satisfacer el mecanismo del compilador para comprobar los tipos que el objeto es una cadena.The implementation of GetterCode then splices into the result quotation at the erased type obj, and a cast is used to satisfy the compiler's mechanism for checking types that the object is a string. La parte siguiente de makeOneProvidedType proporciona un método de instancia con un parámetro.The next part of makeOneProvidedType provides an instance method with one parameter.

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

Finalmente, cree un tipo anidado que contenga 100 propiedades anidadas.Finally, create a nested type that contains 100 nested properties. La creación de este tipo anidado y sus propiedades se demora, es decir, se calcula a petición.The creation of this nested type and its properties is delayed, that is, computed on-demand.

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])

Detalles sobre los tipos proporcionados borradosDetails about Erased Provided Types

En el ejemplo de esta sección se proporcionan solo tipos proporcionados borrados, que son especialmente útiles en las situaciones siguientes:The example in this section provides only erased provided types, which are particularly useful in the following situations:

  • Cuando se escribe un proveedor para un espacio de información que solo contiene datos y métodos.When you are writing a provider for an information space that contains only data and methods.

  • Cuando se escribe un proveedor en el que la semántica precisa de tipos en tiempo de ejecución no es fundamental para el uso práctico del espacio de información.When you are writing a provider where accurate runtime-type semantics aren't critical for practical use of the information space.

  • Cuando se escribe un proveedor para un espacio de información que es tan grande y está tan interconectado que no es técnicamente factible generar tipos reales de .NET para el espacio de información.When you are writing a provider for an information space that is so large and interconnected that it isn’t technically feasible to generate real .NET types for the information space.

En este ejemplo, se borra cada tipo proporcionado para el tipo obj y todos los usos del tipo aparecen como tipo obj en el código compilado.In this example, each provided type is erased to type obj, and all uses of the type will appear as type obj in compiled code. De hecho, los objetos subyacentes de estos ejemplos son cadenas, pero el tipo aparecerá como System.Object en el código compilado de .NET.In fact, the underlying objects in these examples are strings, but the type will appear as System.Object in .NET compiled code. Como con todos los usos del borrado de tipos, se puede utilizar la conversión boxing explícita, la conversión unboxing y la conversión para trastocar los tipos borrados.As with all uses of type erasure, you can use explicit boxing, unboxing, and casting to subvert erased types. En este caso, puede producirse una excepción de conversión no válida cuando se utiliza el objeto.In this case, a cast exception that isn’t valid may result when the object is used. Un runtime de un proveedor puede definir su propio tipo de representación privado para ayudar a protegerse contra representaciones falsas.A provider runtime can define its own private representation type to help protect against false representations. No se pueden definir tipos borrados en F#.You can’t define erased types in F# itself. Solo se pueden borrar los tipos proporcionados.Only provided types may be erased. Se deben entender las implicaciones, tanto prácticas como semánticas, de utilizar los tipos borrados para el proveedor de tipo o un proveedor que proporcione tipos borrados.You must understand the ramifications, both practical and semantic, of using either erased types for your type provider or a provider that provides erased types. Un tipo borrado no tiene un tipo real de .NET.An erased type has no real .NET type. Por consiguiente, no se puede hacer una reflexión precisa sobre el tipo y se podrían trastocar los tipos borrados si se utilizan conversiones en tiempo de ejecución y otras técnicas que dependen de semánticas exactas de tipos en tiempo de ejecución.Therefore, you cannot do accurate reflection over the type, and you might subvert erased types if you use runtime casts and other techniques that rely on exact runtime type semantics. El trastocamiento de tipos borrados frecuentemente da lugar a excepciones de conversión de tipos en tiempo de ejecución.Subversion of erased types frequently results in type cast exceptions at runtime.

Elegir representaciones para los tipos proporcionados borradosChoosing Representations for Erased Provided Types

Para algunos usos de los tipos proporcionados borrados, no se requiere ninguna representación.For some uses of erased provided types, no representation is required. Por ejemplo, el tipo proporcionado borrado podría contener únicamente propiedades y miembros estáticos y ningún constructor, por lo que ningún método ni propiedad devolvería ninguna instancia del tipo.For example, the erased provided type might contain only static properties and members and no constructors, and no methods or properties would return an instance of the type. Si puede tener acceso a instancias de un tipo proporcionado borrado, considere las preguntas siguientes:If you can reach instances of an erased provided type, you must consider the following questions:

¿Cuál es el borrado de un tipo proporcionado?What is the erasure of a provided type?

  • El borrado de un tipo proporcionado es el modo en que el tipo aparece en el código compilado de .NET.The erasure of a provided type is how the type appears in compiled .NET code.

  • El borrado de una clase de un tipo proporcionado borrado siempre es el primer tipo base no borrado de la cadena de herencia del tipo.The erasure of a provided erased class type is always the first non-erased base type in the inheritance chain of the type.

  • El borrado de una interfaz de un tipo de borrado proporcionado es siempre System.Object.The erasure of a provided erased interface type is always System.Object.

¿Cuáles son las representaciones de un tipo proporcionado?What are the representations of a provided type?

  • Se denomina representaciones al conjunto de objetos posibles para un tipo proporcionado borrado.The set of possible objects for an erased provided type are called its representations. En el ejemplo de este documento, las representaciones de todos los tipos proporcionados borrados Type1..Type100 son siempre objetos de cadena.In the example in this document, the representations of all the erased provided types Type1..Type100 are always string objects.

Todas las representaciones de un tipo proporcionado deben ser compatibles con el borrado del tipo proporcionado.All representations of a provided type must be compatible with the erasure of the provided type. (De lo contrario, el compilador de F# generará un error para un uso del proveedor de tipos o se generará código no comprobable de .NET que no es válido.(Otherwise, either the F# compiler will give an error for a use of the type provider, or unverifiable .NET code that isn't valid will be generated. Un proveedor de tipos no es válido si devuelve código que proporciona una representación no válida).A type provider isn’t valid if it returns code that gives a representation that isn't valid.)

Se puede elegir una representación para los objetos proporcionados mediante uno de los dos métodos siguientes, los cuales son muy comunes:You can choose a representation for provided objects by using either of the following approaches, both of which are very common:

  • Si lo que se hace es simplemente proporcionar un contenedor fuertemente tipado sobre un tipo existente de .NET, tiene sentido que el tipo borre ese tipo, use instancias de ese tipo como representaciones, o ambas cosas.If you're simply providing a strongly typed wrapper over an existing .NET type, it often makes sense for your type to erase to that type, use instances of that type as representations, or both. Este enfoque es adecuado cuando la mayoría de los métodos existentes en ese tipo tienen sentido al usar la versión fuertemente tipada.This approach is appropriate when most of the existing methods on that type still make sense when using the strongly typed version.

  • Si lo que se desea es crear una API que difiera significativamente de cualquier API existente de .NET, tiene sentido crear tipos en tiempo de ejecución que constituyan la representación y el borrado de tipos para los tipos proporcionados.If you want to create an API that differs significantly from any existing .NET API, it makes sense to create runtime types that will be the type erasure and representations for the provided types.

El ejemplo de este documento utiliza cadenas como representaciones de los objetos proporcionados.The example in this document uses strings as representations of provided objects. Con frecuencia, puede ser adecuado utilizar otros objetos para las representaciones.Frequently, it may be appropriate to use other objects for representations. Por ejemplo, se puede utilizar un diccionario como contenedor de propiedades:For example, you may use a dictionary as a property bag:

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

También se puede definir un tipo en el proveedor de tipos que se usará en runtime para formar la representación, junto con una o más operaciones en runtime:As an alternative, you may define a type in your type provider that will be used at runtime to form the representation, along with one or more runtime operations:

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

Entonces los miembros proporcionados podrán construir instancias de este tipo de objeto:Provided members can then construct instances of this object type:

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

En este caso, se puede (opcionalmente) utilizar este tipo como borrado de tipos especificando este tipo como baseType al construir ProvidedTypeDefinition:In this case, you may (optionally) use this type as the type erasure by specifying this type as the baseType when constructing the ProvidedTypeDefinition:

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

Lecciones principalesKey Lessons

En la sección anterior se explicó cómo crear un proveedor de tipos de borrado simple que proporciona una serie de tipos, propiedades y métodos.The previous section explained how to create a simple erasing type provider that provides a range of types, properties, and methods. En dicha sección se explicó también el concepto de borrado de tipos, incluidas algunas de las ventajas y desventajas de proporcionar tipos borrados desde un proveedor de tipos, y se explicaron las representaciones de los tipos borrados.This section also explained the concept of type erasure, including some of the advantages and disadvantages of providing erased types from a type provider, and discussed representations of erased types.

Un proveedor de tipos que usa parámetros estáticosA Type Provider That Uses Static Parameters

La capacidad de parametrizar los proveedores de tipo mediante datos estáticos permite muchos escenarios interesantes, incluso en los casos en que el proveedor no necesita tener acceso a ningún dato local o remoto.The ability to parameterize type providers by static data enables many interesting scenarios, even in cases when the provider doesn't need to access any local or remote data. En esta sección, aprenderá algunas técnicas básicas para construir tales proveedores.In this section, you’ll learn some basic techniques for putting together such a provider.

Proveedor de tipo de comprobación de expresiones regularesType Checked Regex Provider

Imagine que desea implementar un proveedor de tipos para expresiones regulares que contenga las bibliotecas Regex de .NET en una interfaz que proporcione las siguientes garantías en tiempo de compilación:Imagine that you want to implement a type provider for regular expressions that wraps the .NET Regex libraries in an interface that provides the following compile-time guarantees:

  • Comprobar si una expresión regular es válida.Verifying whether a regular expression is valid.

  • Proporcionar propiedades con nombre en las coincidencias basadas en cualquier nombre de grupo de la expresión regular.Providing named properties on matches that are based on any group names in the regular expression.

En esta sección se muestra cómo utilizar los proveedores de tipo para crear un tipo RegexTyped parametrizado por el patrón de expresiones regulares para proporcionar estas ventajas.This section shows you how to use type providers to create a RegexTyped type that the regular expression pattern parameterizes to provide these benefits. El compilador notificará un error si el patrón proporcionado no es válido y el proveedor de tipos puede extraer los grupos del patrón de modo que se pueda tener acceso a ellos mediante propiedades con nombre en las coincidencias.The compiler will report an error if the supplied pattern isn't valid, and the type provider can extract the groups from the pattern so that you can access them by using named properties on matches. Cuando se diseña un proveedor de tipo, se debería considerar el aspecto que debería presentar su API expuesta para los usuarios finales y cómo se traducirá este diseño a código de .NET.When you design a type provider, you should consider how its exposed API should look to end users and how this design will translate to .NET code. El ejemplo siguiente muestra cómo usar una API como esta para obtener los componentes del código de área de un número de teléfono:The following example shows how to use such an API to get the components of the area code:

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"

El ejemplo siguiente muestra cómo convierte el proveedor de tipos estas llamadas:The following example shows how the type provider translates these calls:

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"

Tenga en cuenta los puntos siguientes:Note the following points:

  • El tipo estándar Regex representa el tipo parametrizado RegexTyped.The standard Regex type represents the parameterized RegexTyped type.

  • El constructor RegexTyped produce una llamada al constructor Regex y le pasa el argumento de tipo estático para el patrón.The RegexTyped constructor results in a call to the Regex constructor, passing in the static type argument for the pattern.

  • Los resultados del método Match se representan mediante el tipo estándar Match.The results of the Match method are represented by the standard Match type.

  • Cada grupo con nombre produce una propiedad proporcionada, y el acceso a la propiedad produce el uso de un indizador en la colección Groups de una coincidencia.Each named group results in a provided property, and accessing the property results in a use of an indexer on a match’s Groups collection.

El código siguiente es la base de la lógica para implementar un proveedor como este y este ejemplo omite la adición de todos los miembros al tipo proporcionado.The following code is the core of the logic to implement such a provider, and this example omits the addition of all members to the provided type. Para obtener información sobre cada miembro agregado, consulte la sección correspondiente más adelante en este tema.For information about each added member, see the appropriate section later in this topic. Para obtener el código completo, descargue el ejemplo del F# paquete de ejemplo 3,0 en el sitio web de codeplex.For the full code, download the sample from the F# 3.0 Sample Pack on the CodePlex website.

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 ()

Tenga en cuenta los puntos siguientes:Note the following points:

  • El proveedor de tipos toma dos parámetros estáticos: pattern, el patrón, que es obligatorio, y options, las opciones, que son opcionales (porque se proporciona un valor predeterminado).The type provider takes two static parameters: the pattern, which is mandatory, and the options, which are optional (because a default value is provided).

  • Después de proporcionar los argumentos estáticos, se crea una instancia de la expresión regular.After the static arguments are supplied, you create an instance of the regular expression. Esta instancia inicia una excepción si la Regex no es correcta, y se notifica el error a los usuarios.This instance will throw an exception if the Regex is malformed, and this error will be reported to users.

  • El tipo que se devolverá cuando se den los argumentos se define dentro de la devolución de llamada DefineStaticParameters.Within the DefineStaticParameters callback, you define the type that will be returned after the arguments are supplied.

  • Este código establece HideObjectMethods en true para que el uso de IntelliSense siga estando optimizado.This code sets HideObjectMethods to true so that the IntelliSense experience will remain streamlined. Este atributo hace que los miembros Equals, GetHashCode, Finalize y GetType se supriman de listas de IntelliSense para un objeto proporcionado.This attribute causes the Equals, GetHashCode, Finalize, and GetType members to be suppressed from IntelliSense lists for a provided object.

  • Se utiliza obj como tipo base del método, pero se utilizará un objeto Regex como representación en tiempo de ejecución de este tipo, como muestra el ejemplo siguiente.You use obj as the base type of the method, but you’ll use a Regex object as the runtime representation of this type, as the next example shows.

  • La llamada al constructor Regex inicia una excepción ArgumentException cuando una expresión regular no es válida.The call to the Regex constructor throws a ArgumentException when a regular expression isn’t valid. El compilador detecta esta excepción y envía un mensaje de error al usuario en tiempo de compilación o en el editor de Visual Studio.The compiler catches this exception and reports an error message to the user at compile time or in the Visual Studio editor. Esta excepción permite que las expresiones regulares se validen sin ejecutar una aplicación.This exception enables regular expressions to be validated without running an application.

El tipo definido anteriormente todavía no es útil porque no contiene propiedades ni métodos significativos.The type defined above isn't useful yet because it doesn’t contain any meaningful methods or properties. En primer lugar, agregue un método IsMatch estático:First, add a static IsMatch method:

let isMatch =
    ProvidedMethod(
        methodName = "IsMatch",
        parameters = [ProvidedParameter("input", typeof<string>)],
        returnType = typeof<bool>,
        isStatic = 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

El código anterior define un método IsMatch, que toma una cadena como entrada y devuelve un valor bool.The previous code defines a method IsMatch, which takes a string as input and returns a bool. La única parte difícil es el uso del argumento args dentro de la definición de la función InvokeCode.The only tricky part is the use of the args argument within the InvokeCode definition. En este ejemplo, args es una lista de expresiones de código delimitadas que representa los argumentos de este método.In this example, args is a list of quotations that represents the arguments to this method. Si el método es un método de instancia, el primer argumento representa el argumento this.If the method is an instance method, the first argument represents the this argument. Sin embargo, para un método estático, los argumentos son simplemente los argumentos explícitos para el método.However, for a static method, the arguments are all just the explicit arguments to the method. Observe que el tipo del valor de la expresión de código delimitada debe coincidir con el tipo del valor devuelto especificado (en este caso, bool).Note that the type of the quoted value should match the specified return type (in this case, bool). Observe también que este código utiliza el método AddXmlDoc para asegurarse de que el método proporcionado también tiene documentación útil, que se puede proporcionar con IntelliSense.Also note that this code uses the AddXmlDoc method to make sure that the provided method also has useful documentation, which you can supply through IntelliSense.

A continuación, agregue un método de instancia Match.Next, add an instance Match method. Sin embargo, este método debe devolver un valor de un tipo Match proporcionado, de modo que se pueda tener acceso a los grupos de manera fuertemente tipada.However, this method should return a value of a provided Match type so that the groups can be accessed in a strongly typed fashion. Por ello, primero se declara el tipo Match.Thus, you first declare the Match type. Como este tipo depende del patrón que se proporcionó como argumento estático, este tipo debe estar anidado dentro de la definición de tipos parametrizados:Because this type depends on the pattern that was supplied as a static argument, this type must be nested within the parameterized type definition:

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

ty.AddMember matchTy

A continuación, se agrega una propiedad al tipo Match de cada grupo.You then add one property to the Match type for each group. En tiempo de ejecución, una coincidencia se representa como un valor Match, por lo que la expresión de código delimitada que define la propiedad debe utilizar la propiedad indizada Groups para obtener el grupo pertinente.At runtime, a match is represented as a Match value, so the quotation that defines the property must use the Groups indexed property to get the relevant group.

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

Una vez más, observe que se está agregando la documentación XML a la propiedad proporcionada.Again, note that you’re adding XML documentation to the provided property. También observe que una propiedad puede leerse si se proporciona una función GetterCode y puede escribirse si se proporciona una función SetterCode, por lo que la propiedad resultante es de solo lectura.Also note that a property can be read if a GetterCode function is provided, and the property can be written if a SetterCode function is provided, so the resulting property is read only.

Ahora puede crear un método de instancia que devuelve un valor de este Match tipo:Now you can create an instance method that returns a value of this Match type:

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

Como está creando un método de instancia, args.[0] representa la instancia de RegexTyped en la que se está llamando al método y args.[1] es el argumento de entrada.Because you are creating an instance method, args.[0] represents the RegexTyped instance on which the method is being called, and args.[1] is the input argument.

Finalmente, proporcione un constructor para que se puedan crear instancias del tipo proporcionado.Finally, provide a constructor so that instances of the provided type can be created.

let ctor =
    ProvidedConstructor(
        parameters = [],
        invokeCode = fun args -> <@@ Regex(pattern, options) :> obj @@>)

ctor.AddXmlDoc("Initializes a regular expression instance.")

ty.AddMember ctor

El constructor simplemente borra para la creación de una Regex estándar de .NET, a la cual también se le aplica la conversión box en un objeto porque obj es el borrado del tipo proporcionado.The constructor merely erases to the creation of a standard .NET Regex instance, which is again boxed to an object because obj is the erasure of the provided type. Con ese cambio, el uso de la API de ejemplo especificada previamente en el tema funciona como se esperaba.With that change, the sample API usage that specified earlier in the topic works as expected. A continuación se muestra la versión final del código completo:The following code is complete and final:

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>,
                        isStatic = 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 occurrence 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 ()

Lecciones principalesKey Lessons

En esta sección se ha explicado cómo crear un proveedor de tipos que opera con sus parámetros estáticos.This section explained how to create a type provider that operates on its static parameters. El proveedor comprueba el parámetro estático y proporciona operaciones basadas en su valor.The provider checks the static parameter and provides operations based on its value.

Un proveedor de tipos que está respaldado por datos localesA Type Provider That Is Backed By Local Data

Con frecuencia se requiere que los proveedores de tipos muestren API basadas no solo en parámetros estáticos sino también en información procedente de sistemas locales o remotos.Frequently you might want type providers to present APIs based on not only static parameters but also information from local or remote systems. Esta sección trata sobre los proveedores de tipos basados en datos locales, como los archivos de datos locales.This section discusses type providers that are based on local data, such as local data files.

Proveedor simple de archivos CSVSimple CSV File Provider

Como ejemplo sencillo, considere un proveedor de tipo para tener acceso a datos científicos con el formato de valores separados por comas (CSV).As a simple example, consider a type provider for accessing scientific data in Comma Separated Value (CSV) format. En esta sección se supone que los archivos CSV contienen una fila de encabezado seguida de datos en coma flotante, como se muestra en la tabla siguiente:This section assumes that the CSV files contain a header row followed by floating point data, as the following table illustrates:

Distancia (metros)Distance (meter) Tiempo (segundos)Time (second)
50.050.0 3.73.7
100.0100.0 5.25.2
150.0150.0 6.46.4

En esta sección se muestra cómo proporcionar un tipo que se puede usar para obtener filas con una propiedad Distance de tipo float<meter> y una propiedad Time de tipo float<second>.This section shows how to provide a type that you can use to get rows with a Distance property of type float<meter> and a Time property of type float<second>. Para simplificar, se realizan las suposiciones siguientes:For simplicity, the following assumptions are made:

  • Los nombres de encabezado son de unidad menos o tienen el formato "nombre (unidad)" y no contienen comas.Header names are either unit-less or have the form "Name (unit)" and don't contain commas.

  • Las unidades son unidades internacionales (si) del sistema como define el módulo Microsoft. FSharp. Data. UnitSystems. Si.F#UnitNames () .Units are all System International (SI) units as the Microsoft.FSharp.Data.UnitSystems.SI.UnitNames Module (F#) module defines.

  • Las unidades son todas simples (por ejemplo, metro) en lugar de compuestas (por ejemplo, metros por segundo).Units are all simple (for example, meter) rather than compound (for example, meter/second).

  • Todas las columnas contienen datos en coma flotante.All columns contain floating point data.

Un proveedor más completo relajaría estas restricciones.A more complete provider would loosen these restrictions.

De nuevo, el primer paso es considerar qué aspecto debe tener la API.Again the first step is to consider how the API should look. Dado un archivo info.csv con el contenido de la tabla anterior (en formato separado por comas), los usuarios del proveedor deberían poder escribir un código similar al del ejemplo siguiente:Given an info.csv file with the contents from the previous table (in comma-separated format), users of the provider should be able to write code that resembles the following example:

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

En este caso, el compilador debería convertir estas llamadas en algo similar al ejemplo siguiente:In this case, the compiler should convert these calls into something like the following example:

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

La conversión óptima requerirá que el proveedor de tipos defina un tipo CsvFile real en el ensamblado del proveedor de tipos.The optimal translation will require the type provider to define a real CsvFile type in the type provider's assembly. Los proveedores de tipos a veces se basan en algunos tipos y métodos del asistente para contener la lógica importante.Type providers often rely on a few helper types and methods to wrap important logic. Dado que las medidas se borran en tiempo de ejecución, se puede utilizar float[] como el tipo borrado para una fila.Because measures are erased at runtime, you can use a float[] as the erased type for a row. El compilador considerará que las distintas columnas contienen distintos tipos de medidas.The compiler will treat different columns as having different measure types. Por ejemplo, la primera columna de nuestro ejemplo contiene el tipo float<meter> y la segunda contiene float<second>.For example, the first column in our example has type float<meter>, and the second has float<second>. Sin embargo, la representación borrada puede seguir siendo bastante simple.However, the erased representation can remain quite simple.

En el ejemplo de código siguiente se muestra el núcleo de la implementación.The following code shows the core of the implementation.

// 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(cfg)

    // 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])

Tenga en cuenta las siguientes observaciones sobre la implementación:Note the following points about the implementation:

  • Los constructores sobrecargados permiten leer el archivo original o uno que tenga un esquema idéntico.Overloaded constructors allow either the original file or one that has an identical schema to be read. Este patrón es habitual al escribir un proveedor de tipo para orígenes de datos locales o remotos, y además permite el uso de un archivo local como plantilla para los datos remotos.This pattern is common when you write a type provider for local or remote data sources, and this pattern allows a local file to be used as the template for remote data.

  • Puede utilizar el valor TypeProviderConfig que se pasa al constructor del proveedor de tipos para resolver nombres de archivo relativos.You can use the TypeProviderConfig value that’s passed in to the type provider constructor to resolve relative file names.

  • Se puede utilizar el método AddDefinitionLocation para definir la ubicación de las propiedades proporcionadas.You can use the AddDefinitionLocation method to define the location of the provided properties. Por lo tanto, si Go To Definition usa en una propiedad proporcionada, el archivo CSV se abrirá en Visual Studio.Therefore, if you use Go To Definition on a provided property, the CSV file will open in Visual Studio.

  • Se puede utilizar el tipo ProvidedMeasureBuilder para buscar las unidades del SI y generar los tipos float<_> pertinentes.You can use the ProvidedMeasureBuilder type to look up the SI units and to generate the relevant float<_> types.

Lecciones principalesKey Lessons

En esta sección se ha explicado cómo crear un proveedor de tipo para un origen de datos local con un esquema simple que está contenido en el propio origen de datos.This section explained how to create a type provider for a local data source with a simple schema that's contained in the data source itself.

Ampliar conocimientosGoing Further

En las secciones siguientes se incluyen sugerencias para ampliar conocimientos sobre el tema.The following sections include suggestions for further study.

Un vistazo al código compilado de los tipos borradosA Look at the Compiled Code for Erased Types

Para tener una idea de cómo se corresponde el uso del proveedor de tipos con el código emitido, revise la función siguiente mediante el proveedor HelloWorldTypeProvider utilizado anteriormente en este tema.To give you some idea of how the use of the type provider corresponds to the code that's emitted, look at the following function by using the HelloWorldTypeProvider that's used earlier in this topic.

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

A continuación se muestra una imagen del código resultante descompilado mediante ildasm.exe:Here’s an image of the resulting code decompiled by using 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

Como muestra el ejemplo, se han borrado todas las menciones del tipo Type1 y la propiedad InstanceProperty, y quedan solo las operaciones para los tipos de tiempo de ejecución relacionados.As the example shows, all mentions of the type Type1 and the InstanceProperty property have been erased, leaving only operations on the runtime types involved.

Diseño y convenciones de nomenclatura para los proveedores de tiposDesign and Naming Conventions for Type Providers

Respete las convenciones siguientes al crear proveedores de tipos.Observe the following conventions when authoring type providers.

Proveedores de protocolos de conectividad En general, los nombres de la mayoría de los archivos dll de proveedor para los protocolos de datos y conectividad de servicio, como TypeProvider las TypeProvidersconexiones de oData o SQL, deben acabar en o.Providers for Connectivity Protocols In general, names of most provider DLLs for data and service connectivity protocols, such as OData or SQL connections, should end in TypeProvider or TypeProviders. Por ejemplo, utilice un nombre de DLL similar a la siguiente cadena:For example, use a DLL name that resembles the following string:

Fabrikam.Management.BasicTypeProviders.dll

Asegúrese de que los tipos proporcionados son miembros del espacio de nombres correspondiente e indican el protocolo de conexión que se ha implementado:Ensure that your provided types are members of the corresponding namespace, and indicate the connectivity protocol that you implemented:

  Fabrikam.Management.BasicTypeProviders.WmiConnection<…>
  Fabrikam.Management.BasicTypeProviders.DataProtocolConnection<…>

Proveedores de utilidades para la codificación general.Utility Providers for General Coding. Para un proveedor de tipos de utilidad, como el de las expresiones regulares, el proveedor de tipos puede ser parte de una biblioteca base, como se muestra en el ejemplo siguiente:For a utility type provider such as that for regular expressions, the type provider may be part of a base library, as the following example shows:

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

En este caso, el tipo proporcionado aparecería en un punto adecuado según las convenciones normales de diseño de .NET:In this case, the provided type would appear at an appropriate point according to normal .NET design conventions:

  open Fabrikam.Core.Text.RegexTyped

  let regex = new RegexTyped<"a+b+a+b+">()

Orígenes de datos singleton.Singleton Data Sources. Algunos proveedores de tipos se conectan a un único origen de datos dedicado y solo proporcionan datos.Some type providers connect to a single dedicated data source and provide only data. En este caso, se debería colocar el sufijo TypeProvider y utilizar las convenciones normales de nomenclatura de .NET:In this case, you should drop the TypeProvider suffix and use normal conventions for .NET naming:

#r "Fabrikam.Data.Freebase.dll"

let data = Fabrikam.Data.Freebase.Astronomy.Asteroids

Para obtener más información, vea la convención de diseño GetConnection que se describe más adelante en este tema.For more information, see the GetConnection design convention that's described later in this topic.

Patrones de diseño para los proveedores de tiposDesign Patterns for Type Providers

En las secciones siguientes se describen los patrones de diseño que se pueden usar cuando se crean los proveedores de tipos.The following sections describe design patterns you can use when authoring type providers.

El patrón de diseño GetConnectionThe GetConnection Design Pattern

La mayoría de los proveedores de tipos se deben escribir para que usen el patrón GetConnection utilizado por los proveedores de tipo en FSharp.Data.TypeProviders.dll, como se muestra en el ejemplo siguiente:Most type providers should be written to use the GetConnection pattern that's used by the type providers in FSharp.Data.TypeProviders.dll, as the following example shows:

#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

Proveedores de tipos respaldados por datos y servicios remotosType Providers Backed By Remote Data and Services

Antes de crear un proveedor de tipos respaldado por datos y servicios remotos, se deben tener en cuenta varios problemas que son inherentes a la programación conectada.Before you create a type provider that's backed by remote data and services, you must consider a range of issues that are inherent in connected programming. Estos problemas incluyen los siguientes aspectos:These issues include the following considerations:

  • Asignación de esquemasschema mapping

  • Vida e invalidación en presencia de un cambio de esquemaliveness and invalidation in the presence of schema change

  • Almacenamiento en caché de esquemasschema caching

  • Implementaciones asíncronas de operaciones de acceso a datosasynchronous implementations of data access operations

  • Consultas admitidas, incluidas las consultas LINQsupporting queries, including LINQ queries

  • Credenciales y autenticacióncredentials and authentication

Este tema no profundiza en estos problemas.This topic doesn't explore these issues further.

Técnicas adicionales de creaciónAdditional Authoring Techniques

Al escribir sus propios proveedores de tipos, quizá quiera utilizar las siguientes técnicas adicionales.When you write your own type providers, you might want to use the following additional techniques.

Crear tipos y miembros bajo demandaCreating Types and Members On-Demand

La API de ProvidedType tiene versiones demoradas de AddMember.The ProvidedType API has delayed versions of AddMember.

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

Estas versiones se utilizan para crear espacios de tipos a petición.These versions are used to create on-demand spaces of types.

Proporcionar tipos de matriz y crear instancias de tipos genéricosProviding Array types and Generic Type Instantiations

Los miembros proporcionados (cuyas firmas incluyen tipos de matriz, tipos de ByRef y creaciones de instancias de tipos genéricos) usan el MakeArrayTypenormal MakePointerType, y MakeGenericType en cualquier instancia de Type, incluido ProvidedTypeDefinitions.You make provided members (whose signatures include array types, byref types, and instantiations of generic types) by using the normal MakeArrayType, MakePointerType, and MakeGenericType on any instance of Type, including ProvidedTypeDefinitions.

Nota

En algunos casos, es posible que tenga que usar el ayudante ProvidedTypeBuilder.MakeGenericTypeen.In some cases you may have to use the helper in ProvidedTypeBuilder.MakeGenericType. Consulte la documentación del SDK del proveedor de tipos para obtener más detalles.See the Type Provider SDK documentation for more details.

Proporcionar anotaciones de unidades de medidaProviding Unit of Measure Annotations

La API ProvidedTypes proporciona asistentes para proporcionar anotaciones de medidas.The ProvidedTypes API provides helpers for providing measure annotations. Por ejemplo, para proporcionar el tipo float<kg>, utilice el código siguiente:For example, to provide the type float<kg>, use the following code:

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

Para proporcionar el tipo Nullable<decimal<kg/m^2>>, utilice el código siguiente:To provide the type Nullable<decimal<kg/m^2>>, use the following code:

  let kgpm2 = measures.Ratio(kg, measures.Square m)
  let dkgpm2 = measures.AnnotateType(typeof<decimal>,[kgpm2])
  let nullableDecimal_kgpm2 = typedefof<System.Nullable<_>>.MakeGenericType [|dkgpm2 |]

Acceder a recursos locales del proyecto o del scriptAccessing Project-Local or Script-Local Resources

A cada instancia de un proveedor de tipo se le puede asignar un valor TypeProviderConfig durante la construcción.Each instance of a type provider can be given a TypeProviderConfig value during construction. Este valor contiene la "carpeta de resolución" para el proveedor (es decir, la carpeta del proyecto para la compilación o el directorio que contiene un script), la lista de ensamblados a los que se hace referencia y otra información.This value contains the "resolution folder" for the provider (that is, the project folder for the compilation or the directory that contains a script), the list of referenced assemblies, and other information.

InvalidaciónInvalidation

Los proveedores pueden generar señales de invalidación para notificar al servicio del lenguaje F# que las suposiciones acerca del esquema pueden haber cambiado.Providers can raise invalidation signals to notify the F# language service that the schema assumptions may have changed. Cuando se produce la invalidación, se repite una comprobación de tipos si el proveedor se hospeda en Visual Studio.When invalidation occurs, a typecheck is redone if the provider is being hosted in Visual Studio. Esta señal se omite cuando el proveedor se hospeda en F# Interactive o por el compilador de F# (fsc.exe).This signal will be ignored when the provider is hosted in F# Interactive or by the F# Compiler (fsc.exe).

Almacenar en caché la información del esquemaCaching Schema Information

A menudo los proveedores deben almacenar en memoria caché el acceso a la información del esquema.Providers must often cache access to schema information. Los datos almacenados en caché deben almacenarse utilizando un nombre de archivo que se da como parámetro estático o como datos de usuario.The cached data should be stored by using a file name that's given as a static parameter or as user data. Un ejemplo de almacenamiento en caché de esquema es el parámetro LocalSchemaFile en los proveedores de tipos del ensamblado FSharp.Data.TypeProviders.An example of schema caching is the LocalSchemaFile parameter in the type providers in the FSharp.Data.TypeProviders assembly. En la implementación de estos proveedores, este parámetro estático ordena al proveedor de tipos que use la información del esquema del archivo local especificado en lugar de acceder al origen de datos en la red.In the implementation of these providers, this static parameter directs the type provider to use the schema information in the specified local file instead of accessing the data source over the network. Para utilizar la información del esquema almacenada en caché, también se debe establecer el parámetro estático ForceUpdate en false.To use cached schema information, you must also set the static parameter ForceUpdate to false. Se puede usar una técnica similar para permitir el acceso a datos en línea y sin conexión.You could use a similar technique to enable online and offline data access.

Ensamblado de respaldoBacking Assembly

Al compilar .dll un .exe archivo o, el archivo. dll de respaldo para los tipos generados se vincula estáticamente en el ensamblado resultante.When you compile a .dll or .exe file, the backing .dll file for generated types is statically linked into the resulting assembly. Este vínculo se crea copiando las definiciones de tipos del lenguaje intermedio (IL) y cualquier recurso administrado del ensamblado de respaldo al ensamblado final.This link is created by copying the Intermediate Language (IL) type definitions and any managed resources from the backing assembly into the final assembly. Cuando se usa F# Interactive, el archivo .dll de respaldo no se copia, sino que se carga directamente en el proceso de F# Interactive.When you use F# Interactive, the backing .dll file isn't copied and is instead loaded directly into the F# Interactive process.

Excepciones y diagnósticos de proveedores de tipoExceptions and Diagnostics from Type Providers

Todos los usos de todos los miembros de los tipos proporcionados pueden producir excepciones.All uses of all members from provided types may throw exceptions. En todos los casos, si un proveedor de tipos genera una excepción, el compilador host atribuye el error a un proveedor de tipos específico.In all cases, if a type provider throws an exception, the host compiler attributes the error to a specific type provider.

  • Las excepciones de proveedores de tipos nunca deben producir errores internos del compilador.Type provider exceptions should never result in internal compiler errors.

  • Los proveedores de tipo no pueden notificar advertencias.Type providers can't report warnings.

  • Cuando un proveedor de tipo se hospeda en el compilador de F#, un entorno de desarrollo de F# o F# Interactive, se detectan todas las excepciones de ese proveedor.When a type provider is hosted in the F# compiler, an F# development environment, or F# Interactive, all exceptions from that provider are caught. La propiedad Message es siempre el texto del error y no aparece ningún seguimiento de pila.The Message property is always the error text, and no stack trace appears. Si va a iniciar una excepción, puede iniciar los ejemplos siguientes: System.NotSupportedException, System.IO.IOException, System.Exception.If you’re going to throw an exception, you can throw the following examples: System.NotSupportedException, System.IO.IOException, System.Exception.

Proporcionar tipos generadosProviding Generated Types

Hasta ahora, en este documento se ha explicado cómo proporcionar tipos borrados.So far, this document has explained how to provide erased types. También se puede usar el mecanismo de proveedores de tipo de F# para proporcionar tipos generados, que se agregan como definiciones de tipo reales de .NET en el programa del usuario.You can also use the type provider mechanism in F# to provide generated types, which are added as real .NET type definitions into the users' program. Se debe hacer referencia a los tipos proporcionados generados mediante una definición de tipo.You must refer to generated provided types by using a type definition.

open Microsoft.FSharp.TypeProviders

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

El código del asistente ProvidedTypes-0.2 que forma parte de la versión 3.0 de F# solo tiene compatibilidad limitada para proporcionar tipos generados.The ProvidedTypes-0.2 helper code that is part of the F# 3.0 release has only limited support for providing generated types. Los enunciados siguientes deben ser verdaderos para una definición de un tipo generado:The following statements must be true for a generated type definition:

  • isEraseddebe establecerse en false.isErased must be set to false.

  • El tipo generado se debe agregar a un recién construido ProvidedAssembly(), que representa un contenedor para fragmentos de código generados.The generated type must be added to a newly constructed ProvidedAssembly(), which represents a container for generated code fragments.

  • El proveedor debe tener un ensamblado que tenga un archivo .dll de respaldo real de .NET con un archivo .dll coincidente en el disco.The provider must have an assembly that has an actual backing .NET .dll file with a matching .dll file on disk.

Reglas y limitacionesRules and Limitations

Al escribir proveedores de tipos, tenga en cuenta las siguientes reglas y limitaciones.When you write type providers, keep the following rules and limitations in mind.

Los tipos proporcionados deben ser accesiblesProvided types must be reachable

Debe tenerse acceso a todos los tipos proporcionados desde los tipos no anidados.All provided types should be reachable from the non-nested types. Los tipos no anidados se proporcionan en la llamada al constructor TypeProviderForNamespaces o en una llamada a AddNamespace.The non-nested types are given in the call to the TypeProviderForNamespaces constructor or a call to AddNamespace. Por ejemplo, si el proveedor proporciona un tipo StaticClass.P : T, debe asegurarse de que T es un tipo no anidado o está anidado debajo de uno.For example, if the provider provides a type StaticClass.P : T, you must ensure that T is either a non-nested type or nested under one.

Por ejemplo, algunos proveedores tienen una clase estática como DataTypes que contiene estos tipos T1, T2, T3, ....For example, some providers have a static class such as DataTypes that contain these T1, T2, T3, ... types. De lo contrario, el error indica que se ha encontrado una referencia al tipo T en el ensamblado A, pero el tipo no se encuentra en ese ensamblado.Otherwise, the error says that a reference to type T in assembly A was found, but the type couldn't be found in that assembly. Si aparece este error, compruebe que se puede obtener acceso a todos los subtipos desde los tipos del proveedor.If this error appears, verify that all your subtypes can be reached from the provider types. Nota: Estos T1, T2, T3... tipos se denominan tipos sobre la marcha .Note: These T1, T2, T3... types are referred to as the on-the-fly types. Recuerde colocarlos en un espacio de nombres accesible o en un tipo primario.Remember to put them in an accessible namespace or a parent type.

Limitaciones del mecanismo de proveedores de tiposLimitations of the Type Provider Mechanism

El mecanismo de proveedores de tipos de F# tiene las siguientes limitaciones:The type provider mechanism in F# has the following limitations:

  • La infraestructura subyacente para los proveedores de tipos de F# no admite tipos genéricos proporcionados ni métodos genéricos proporcionados.The underlying infrastructure for type providers in F# doesn't support provided generic types or provided generic methods.

  • El mecanismo no admite tipos anidados con parámetros estáticos.The mechanism doesn't support nested types with static parameters.

Sugerencias de desarrolloDevelopment Tips

Puede que le resulten útiles las siguientes sugerencias durante el proceso de desarrollo:You might find the following tips helpful during the development process:

Ejecutar dos instancias de Visual StudioRun two instances of Visual Studio

Puede desarrollar el proveedor de tipo en una instancia y probarlo en la otra porque el IDE de prueba tomará un bloqueo en el archivo .dll que evita que se recompile el proveedor de tipo.You can develop the type provider in one instance and test the provider in the other because the test IDE will take a lock on the .dll file that prevents the type provider from being rebuilt. Por lo tanto, debe cerrar la segunda instancia de Visual Studio mientras se compila el proveedor en la primera y, a continuación, debe volver a abrir la segunda instancia después de compilar el proveedor.Thus, you must close the second instance of Visual Studio while the provider is built in the first instance, and then you must reopen the second instance after the provider is built.

Depurar proveedores de tipos mediante invocaciones de FSC. exeDebug type providers by using invocations of fsc.exe

Puede invocar proveedores de tipo mediante las herramientas siguientes:You can invoke type providers by using the following tools:

  • fsc.exe (el compilador de línea de comandos de F#)fsc.exe (The F# command line compiler)

  • fsi.exe (el compilador interactivo de F#)fsi.exe (The F# Interactive compiler)

  • devenv.exe (Visual Studio)devenv.exe (Visual Studio)

A menudo, lo más fácil es depurar los proveedores de tipo mediante fsc.exe en un archivo de script de prueba (por ejemplo, script.fsx).You can often debug type providers most easily by using fsc.exe on a test script file (for example, script.fsx). Puede iniciar un depurador desde el símbolo del sistema.You can launch a debugger from a command prompt.

devenv /debugexe fsc.exe script.fsx

Puede usar print-to-stdout como registro.You can use print-to-stdout logging.

Vea tambiénSee also