# Basics 25 - Classes and Interfaces

[Learning-FSharp/Ch25-OOP-Classes/Program.fs](https://github.com/Ankur-Kashyap/Learning-FSharp/blob/main/Learning-FSharp/Ch25-OOP-Classes/Program.fs)

Check out all the comments that are placed in the source file.

The objective of this article is to give you an overview of classes and interfaces in F# through various examples.

# Before We Begin

Throughout this series, I have and will continue to speak against OOP. You may wonder why am I covering classes and interfaces. Largely for 3 reasons:

1. Some features of F# can't be used without classes; the prime example being computation expressions
    
2. You need to use existing classes and interfaces that belong to .NET Framework or third-party libraries
    
3. You may need to implement areas of a project in F# that should be exposed in C# in OOP fashion
    

My goal is to give an overall idea of the playing field. I am not covering classes, inheritance and interfaces in depth here. I use these to the absolute minimum possible and stick to functional design.

# Basic Syntax for Creating Classes

```plaintext
// The basic template for a class.
// Notice the () after Student1, the type name.
// Student1() is the default constructor for type (class) Student1.
type Student1() =
    class

    end
```

1. A class must have the default constructor
    
2. The default constructor is part of the type declaration, `Student1()` in this case
    

# Classes with Additional Constructors and Members

```plaintext
// Class/type Student2.
// The default constructor takes two arguments name and age.
type Student2(name: string, age: int) =
    class
        // This area is actually default constructor's body
        do printfn "Creating Student2 with name: %s" name
        do printfn "Creating Student2 with age: %d" age

        // After the body of the default constructor comes
        // the area for members

        // Additional constructors
        // Must invoke the default constructor
        new() =
            printfn "%s" "new() Invoked."
            Student2("Unknown", 0)

        new(name: string) =
            printfn "%s" "new(name) Invoked."
            Student2(name, 0)

        new(age: int) =
            printfn "%s" "new(age) Invoked."
            Student2("Unknown", age)

        // An instance method.
        // Notice name and age are available.
        // this is the name used to refer to the current instance.
        // You can name it this or anything else.
        member this.NamePlusAge() = $"{name} {age}"

        // Notice this is replaced with current.
        member current.AnotherNamePlusAge() = $"{name} {age}"

        // A static method
        static member Create name age = Student2(name, age)
    end
```

1. The default constructor is `Student2(name: string, age: int)`
    
2. Three additional constructors are created: `new()`, `new(name: string)` and `new(age: int)`
    
3. Notice that all additional constructors ***must call the default constructor***
    
4. The area between the beginning of the class body and the area for class members is the ***body of the default constructor***
    
5. Instance members:
    
    1. Unlike C#/Java, you are free to refer to the current/calling instance by any name
        
    2. `member this.NamePlusAge() =` refers to the current instance by `this`
        
    3. `member current.AnotherNamePlusAge() =` refers to the current instance by `current`
        
6. Static members are created with `static member`
    

# Basic Syntax for Creating Interfaces

```plaintext
// The basic template for an Interface.
type INamePlusAge =
    interface
        // A method for derived types to implement.
        abstract member NamePlusAge: unit -> string
    end
```

1. Use `abstract member` declaring members in the interface
    
2. These members must be defined the type implementing the interface
    

# Class Implementing an Interface

```plaintext
// Example of a class implementing an interface.
type Student3(name: string, age: int) =
    class
        // Default constructor body.
        do printfn "%s %d" name age

        // Area for members.

        // Implementation of interface method.

        interface INamePlusAge with
            member this.NamePlusAge() = $"{name} {age}"
    end
```

1. Implement interface with `interface <Interface Name> with`
    
2. Define all the members that are part of the interface
    

# Record Implementing an Interface

```plaintext
// Example of a record implementing an interface
type StudentRecord =
    { Name: string
      Age: int }

    interface INamePlusAge with
        member this.NamePlusAge() = $"{this.Name} {this.Age}"
```

1. Same as the class
    

# A Generic Interface and Class Implementation

```plaintext
// Here's an interface with generic type T.
type IGenericIntarfec<'T> =
    interface
        // Methods for derived types to implement.

        abstract member Get: unit -> 'T
        abstract member GetToString: unit -> string
    end

// A class implementing IGenericIntarfec<'T>.
type MyClass<'T>(t: 'T) =
    class
        interface IGenericIntarfec<'T> with
            member this.Get() = t
            member this.GetToString() = t.ToString().ToUpper()
    end
```

1. Here's an interface with a generic type `'T`
    

# Let Bindings in Class for Private Fields and Functions

```plaintext
// Example of let bindings within a class.
// Use let bindings for creating provate fields and functions.
type ClassWithLetBindings(name: string, age: int) =
    class
        // 2 private fields and a private function.
        let _name = name
        let _age = age
        let agePlus num = _age + num

        // A private static field.
        static let _a = "A"

        // A private static function.
        static let _add2Nums x y = x + y

        member this.NamePlusAge() = $"{name} {age}"
        member this.YetAnotherNamePlusAge() = $"{_name} {_age}"
        member this.AgePlusNum num = agePlus num
    end
```

1. You can use `let` bindings to create private fields and functions
    
2. Similarly, you can use `static let` to create private static fields and private static functions
    

# Class Properties with Backing Stores

```plaintext
// Example of properties with backing stores.
type ClassWithProperties(name: string, age: int, rollNo: string) =
    class
        let _name = name
        let mutable _age = age
        let mutable _rollNo = rollNo

        // A read-only property.
        member this.Name = _name

        // A write-only property.
        member this.RollNo
            with set (value) = _rollNo <- value

        // A read-write property.
        member this.Age
            with get () = _age
            and set (value) = _age <- value
    end
```

1. Here's an example of properties that are linked with backing stores (fields)
    

# Class Properties without Backing Stores

```plaintext
// Example of properties with no backing stores.
type ClassWithPropertiesWithNoBackingStore(name: string, age: int, rollNo: string) =
    class
        // A read-only property.
        member val Name = name

        // 2 read-write properties.
        member val Age = age with get, set
        member val RollNo = rollNo with get, set
    end
```

1. Here's an example of properties without backing stores (fields)
    

# Creating Instances of Classes

```plaintext
// new is optional
let student1_1 = Student1()
let student1_2 = new Student1()

// Invoking difference constructors
let student2_1 = Student2()
let student2_2 = Student2("Name")
let student2_3 = Student2(10)
let student2_4 = Student2("Name", 10)

// Interface pointing to class and record
let iface_1: INamePlusAge = Student3("Name", 10)
let iface_2: INamePlusAge = { Name = "Name"; Age = 10 }

// Calling interface method

let x = iface_1.NamePlusAge()
let y = iface_2.NamePlusAge()

// If you need to call interface methods,
// can't be done directly from class or record

let student3_1 = Student3("Name", 10)

// student3_1.NamePlusAge <- will result in compile error

// Use casting operating :>
let namePlusAge = (student3_1 :> INamePlusAge).NamePlusAge()

// Generic class instance
let g_1 = MyClass<int>(10)
let g_2 = MyClass<string>("Ten")

// Casting to interface

let x = (g_1 :> IGenericIntarfec<int>).Get()
let y = (g_2 :> IGenericIntarfec<string>).Get()
```

If you have reached so far, congratulations.

Keep reading!
