Providing Types, Members, and Features
Static Parameters
Static parameters let consumers specialise a type at compile time:
type MyParameterisedProvider(config: TypeProviderConfig) as this =
inherit TypeProviderForNamespaces(config)
let ns = "MyProvider.Provided"
let asm = Assembly.GetExecutingAssembly()
let createType typeName (count: int) =
let t = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<obj>)
for i in 1 .. count do
let prop =
ProvidedProperty(
"Property" + string i,
typeof<int>,
isStatic = true,
getterCode = fun _args -> <@@ i @@>)
t.AddMember prop
t
do
let containerType = ProvidedTypeDefinition(asm, ns, "Schema", Some typeof<obj>)
containerType.DefineStaticParameters(
[ ProvidedStaticParameter("Count", typeof<int>) ],
fun typeName args -> createType typeName (args.[0] :?> int))
this.AddNamespace(ns, [containerType])
Usage in user code:
type MySchema = MyProvider.Provided.Schema<Count = 5>
printfn "%d" MySchema.Property3 // 3
Optional Static Parameters with Defaults
containerType.DefineStaticParameters(
[ ProvidedStaticParameter("Count", typeof<int>)
ProvidedStaticParameter("Prefix", typeof<string>, parameterDefaultValue = "Col") ],
fun typeName args ->
let count = args.[0] :?> int
let prefix = args.[1] :?> string
createType typeName count prefix)
Warning: if all static parameters have defaults the SDK emits a compiler warning to alert you that the unapplied base type would be the same as the applied type with defaults. This is intentional behaviour. Test coverage:
ErasingProviderWithStaticParamsintests/BasicErasedProvisionTests.fs;GenerativePropertyProviderWithStaticParamsintests/BasicGenerativeProvisionTests.fs.
Quotations in Type Providers
Quotation literals (<@@ ... @@>) are the primary way to specify the runtime behaviour of erased members.
The F# compiler splices the quotation body at the call site.
// Access the 'this' parameter of an instance getter
let prop =
ProvidedProperty("Count", typeof<int>,
getterCode = fun args ->
// args.[0] is 'this', typed as the erased base type
<@@ (%%(args.[0]) :> obj :?> System.Collections.Generic.List<int>).Count @@>)
For cross-targeting rules and UncheckedQuotations see Technical Notes.
Your First Generative Type Provider
type MyGenerativeProvider(config: TypeProviderConfig) as this =
inherit TypeProviderForNamespaces(
config,
assemblyReplacementMap = [("MyProvider.DesignTime", "MyProvider.Runtime")])
let ns = "MyProvider.Provided"
let asm = Assembly.GetExecutingAssembly()
let createType typeName (count: int) =
let provAsm = ProvidedAssembly()
let myType = ProvidedTypeDefinition(provAsm, ns, typeName, Some typeof<obj>, isErased = false)
let ctor = ProvidedConstructor([], invokeCode = fun _args -> <@@ () @@>)
myType.AddMember ctor
for i in 1 .. count do
let prop =
ProvidedProperty(
"Property" + string i, typeof<int>,
getterCode = fun _args -> <@@ i @@>)
myType.AddMember prop
provAsm.AddTypes [myType]
myType
do
let containerType = ProvidedTypeDefinition(asm, ns, "GenerativeSchema", Some typeof<obj>, isErased = false)
containerType.DefineStaticParameters(
[ ProvidedStaticParameter("Count", typeof<int>) ],
fun typeName args -> createType typeName (args.[0] :?> int))
this.AddNamespace(ns, [containerType])
Key differences from erased providers:
isErased = falseonProvidedTypeDefinition.- Must create a
ProvidedAssembly()and callprovAsm.AddTypes [myType]. -
The constructor is mandatory in a generative type – every
ProvidedTypeDefinitionmust have at least oneProvidedConstructor.
Test coverage:
GenerativePropertyProviderWithStaticParamsintests/BasicGenerativeProvisionTests.fs.
Providing Members in Depth
Constructors
// Default constructor
let ctor0 = ProvidedConstructor([], invokeCode = fun _args -> <@@ () @@>)
// Constructor with parameters (generative: args.[0] is 'this', args.[1] is first param)
let ctor1 =
ProvidedConstructor(
[ ProvidedParameter("value", typeof<int>) ],
invokeCode = fun args -> <@@ ignore (%%(args.[1]) : int) @@>)
// Static constructor (type initialiser)
let cctor =
ProvidedConstructor(
[], invokeCode = fun _args -> <@@ () @@>,
IsTypeInitializer = true)
For erased providers, args.[0] is the first explicit parameter (there is no this expression).
For generative providers, args.[0] is this.
Events
let myEvent =
ProvidedEvent(
"DataChanged",
typeof<System.EventHandler>,
adderCode = fun args -> <@@ ignore (%%(args.[1]) : System.EventHandler) @@>,
removerCode = fun args -> <@@ ignore (%%(args.[1]) : System.EventHandler) @@>)
myType.AddMember myEvent
Test coverage:
GenerativePropertyProviderWithStaticParamsintests/BasicGenerativeProvisionTests.fs.
Fields (Generative Only)
// Regular field
let field = ProvidedField("_count", typeof<int>)
myType.AddMember field
// Literal / enum constant
let litField = ProvidedField.Literal("MaxSize", typeof<int>, 100)
myType.AddMember litField
Test coverage:
GenerativeEnumsProvisionTests.fs.
Delayed Member Generation
For large schemas it can be expensive to generate all members eagerly. Use AddMembersDelayed to defer
generation until the compiler actually needs them:
myType.AddMembersDelayed(fun () ->
[ for col in schema.Columns ->
ProvidedProperty(col.Name, col.Type,
getterCode = fun args -> <@@ fetchColumn (%%(args.[0]) : obj) col.Name @@>) ])
Interfaces (Generative)
// Declare the interface
let iface =
ProvidedTypeDefinition("IContract", None, isErased = false, isInterface = true)
let meth = ProvidedMethod("Execute", [], typeof<unit>)
meth.AddMethodAttrs(MethodAttributes.Virtual ||| MethodAttributes.Abstract)
iface.AddMember meth
// Implement the interface on a generative type
myType.AddInterfaceImplementation iface
let impl = ProvidedMethod("Execute", [], typeof<unit>,
invokeCode = fun _args -> <@@ () @@>)
myType.AddMember impl
myType.DefineMethodOverride(impl, iface.GetMethod("Execute"))
provAsm.AddTypes [iface; myType]
Test coverage:
GenerativeInterfacesTests.fs.
Enumerations (Generative)
let enumType = ProvidedTypeDefinition("Status", Some typeof<Enum>, isErased = false)
enumType.SetEnumUnderlyingType(typeof<int>)
for (name, value) in [ "Active", 1; "Inactive", 2; "Pending", 3 ] do
let field = ProvidedField.Literal(name, enumType, value)
enumType.AddMember field
provAsm.AddTypes [enumType]
Test coverage:
GenerativeEnumsProvisionTests.fs.
Nested Types
Both erased and generative providers support nested types:
// Erased nested type
let outerType = ProvidedTypeDefinition(asm, ns, "Outer", Some typeof<obj>)
let innerType = ProvidedTypeDefinition("Inner", Some typeof<obj>)
innerType.AddMember(ProvidedProperty("Value", typeof<int>, isStatic = true,
getterCode = fun _args -> <@@ 42 @@>))
outerType.AddMember innerType
For generative providers, nested types must also be registered with the ProvidedAssembly using
AddNestedTypes:
provAsm.AddNestedTypes([innerType], ["Outer"])
XML Documentation
Use AddXmlDoc to supply IntelliSense documentation for any provided member:
let myProp = ProvidedProperty("Status", typeof<string>, ...)
myProp.AddXmlDoc "Gets the current connection status."
// Delayed – evaluated only when IntelliSense requests it
myProp.AddXmlDocDelayed(fun () ->
sprintf "Gets the '%s' column value." columnName)
Custom Attributes
Attach arbitrary custom attributes to provided members using AddCustomAttribute:
open System
open System.Reflection
// Mark a type as [<Obsolete("use NewType instead")>]
myType.AddCustomAttribute {
new CustomAttributeData() with
member _.Constructor = typeof<ObsoleteAttribute>.GetConstructor([| typeof<string> |])
member _.ConstructorArguments =
[| CustomAttributeTypedArgument(typeof<string>, "use NewType instead" :> obj) |] :> _
member _.NamedArguments = [||] :> _
}
// Mark a parameter as [<ReflectedDefinition>]
let param = ProvidedParameter("expr", typeof<Microsoft.FSharp.Quotations.Expr<int>>)
param.AddCustomAttribute {
new CustomAttributeData() with
member _.Constructor = typeof<ReflectedDefinitionAttribute>.GetConstructor([||])
member _.ConstructorArguments = [||] :> _
member _.NamedArguments = [||] :> _
}
AddCustomAttribute is available on ProvidedTypeDefinition, ProvidedMethod, ProvidedProperty,
and ProvidedParameter. ProvidedConstructor does not support it; use AddObsoleteAttribute for
the common case of marking a constructor as obsolete.
When using cross-targeting (the normal mode), custom attributes whose types exist in the target
reference assemblies will appear in GetCustomAttributesData() on the translated (target) type.
Attributes whose types are not present in the target assemblies are silently omitted from the
cross-targeted model.
Test coverage:
GenerativeEnumsProvisionTests.fs(enum field attributes);tests/BasicErasedProvisionTests.fs(parameter custom attributes in theNameOfexample).
Units of Measure
See Units of Measure in F# Type Providers for SI units, compound units, custom units, and multi-argument annotation.
Non-Nullability and HideObjectMethods
nonNullable
Set nonNullable = true when constructing a ProvidedTypeDefinition to mark the type with
[<AllowNullLiteral(false)>]. This prevents the F# null literal from being used with the
provided type, which is the standard F# behaviour for non-nullable reference types.
let myType = ProvidedTypeDefinition(asm, ns, "Row", Some typeof<obj>, nonNullable = true)
// myType.NonNullable = true
// myType.GetCustomAttributesData() contains AllowNullLiteralAttribute
The NonNullable property can be read back to check whether the flag was set.
hideObjectMethods
Set hideObjectMethods = true to suppress the standard .NET Object methods (ToString,
GetHashCode, Equals) from appearing in IntelliSense for instances of the provided type.
This is useful for simple record-like or data types where the inherited methods add noise.
let myType =
ProvidedTypeDefinition(asm, ns, "Connection", Some typeof<obj>, hideObjectMethods = true)
// myType.HideObjectMethods = true
Abstract Classes and Base Constructors (Generative)
let baseType =
ProvidedTypeDefinition(provAsm, ns, "AnimalBase", Some typeof<obj>,
isErased = false, isAbstract = true, isSealed = false)
// Implicit constructor (fields become accessible with Expr.GlobalVar)
let baseCtor =
ProvidedConstructor(
[ ProvidedParameter("name", typeof<string>) ],
invokeCode = fun _args -> <@@ () @@>,
IsImplicitConstructor = true)
baseType.AddMember baseCtor
// Derived type
let derivedType =
ProvidedTypeDefinition(provAsm, ns, "Dog", Some (baseType :> System.Type), isErased = false)
let derivedCtor =
ProvidedConstructor(
[ ProvidedParameter("name", typeof<string>) ],
invokeCode = fun args -> <@@ () @@>)
derivedCtor.BaseConstructorCall <- fun args -> (baseCtor :> System.Reflection.ConstructorInfo), [args.[1]]
derivedType.AddMember derivedCtor
provAsm.AddTypes [baseType; derivedType]
Test coverage:
GenerativeAbstractClassesTests.fs.
type MyParameterisedProvider = new: config: obj -> MyParameterisedProvider
--------------------
new: config: obj -> MyParameterisedProvider
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
val string: value: 'T -> string
--------------------
type string = System.String
type List<'T> = interface ICollection<'T> interface IEnumerable<'T> interface IEnumerable interface IList<'T> interface IReadOnlyCollection<'T> interface IReadOnlyList<'T> interface ICollection interface IList new: unit -> unit + 2 overloads member Add: item: 'T -> unit ...
<summary>Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.</summary>
<typeparam name="T">The type of elements in the list.</typeparam>
--------------------
System.Collections.Generic.List() : System.Collections.Generic.List<'T>
System.Collections.Generic.List(collection: System.Collections.Generic.IEnumerable<'T>) : System.Collections.Generic.List<'T>
System.Collections.Generic.List(capacity: int) : System.Collections.Generic.List<'T>
type MyGenerativeProvider = new: config: obj -> MyGenerativeProvider
--------------------
new: config: obj -> MyGenerativeProvider
type EventHandler<'TEventArgs> = new: object: obj * method: nativeint -> unit member BeginInvoke: sender: obj * e: 'TEventArgs * callback: AsyncCallback * object: obj -> IAsyncResult member EndInvoke: result: IAsyncResult -> unit member Invoke: sender: obj * e: 'TEventArgs -> unit
<summary>Represents the method that will handle an event when the event provides data.</summary>
<param name="sender">The source of the event.</param>
<param name="e">An object that contains the event data.</param>
<typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
--------------------
type EventHandler = new: object: obj * method: nativeint -> unit member BeginInvoke: sender: obj * e: EventArgs * callback: AsyncCallback * object: obj -> IAsyncResult member EndInvoke: result: IAsyncResult -> unit member Invoke: sender: obj * e: EventArgs -> unit
<summary>Represents the method that will handle an event that has no event data.</summary>
<param name="sender">The source of the event.</param>
<param name="e">An object that contains no event data.</param>
<summary>Provides access to custom attribute data for assemblies, modules, types, members and parameters that are loaded into the reflection-only context.</summary>
type ObsoleteAttribute = inherit Attribute new: unit -> unit + 2 overloads member DiagnosticId: string member IsError: bool member Message: string member UrlFormat: string
<summary>Marks the program elements that are no longer in use. This class cannot be inherited.</summary>
--------------------
ObsoleteAttribute() : ObsoleteAttribute
ObsoleteAttribute(message: string) : ObsoleteAttribute
ObsoleteAttribute(message: string, error: bool) : ObsoleteAttribute
val string: value: 'T -> string
--------------------
type string = String
[<Struct>] type CustomAttributeTypedArgument = new: value: obj -> unit + 1 overload member Equals: obj: obj -> bool + 1 overload member GetHashCode: unit -> int member ToString: unit -> string static member (<>) : left: CustomAttributeTypedArgument * right: CustomAttributeTypedArgument -> bool static member (=) : left: CustomAttributeTypedArgument * right: CustomAttributeTypedArgument -> bool member ArgumentType: Type member Value: obj
<summary>Represents an argument of a custom attribute in the reflection-only context, or an element of an array argument.</summary>
--------------------
CustomAttributeTypedArgument ()
CustomAttributeTypedArgument(value: obj) : CustomAttributeTypedArgument
CustomAttributeTypedArgument(argumentType: Type, value: obj) : CustomAttributeTypedArgument
type Expr = override Equals: obj: objnull -> bool member GetFreeVars: unit -> Var seq member Substitute: substitution: (Var -> Expr option) -> Expr member ToString: full: bool -> string static member AddressOf: target: Expr -> Expr static member AddressSet: target: Expr * value: Expr -> Expr static member Application: functionExpr: Expr * argument: Expr -> Expr static member Applications: functionExpr: Expr * arguments: Expr list list -> Expr static member Call: methodInfo: MethodInfo * arguments: Expr list -> Expr + 1 overload static member CallWithWitnesses: methodInfo: MethodInfo * methodInfoWithWitnesses: MethodInfo * witnesses: Expr list * arguments: Expr list -> Expr + 1 overload ...
--------------------
type Expr<'T> = inherit Expr member Raw: Expr with get
type ReflectedDefinitionAttribute = inherit Attribute new: unit -> ReflectedDefinitionAttribute + 1 overload member IncludeValue: bool with get
--------------------
new: unit -> ReflectedDefinitionAttribute
new: includeValue: bool -> ReflectedDefinitionAttribute
<summary>Represents type declarations: class types, interface types, array types, value types, enumeration types, type parameters, generic type definitions, and open or closed constructed generic types.</summary>
<summary>Discovers the attributes of a class constructor and provides access to constructor metadata.</summary>
FSharp.TypeProviders.SDK