F# Quick Guides: Object Oriented Programming
Hello! As I mentioned in my earlier post, the F# team is tremendously proud of being a part of Visual Studio 2010. We have worked long and hard to get to this point, and we are very excited with the release of the first fully supported F# language.
To kick off the release of F#, I wanted to share with you a few quick guides to F#. The point of the quick guides is not to be a comprehensive language reference, but more of a “I know how to do this in C#, but how do I do it in F#” guide. As a result, the guides will be organized in table format, with F# code in the left, and corresponding C# code on the right.
The first guide will be about Object Oriented Programming in F#. You can download a copy of the F# Object Oriented Programming Quick Guide to make it easy for offline browsing. In addition, I’ve provided the F# source code for all the examples on the F# Quick Guides Code Gallery page.
Without further ado, here is the F# Quick Guide for Object Oriented Programming.
Language Feature |
F# |
C# |
Classes with properties and default constructor |
type Vector(x : float, y : float) = member this.X = x member this.Y = y
// Usage: let v = Vector(10., 10.) let x = v.X let y = v.Y |
public class Vector { double x; double y;
public Vector(double x, double y) { this.x = x; this.y = y; } public double X { get { return this.x; } } public double Y { get { return this.y; } } }
// Usage: Vector v = new Vector(10, 10); double x = v.X; double y = v.Y; |
Structs with properties |
[<Struct>] type Vector(x : float, y : float) = member this.X = x member this.Y = y
// Usage: let v = Vector(10., 10.) let x = v.X let y = v.Y |
public struct Vector { double x; double y;
public Vector(double x, double y) { this.x = x; this.y = y; } public double X { get { return this.x; } } public double Y { get { return this.y; } } }
// Usage: Vector v = new Vector(10, 10); double x = v.X; double y = v.Y; |
Multiple constructors |
type Vector(x : float, y : float) = member this.X = x member this.Y = y new(v : Vector, s) = Vector(v.X * s, v.Y * s)
// Usage: let v = Vector(10., 10.) let w = new Vector(v, 0.5) |
public class Vector { double x; double y; public Vector(double x, double y) { this.x = x; this.y = y; } public Vector(Vector v, double s) : this(v.x * s, v.y * s) { } }
// Usage: Vector v = new Vector(10, 10); Vector w = new Vector(v, 0.5); |
Member functions |
type Vector(x : float, y : float) = member this.Scale(s : float) = Vector(x * s, y * s)
// Usage: let v = Vector(10., 10.) let v2 = v.Scale(0.5) |
public class Vector { double x; double y;
public Vector(double x, double y) { this.x = x; this.y = y; } public double Scale(double s) { return new Vector(this.x * s, this.y * s); } }
// Usage: Vector v = new Vector(10, 10); Vector v2 = v.Scale(0.5); |
Operators |
type Vector(x, y) = member this.X = x member this.Y = y static member (*) (a : Vector, b : Vector) = a.X * b.X + a.Y + b.Y
// Usage: let x = Vector(2., 2.) let y = Vector(3., 3.) let dp = x * y
|
public class Vector { double x; double y; public Vector(double x, double y) { this.x = x; this.y = y; } public double X { get { return this.x; } } public double Y { get { return this.y; } } public static double operator * ( Vector v1, Vector v2) { return v1.x * v2.x + v1.y * v2.y; } }
// Usage: Vector x = new Vector(2, 2); Vector y = new Vector(3, 3); double dp = x * y; |
Static members and properties |
type Vector(x, y) = member this.X = x member this.Y = y static member Dot(a : Vector, b : Vector) = a.X * b.X + a.Y + b.Y static member NormX = Vector(1., 0.)
// Usage: let x = Vector(2., 2.) let y = Vector.NormX let dp = Vector.Dot(x, y) |
public class Vector { double x; double y; public Vector(double x, double y) { this.x = x; this.y = y; } public double X { get { return this.x; } } public double Y { get { return this.y; } } public static double Dot(Vector v1, Vector v2) { return v1.x * v2.x + v1.y * v2.y; } public static Vector NormX { get { return new Vector(1, 0); } } }
// Usage: Vector x = new Vector(2, 2); Vector y = Vector.NormX; double dp = Vector.Dot(x, y); |
Class properties that use let value computations in the constructor |
type Vector(x, y) = let mag = sqrt(x * x + y * y) let rad = if x = 0. && y = 0. then 0. else if x >= 0. then asin(y / mag) else (-1. * asin(y / mag)) + Math.PI member this.Mag = mag member this.Rad = rad
// Usage: let v = Vector(10., 10.) let mag = v.Mag let rad = v.Rad
|
public class Vector { double mag = 0.0; double rad = 0.0; public Vector(double x, double y) { this.mag = Math.Sqrt(x * x + y * y); if (x == 0.0 && y == 0.0) rad = 0.0; else if (x >= 0.0) rad = Math.Asin(y / mag); else rad = (-1.0 * Math.Asin(y / mag)) + Math.PI; } public double Mag { get { return this.mag; } } public double Rad { get { return this.rad; } } }
// Usage: Vector v = new Vector(10, 10); double mag = v.Mag; double rad = v.Rad; |
Class members that use private function values |
type Vector(x, y) = let rotate a = let x' = x * cos a – y * sin a let y' = y * sin a + y * cos a new Vector(x', y') member this.RotateByDegrees(d) = rotate (d * Math.PI / 180.) member this.RotateByRadians(r) = rotate r
// Usage: let v = Vector(10., 0.) let x = v.RotateByDegrees(90.) let y = v.RotateByRadians(Math.PI / 6.)
|
public class Vector { double x = 0.0; double y = 0.0; Vector rotate(double a) { double xx = this.x * Math.Cos(a) – this.y * Math.Sin(a); double yy = this.y * Math.Sin(a) + this.y * Math.Cos(a); return new Vector(xx, yy); } public Vector(double x, double y) { this.x = x; this.y = y; } public Vector RotateByDegrees(double d) { return rotate(d * Math.PI / 180); } public Vector RotateByRadians(double r) { return rotate(r); } }
// Usage: Vector v = new Vector(10, 10); Vector x = v.RotateByDegrees(90.0); Vector y = v.RotateByRadians(Math.PI / 6.0);
|
Overloading members |
type Car() = member this.Drive() = this.Drive(10) () member this.Drive(mph : int) = // Do something ()
// Usage: let c = Car() c.Drive() c.Drive(10) |
public class Car { public void Drive() { Drive(10); } public void Drive(int mph) { // Do something } }
// Usage: Car c = new Car(); c.Drive(); c.Drive(10); |
Mutable fields in a class with get/set properties |
type MutableVector(x : float, y : float) = let mutable cx = x let mutable cy = y member this.X with get() = cx and set(v) = cx <- v member this.Y with get() = cy and set(v) = cy <- v member this.Length = sqrt(x * x + y * y)
// Usage: let v = MutableVector(2., 2.) let len1 = v.Length v.X <- 3. v.Y <- 3. let len2 = v.Length |
public class MutableVector { public MutableVector(double x, double y) { this.X = x; this.Y = y; } public double X { get; set; } public double Y { get; set; } public double Length { get { return Math.Sqrt( this.X * this.X + this.Y * this.Y); } } }
// Usage: MutableVector v = new MutableVector(2.0, 2.0); double len1 = v.Length; v.X = 3.0; v.Y = 3.0; double len2 = v.Length; |
Generic classes and function arguments |
type Factory<'T>(f : unit -> 'T) = member this.Create() = f()
// Usage: let strings = Factory<string>( fun () -> "Hello!") let res = strings.Create() let ints = Factory<int>(fun () -> 10) let res = ints.Create()
|
public class Factory<T> { Func<T> creator; public Factory(Func<T> f) { this.creator = f; } public T Create() { return this.creator(); } }
// Usage: Factory<string> strings = new Factory<string>(() => "Hello"); string res1 = strings.Create(); Factory<int> ints = new Factory<int>(() => 10); int res2 = ints.Create(); |
Generic classes and methods |
type Container<'T>(a : 'T) = member this.Convert<'U>(f : 'T -> 'U) = f a
// Usage: let c = new Container<int>(10) let b = c.Convert(fun a -> a.ToString()) |
public class Container<T> { private T value; public Container(T t) { this.value = t; } public U Convert<U>(Func<T, U> f) { return f(this.value); } }
// Usage: Container<int> c = new Container<int>(10); string result = c.Convert(n => n.ToString()); |
Extension methods |
type List<'T> with member this.MyExtensionMethod() = this |> Seq.map (fun a -> a.ToString())
// Usage: let c = [1; 2; 3] let d = c.MyExtensionMethod() |
public static class ExtensionMethods { public static IEnumerable<string> MyExtensionMethod<T>(this List<T> a) { return a.Select(s => s.ToString()); } }
// Usage: List<int> l = new List<int> { 1, 2, 3 }; IEnumerable<string> res = l.MyExtensionMethod();
|
Extension properties |
type List<'T> with member this.MyExtensionProp = this |> Seq.map (fun a -> a.ToString())
// Usage: let c = [1; 2; 3] let d = c.MyExtensionProp |
N/A. C# does not support this feature. |
Indexers |
type Table() = member this.Item with get(key : string) = int key
// Usage: let tab = Table() let x = tab.["10"] let y = tab.["12"] |
public class Table { public int this[string key] { get { return Convert.ToInt32(key); } } }
// Usage: Table tab = new Table(); int x = tab["10"]; int y = tab["12"]; |
Indexed Properties |
type Table() = member this.Values with get(key : string) = int key member this.MultipleValues with get(key1, key2) = key1 + key2 // Usage: let tab = Table() let x = tab.Values("10") let y = tab.Values("12") let a = tab.MultipleValues(10, 5) |
N/A. C# does not support this feature. |
Abstract classes |
[<AbstractClass>] type Shape() = abstract Name : string with get abstract Scale : float -> Shape |
public abstract class Shape { public abstract string Name { get; } public abstract Shape Scale(double scale); } |
Derive from a base class and overriding base methods with generics |
[<AbstractClass>] type Shape<'T>() = abstract Name : string with get abstract Scale : float -> 'T
type Vector(angle, mag) = inherit Shape<Vector>() member this.Angle = angle member this.Mag = makg override this.Name = "Vector" override this.Scale(factor) = Vector(angle, mag * factor)
// Usage: let v = Vector(45., 10.) let v2 = v.Scale(0.5) |
public abstract class Shape<T> { public abstract string Name { get; } public abstract T Scale(double scale); }
public class Vector : Shape<Vector> { double angle; double mag; public double Angle {get{return angle;}} public double Mag {get{return mag;}} public Vector(double angle, double mag) { this.angle = angle; this.mag = mag; } public override string Name { get { return "Vector"; } } public override Vector Scale(double scale) { return new Vector(this.Angle, this.Mag * scale); } } // Usage: Vector v = new Vector(45, 10); Vector v2 = v.Scale(0.5); |
Calling a base class method |
type Animal() = member this.Rest() = // Rest for the animal ()
type Dog() = inherit Animal() member this.Run() = // Run base.Rest() // Usage: let brian = new Dog() brian.Run() |
public class Animal { public void Rest() { // Rest for the animal } } public class Dog : Animal { public void Run() { // Run base.Rest(); } } // Usage: Dog brian = new Dog(); brian.Run(); |
Implementing an interface |
type IVector = abstract Mag : double with get abstract Scale : float -> IVector
type Vector(x, y) = interface IVector with member this.Mag = sqrt(x * x + y * y) member this.Scale(s) = Vector(x * s, y * s) :> IVector member this.X = x member this.Y = y // Usage: let v = new Vector(1., 2.) :> IVector let w = v.Scale(0.5) let mag = w.Mag
|
interface IVector { double Mag { get; } IVector Scale(double s); } class Vector : IVector { double x; double y; public Vector(double x, double y) { this.x = x; this.y = y; } public double X { get { return this.x; } } public double Y { get { return this.y; } } public double Mag { get { return Math.Sqrt( this.x * this.x + this.y * this.y); } } public IVector Scale(double s) { return new Vector(this.x * s, this.y * s); } } // Usage: IVector v = new Vector(1, 2); IVector w = v.Scale(0.5); double mag = w.Mag; |
Implementing an interface with object expressions |
type ICustomer = abstract Name : string with get abstract Age : int with get
let CreateCustomer name age = { new ICustomer with member this.Name = name member this.Age = age } // Usage: let c = CreateCustomer "Snoopy" 16 let d = CreateCustomer "Garfield" 5 |
N/A. C# does not support creating object expressions. |
Events |
type BarkArgs(msg:string) = inherit EventArgs() member this.Message = msg
type BarkDelegate = delegate of obj * BarkArgs -> unit
type Dog() = let ev = new Event<BarkDelegate, BarkArgs>() member this.Bark(msg) = ev.Trigger(this, new BarkArgs(msg)) [<CLIEvent>] member this.OnBark = ev.Publish // Usage: let snoopy = new Dog() snoopy.OnBark.Add( fun ba -> printfn "%s" (ba.Message)) snoopy.Bark("Hello") |
public class BarkArgs : EventArgs { private string msg; public BarkArgs(string msg) { this.msg = msg; } public string Message { get { return this.msg; } } }
public delegate void BarkDelegate( Object sender, BarkArgs args);
class Dog { public event BarkDelegate OnBark; public void Bark(string msg) { OnBark(this, new BarkArgs(msg)); } } // Usage: Dog snoopy = new Dog(); snoopy.OnBark += new BarkDelegate( (sender, msg) => Console.WriteLine(msg.Message)); snoopy.Bark("Hello"); |
Explicit class constructor |
type Vector = val mutable x : float val mutable y : float new() = {x = 0.; y = 0.}
// Usage: let v = Vector() v.x <- 10. v.y <- 10. |
|
Explicit public fields |
type Vector() = [<DefaultValue()>] val mutable x : int [<DefaultValue()>] val mutable y : int
// Usage: let v = Vector() v.x <- 10 v.y <- 10 |
public class Vector { public int x; public int y; }
// Usage: Vector v = new Vector(); v.x = 10; v.y = 10; |
Explicit struct definition |
[<Struct>] type Vector = val mutable public x : int val mutable public y : int
// Usage: let mutable v = Vector() v.x <- 10 v.y <- 10 |
public struct Vector { public int x; public int y; }
// Usage: Vector v = new Vector(); v.x = 10; v.y = 10; |