layout: post
title: “Anything C# can do…”
description: “A whirlwind tour of object-oriented code in F#”
nav: why-use-fsharp
seriesId: “Why use F#?”
seriesOrder: 29

categories: [Completeness]

As should be apparent, you should generally try to prefer functional-style code over object-oriented code in F#, but in some situations, you may need all the features of a fully fledged OO language ? classes, inheritance, virtual methods, etc.

So just to conclude this section, here is a whirlwind tour of the F# versions of these features.

Some of these will be dealt with in much more depth in a later series on .NET integration. But I won’t cover some of the more obscure ones, as you can read about them in the MSDN documentation if you ever need them.

Classes and interfaces

First, here are some examples of an interface, an abstract class, and a concrete class that inherits from the abstract class.

  1. // interface
  2. type IEnumerator<'a> =
  3. abstract member Current : 'a
  4. abstract MoveNext : unit -> bool
  5. // abstract base class with virtual methods
  6. [<AbstractClass>]
  7. type Shape() =
  8. //readonly properties
  9. abstract member Width : int with get
  10. abstract member Height : int with get
  11. //non-virtual method
  12. member this.BoundingArea = this.Height * this.Width
  13. //virtual method with base implementation
  14. abstract member Print : unit -> unit
  15. default this.Print () = printfn "I'm a shape"
  16. // concrete class that inherits from base class and overrides
  17. type Rectangle(x:int, y:int) =
  18. inherit Shape()
  19. override this.Width = x
  20. override this.Height = y
  21. override this.Print () = printfn "I'm a Rectangle"
  22. //test
  23. let r = Rectangle(2,3)
  24. printfn "The width is %i" r.Width
  25. printfn "The area is %i" r.BoundingArea
  26. r.Print()

Classes can have multiple constructors, mutable properties, and so on.

  1. type Circle(rad:int) =
  2. inherit Shape()
  3. //mutable field
  4. let mutable radius = rad
  5. //property overrides
  6. override this.Width = radius * 2
  7. override this.Height = radius * 2
  8. //alternate constructor with default radius
  9. new() = Circle(10)
  10. //property with get and set
  11. member this.Radius
  12. with get() = radius
  13. and set(value) = radius <- value
  14. // test constructors
  15. let c1 = Circle() // parameterless ctor
  16. printfn "The width is %i" c1.Width
  17. let c2 = Circle(2) // main ctor
  18. printfn "The width is %i" c2.Width
  19. // test mutable property
  20. c2.Radius <- 3
  21. printfn "The width is %i" c2.Width

Generics

F# supports generics and all the associated constraints.

  1. // standard generics
  2. type KeyValuePair<'a,'b>(key:'a, value: 'b) =
  3. member this.Key = key
  4. member this.Value = value
  5. // generics with constraints
  6. type Container<'a,'b
  7. when 'a : equality
  8. and 'b :> System.Collections.ICollection>
  9. (name:'a, values:'b) =
  10. member this.Name = name
  11. member this.Values = values

Structs

F# supports not just classes, but the .NET struct types as well, which can help to boost performance in certain cases.

  1. type Point2D =
  2. struct
  3. val X: float
  4. val Y: float
  5. new(x: float, y: float) = { X = x; Y = y }
  6. end
  7. //test
  8. let p = Point2D() // zero initialized
  9. let p2 = Point2D(2.0,3.0) // explicitly initialized

Exceptions

F# can create exception classes, raise them and catch them.

  1. // create a new Exception class
  2. exception MyError of string
  3. try
  4. let e = MyError("Oops!")
  5. raise e
  6. with
  7. | MyError msg ->
  8. printfn "The exception error was %s" msg
  9. | _ ->
  10. printfn "Some other exception"

Extension methods

Just as in C#, F# can extend existing classes with extension methods.

  1. type System.String with
  2. member this.StartsWithA = this.StartsWith "A"
  3. //test
  4. let s = "Alice"
  5. printfn "'%s' starts with an 'A' = %A" s s.StartsWithA
  6. type System.Int32 with
  7. member this.IsEven = this % 2 = 0
  8. //test
  9. let i = 20
  10. if i.IsEven then printfn "'%i' is even" i

Parameter arrays

Just like C#’s variable length “params” keyword, this allows a variable length list of arguments to be converted to a single array parameter.

  1. open System
  2. type MyConsole() =
  3. member this.WriteLine([<ParamArray>] args: Object[]) =
  4. for arg in args do
  5. printfn "%A" arg
  6. let cons = new MyConsole()
  7. cons.WriteLine("abc", 42, 3.14, true)

Events

F# classes can have events, and the events can be triggered and responded to.

  1. type MyButton() =
  2. let clickEvent = new Event<_>()
  3. [<CLIEvent>]
  4. member this.OnClick = clickEvent.Publish
  5. member this.TestEvent(arg) =
  6. clickEvent.Trigger(this, arg)
  7. // test
  8. let myButton = new MyButton()
  9. myButton.OnClick.Add(fun (sender, arg) ->
  10. printfn "Click event with arg=%O" arg)
  11. myButton.TestEvent("Hello World!")

Delegates

F# can do delegates.

  1. // delegates
  2. type MyDelegate = delegate of int -> int
  3. let f = MyDelegate (fun x -> x * x)
  4. let result = f.Invoke(5)

Enums

F# supports CLI enums types, which look similar to the “union” types, but are actually different behind the scenes.

  1. // enums
  2. type Color = | Red=1 | Green=2 | Blue=3
  3. let color1 = Color.Red // simple assignment
  4. let color2:Color = enum 2 // cast from int
  5. // created from parsing a string
  6. let color3 = System.Enum.Parse(typeof<Color>,"Green") :?> Color // :?> is a downcast
  7. [<System.FlagsAttribute>]
  8. type FileAccess = | Read=1 | Write=2 | Execute=4
  9. let fileaccess = FileAccess.Read ||| FileAccess.Write

Working with the standard user interface

Finally, F# can work with the WinForms and WPF user interface libraries, just like C#.

Here is a trivial example of opening a form and handling a click event.

  1. open System.Windows.Forms
  2. let form = new Form(Width= 400, Height = 300, Visible = true, Text = "Hello World")
  3. form.TopMost <- true
  4. form.Click.Add (fun args-> printfn "the form was clicked")
  5. form.Show()