|
F# Manual
- Contents
C# from F# - using C# and the .NET APIs from F#
Assemblies written in C# and other .NET languages can be directly accessed from F#. This makes F# an amazingly powerful language given the huge number of libraries available. This section describes how various to use various .NET constructs from F#. A working knowledge of C# or another .NET language is assumed. Longer examples of interoperability can be found in the samples that come with the F# compiler. Types. Types are accessed using the "Namespace.Type" notation. You may simply use "Type" if an "open Namespace" declaration has been given. Constructors. Instances of .NET types are constructed using the expression form new Type(arg1,...,argN). See also construction expressions for instances which are extensions and/or implementations as described further below. Instance Methods. Instance members are accessed using "obj.Method(arg1,...,argN)" or "obj.Property" or "obj.Field" Instance "Getter" Properties. Setter properties are accessed using the syntax obj.Property. Instance "Setter" Properties. Setter properties are accessed using the syntax obj.Property <- expr. Static Methods. Static members are accessed using "Namespace.Type.Method(arg1,...,argN)" or "Type.Method(arg1,...,argN)". Static "Getter" Properties. Static setter properties are accessed using the syntax Type.Property. Static "Setter" Properties. Static setter properties are accessed using the syntax Type.Property <- expr. Fields. Instance and static fields are accessed using the same synax as properties.
Events. Access these using the Add/Remove/Fire methods for the event. Void. Methods returning type "void" returning type "unit"
Delegates. Delegates values can be constructed by supplying an argument of the corresponding function type.
F# values that implement .NET interfaces can be generated using object expressions. Sometimes extra annotations are needed to get the program to typecheck, e.g. downcasts using "(<expr> :> <type>)" and some extra type annotations may be required to help resolve overloading. This works by a process of strict left-to-right, outer-to-inner type checking, i.e. if extra type information is needed you should add it either to the expression where the information is required itself or one a related expression to the left of the expression. All F# and .NET values are convertible to the 'object' type - in F# this is called obj. Thus F# values can be stored in heterogeneous collections such as System.Collections.ArrayList. Conversions in ther reverse direction are dynamically checked and may raise failures. F# supports the following operators to convert to and from the 'object' type:
All .NET and F# object types are convertible to their 'base' types. Other F# types (records, discriminated unions and abstract types) are not arranged in a hierarchy and the only 'base' type of most F# types is obj. .NET values can also be re-converted to sub types, with possible runtime exceptions. F# supports the following operators to convert .NET values to and from their base types:
The expression "e :? ty" is equivalent to a dynamic type test. A warning will be emitted if the type of e cannot be statically determined to be a subtype of ty (statically determined == using the inference information available at this point in the typechecking of the program, where inference proceeds left-to-right through the program). An error will be reported if the type test will always succeed. This is especially useful for testing for classes of .NET exceptions, e.g.
Valid casts are those between .NET types related by class extension or interface inheritance, and also between F# reference types and the type 'obj' (i.e. System.Object). In F# code, .NET reference types can be null values. Nulls can be checked and created using the following contructs:
Here is an example where we do a null test in a pattern match:
Here is an example where we do a null test in an expression:
At the source language level null is not a valid value for other F# types, and if you attempt to use nullchecking constructs with types such as int or int list then the compiler will complain.] It will also complain if you do null checks or generate null values for a variable type 'a. However note that null will be used as a representation for some F# values, as described in the section on export interopability, though that is largely a choice made by the compiler. On the whole F# code will often assume values .NET reference type values are non-null, so it is good style to eliminate the nullness and make it explicit as early as possible. You should only really need null checks at the API boundary. Strings (which are the .NET reference type System.String) will only be null if returned as such by a .NET API. String values returned by a .NET API should always be checked for nullness, since F# code generally assumes string values are non-null. null is not otherwise part of the F# type system. Thus the compiler does not check that you insert null checks after API calls. If you do not insert a null check then just as in C# you will get a null pointer exception whenever you try to call a method on the object.
Aside: It is likely that a future version of F# will add some kind of refinement types to cover these circumstances, along with an analysis of the .NET libraries to determine nullness. Some other Microsoft Research projects are proposing and annotating common .NET libraries with nullness information, and F# would piggy-back off that. Aside: It is possible to generate null values of any F# type through a number of backdoor routes, e.g. by taking the null object value and casting it to an F# type, or by using reflection to create a null value of an F# type, or by using other .NET languages. The runtime behaviour of such programs is unspecified and may raise a NullReferenceException. However, overall memory safety will not compromised as a result of this. Advanced Topics
Advanced Topics: Resolving overloading Documentation in progress. Advanced Topics: Accessing generic types Documentation in progress. |