do Bindings in Classes

A do binding in a class definition performs actions when the object is constructed or, for a static do binding, when the type is first used.


[static] do expression


A do binding appears together with or after let bindings but before member definitions in a class definition. Although the do keyword is optional for do bindings at the module level, it is not optional for do bindings in a class definition.

For the construction of every object of any given type, non-static do bindings and non-static let bindings are executed in the order in which they appear in the class definition. Multiple do bindings can occur in one type. The non-static let bindings and the non-static do bindings become the body of the primary constructor. The code in the non-static do bindings section can reference the primary constructor parameters and any values or functions that are defined in the let bindings section.

Non-static do bindings can access members of the class as long as the class has a self identifier that is defined by an as keyword in the class heading, and as long as all uses of those members are qualified with the self identifier for the class.

Because let bindings initialize the private fields of a class, which is often necessary to guarantee that members behave as expected, do bindings are usually put after let bindings so that code in the do binding can execute with a fully initialized object. If your code attempts to use a member before the initialization is complete, an InvalidOperationException is raised.

Static do bindings can reference static members or fields of the enclosing class but not instance members or fields. Static do bindings become part of the static initializer for the class, which is guaranteed to execute before the class is first used.

Attributes are ignored for do bindings in types. If an attribute is required for code that executes in a do binding, it must be applied to the primary constructor.

In the following code, a class has a static do binding and a non-static do binding. The object has a constructor that has two parameters, a and b, and two private fields are defined in the let bindings for the class. Two properties are also defined. All of these are in scope in the non-static do bindings section, as is illustrated by the line that prints all those values.

open System

type MyType(a: int, b: int) as this =
    inherit Object()
    let x = 2 * a
    let y = 2 * b
    do printfn "Initializing object %d %d %d %d %d %d" a b x y (this.Prop1) (this.Prop2)
    static do printfn "Initializing MyType."
    member this.Prop1 = 4 * x
    member this.Prop2 = 4 * y

    override this.ToString() =
        System.String.Format("{0} {1}", this.Prop1, this.Prop2)

let obj1 = new MyType(1, 2)

The output is as follows.

Initializing MyType.
Initializing object 1 2 2 4 8 16

See also