Access control refers to declaring which clients can use certain program elements, such as types, methods, and functions.
Basics of Access Control
In F#, the access control specifiers
private can be applied to modules, types, methods, value definitions, functions, properties, and explicit fields.
publicindicates that the entity can be accessed by all callers.
internalindicates that the entity can be accessed only from the same assembly.
privateindicates that the entity can be accessed only from the enclosing type or module.
The access specifier
protected is not used in F#, although it is acceptable if you are using types authored in languages that do support
protected access. Therefore, if you override a protected method, your method remains accessible only within the class and its descendents.
In general, the specifier is put in front of the name of the entity, except when a
inline specifier is used, which appear after the access control specifier.
If no access specifier is used, the default is
public, except for
let bindings in a type, which are always
private to the type.
Signatures in F# provide another mechanism for controlling access to F# program elements. Signatures are not required for access control. For more information, see Signatures.
Rules for Access Control
Access control is subject to the following rules:
Inheritance declarations (that is, the use of
inheritto specify a base class for a class), interface declarations (that is, specifying that a class implements an interface), and abstract members always have the same accessibility as the enclosing type. Therefore, an access control specifier cannot be used on these constructs.
Accessibility for individual cases in a discriminated union is determined by the accessibility of the discriminated union itself. That is, a particular union case is no less accessible than the union itself.
Accessibility for individual fields of a record type cannot is determined by the accessibility of the record itself. That is, a particular record label is no less accessible than the record itself.
The following code illustrates the use of access control specifiers. There are two files in the project,
Module2.fs. Each file is implicitly a module. Therefore, there are two modules,
Module2. A private type and an internal type are defined in
Module1. The private type cannot be accessed from
Module2, but the internal type can.
// Module1.fs module Module1 // This type is not usable outside of this file type private MyPrivateType() = // x is private since this is an internal let binding let x = 5 // X is private and does not appear in the QuickInfo window // when viewing this type in the Visual Studio editor member private this.X() = 10 member this.Z() = x * 100 type internal MyInternalType() = let x = 5 member private this.X() = 10 member this.Z() = x * 100 // Top-level let bindings are public by default, // so "private" and "internal" are needed here since a // value cannot be more accessible than its type. let private myPrivateObj = new MyPrivateType() let internal myInternalObj = new MyInternalType() // let bindings at the top level are public by default, // so result1 and result2 are public. let result1 = myPrivateObj.Z let result2 = myInternalObj.Z
The following code tests the accessibility of the types created in
// Module2.fs module Module2 open Module1 // The following line is an error because private means // that it cannot be accessed from another file or module // let private myPrivateObj = new MyPrivateType() let internal myInternalObj = new MyInternalType() let result = myInternalObj.Z