Attributes (F#)

Attributes enable metadata to be applied to a programming construct.

Syntax

[<target:attribute-name(arguments)>]

Remarks

In the previous syntax, the target is optional and, if present, specifies the kind of program entity that the attribute applies to. Valid values for target are shown in the table that appears later in this document.

The attribute-name refers to the name (possibly qualified with namespaces) of a valid attribute type, with or without the suffix Attribute that is usually used in attribute type names. For example, the type ObsoleteAttribute can be shortened to just Obsolete in this context.

The arguments are the arguments to the constructor for the attribute type. If an attribute has a parameterless constructor, the argument list and parentheses can be omitted. Attributes support both positional arguments and named arguments. Positional arguments are arguments that are used in the order in which they appear. Named arguments can be used if the attribute has public properties. You can set these by using the following syntax in the argument list.

property-name = property-value

Such property initializations can be in any order, but they must follow any positional arguments. The following is an example of an attribute that uses positional arguments and property initializations:

open System.Runtime.InteropServices

[<DllImport("kernel32", SetLastError=true)>]
extern bool CloseHandle(nativeint handle)

In this example, the attribute is DllImportAttribute, here used in shortened form. The first argument is a positional parameter and the second is a property.

Attributes are a .NET programming construct that enables an object known as an attribute to be associated with a type or other program element. The program element to which an attribute is applied is known as the attribute target. The attribute usually contains metadata about its target. In this context, metadata could be any data about the type other than its fields and members.

Attributes in F# can be applied to the following programming constructs: functions, methods, assemblies, modules, types (classes, records, structures, interfaces, delegates, enumerations, unions, and so on), constructors, properties, fields, parameters, type parameters, and return values. Attributes are not allowed on let bindings inside classes, expressions, or workflow expressions.

Typically, the attribute declaration appears directly before the declaration of the attribute target. Multiple attribute declarations can be used together, as follows:

[<Owner("Jason Carlson")>]
[<Company("Microsoft")>]
type SomeType1 =

You can query attributes at run time by using .NET reflection.

You can declare multiple attributes individually, as in the previous code example, or you can declare them in one set of brackets if you use a semicolon to separate the individual attributes and constructors, as follows:

[<Owner("Darren Parker"); Company("Microsoft")>]
type SomeType2 =

Typically encountered attributes include the Obsolete attribute, attributes for security considerations, attributes for COM support, attributes that relate to ownership of code, and attributes indicating whether a type can be serialized. The following example demonstrates the use of the Obsolete attribute.

open System

[<Obsolete("Do not use. Use newFunction instead.")>]
let obsoleteFunction x y =
  x + y

let newFunction x y =
  x + 2 * y

// The use of the obsolete function produces a warning.
let result1 = obsoleteFunction 10 100
let result2 = newFunction 10 100

For the attribute targets assembly and module, you apply the attributes to a top-level do binding in your assembly. You can include the word assembly or ``module`` in the attribute declaration, as follows:

open System.Reflection
[<assembly:AssemblyVersionAttribute("1.0.0.0")>]
[<``module``:MyCustomModuleAttribute>]
do
   printfn "Executing..."

If you omit the attribute target for an attribute applied to a do binding, the F# compiler attempts to determine the attribute target that makes sense for that attribute. Many attribute classes have an attribute of type System.AttributeUsageAttribute that includes information about the possible targets supported for that attribute. If the System.AttributeUsageAttribute indicates that the attribute supports functions as targets, the attribute is taken to apply to the main entry point of the program. If the System.AttributeUsageAttribute indicates that the attribute supports assemblies as targets, the compiler takes the attribute to apply to the assembly. Most attributes do not apply to both functions and assemblies, but in cases where they do, the attribute is taken to apply to the program's main function. If the attribute target is specified explicitly, the attribute is applied to the specified target.

Although you do not usually need to specify the attribute target explicitly, valid values for target in an attribute along with examples of usage are shown in the following table:

Attribute target Example
assembly
[<assembly: AssemblyVersion("1.0.0.0")>]
module
[<``module``: MyCustomAttributeThatWorksOnModules>]
method
[<MyCustomAttributeThatWorksOnMethods>]
let someFunction() = 42
class
[<MyCustomAttributeThatWorksOnClasses>]
type MyClass(myValue: int) =
    member _.MyValue = myValue
struct
[<MyCustomAttributeThatWorksOnStructs>]
[<Struct>]
type MyStruct(myValue: int) =
    member _.MyValue = myValue
interface
[<MyCustomAttributeThatWorksOnInterfaces>]
type MyInterface =
    abstract member Prop: string
enum
[<MyCustomAttributeThatWorksOnEnums>]
type Color =
    | Red = 0
    | Green = 1
    | Blue = 2
constructor
type MyClass(myValue: int) =
    member _.MyValue = myValue

    [<MyCustomAttributeThatWorksOnCtors>]
    new () = MyClass 42
return
let function1 x : [<return: MyCustomAttributeThatWorksOnReturns>] int = x + 1
field
[<DefaultValue>] val mutable x: int
property
[<Obsolete>] this.MyProperty = x
param
member this.MyMethod([<Out>] x : ref<int>) = x := 10
type
[<type: StructLayout(LayoutKind.Sequential)>]
type MyStruct =
  struct
    val x : byte
    val y : int
  end

See also