Home

Introduction

 

Fundamentals

 

Introduction

 

 

     
 
 

 

   
   
 

Previous Copyright © 2015 FunctionX Next

Home

Interfaces

 

An Object's Interface

 

Introduction

An interface is a non-functioning layout for a structure or a class. The interface itself is not a structure or a class; it only provides a list of actions that a class would need.

Like a class, an interface has members but those members are not defined. Each member simply indicates its name but also provides such information as its type(s) of argument(s), if any, and its return type.

Creating an Interface

The primary formula to create an interface is:

type InterfaceName
  [ interface ]
  	abstract Member 1 : [ Data Type 1 -> ] Return Type 1
  	abstract Member 2 : [ Data Type 2 -> ] Return Type 2
  	abstract Member_n : [ Data Type_n -> ] Return Type_n
  [ end ]

You start with the required type keyword followed by a name. By tradition, the name of an interface starts with I (i in uppercase). After the name of the interface, create a body for it on the next line. You can start the body with the interface keyword. If you do, then you must end the body with the end keyword. Normally, both keywords are a combinational option. This means that either you omit both or you use both.

In the body of the interface, create an abstraction for each member. Every member of an interface must start with the abstract keyword but doesn't include a body.

Implementing an Interface

Once an interface exists, any class that needs its functionality must imprement it. The formula to follow to implement an interface is:

type Class-Name[(Parametere(s))] =
    Member(s)
    interface InterfaceName with
  	Interface-Member(s)

To implement an interface, in the body of the class, after the local members of the class, type interface, followed by the name of the interface, and followed by with. After this and on the next line, implement the member(s) of the interface.

Interfaces and Methods

In the interface, a method starts with either the abstract keyword or the abstract member expression. This is followed by the name and the signature. Here is an example:

type IPolygon =
    interface
        abstract member Describe : unit -> string
    end

In a class that implements the interface, define the method as we have done so far for any regular method but make sure you define and indent the method in the interface section. Here is an example:

type IPolygon =
    interface
        abstract member Describe : unit -> string
    end
    
type Octagon() =
    interface IPolygon with
        member this.Describe() = "An octagon is a polygon with 8 equal sides. The angle between 2 adjacent sides is 135 degrees."

Interfaces and Properties

In the interface, a property is created with either the abstract keyword or the abstract member expression. This is followed by the name of the property, a colon and the data type. If the property is read-only, add with get. Here are examples:

type IPolygon =
    interface
        abstract SidesCount      : int   with get
        abstract member Angle    : float with get
        abstract member Describe : unit -> string
    end

For a write-only property, add with set. If the property is read-write, add with get, set. Here is an example:

type IPolygon =
    interface
        abstract Side : float with get, set
    end

When defining the method in the class, proceed as we have done so far for any property as long as you include it in the interface section.

Using an Interface

To actually use the functionaity of an interface, you must cast an object created from a class that implements to interface. There are various techniques you can use.

After implementing an interface in a class, you can create an object of the class by declaring a variable from that class. To access a member of the interface (a member that is implemented by the class), you must up-cast it to the interface. Here are examples:

open System
open System.Windows.Forms

type IPolygon =
    interface
        abstract Side            : float with get, set
        abstract SidesCount      : int   with get
        abstract member Angle    : float with get
        abstract Perimeter       : float with get
        abstract member Area     : float with get
        abstract member Describe : unit -> string
    end

type Octagon(side : float) =
    let sd = ref side

    interface IPolygon with
        member this.Side       with get() = !sd and set(value) = sd := value
        member this.SidesCount with get() = 8
        member this.Angle      with get() = 135.00
        member this.Perimeter  with get() = !sd  * float (this :> IPolygon).SidesCount
        member this.Area       with get() = 2.00 * (1.00 + (sqrt 2.00)) * !sd * !sd
        member this.Describe() = "An octagon is a polygon with 8 equal sides. The angle between 2 adjacent sides is 135 degrees."

// Form: Polygons: Hexagon
let geometry = new Form(Width = 316, Height = 250, Text = "Polygon: Octagon")

// Side Value
geometry.Controls.Add(new Label(Left = 25, Top = 26, Width = 64, Text = "Side Value:"))
let txtSideValue = new TextBox(Left = 102, Top = 23, Text = "0.00")
geometry.Controls.Add txtSideValue

// Button: Calculate
let btnCalculate = new Button(Left = 208, Top = 21, Text = "Calculate")
geometry.Controls.Add btnCalculate

// Line
geometry.Controls.Add(new Label(Left = 22, Top = 49, Width = 275, Text = "-----------------------------------------------------------------------"))

// Side Count
geometry.Controls.Add(new Label(Left = 25, Top = 75, Width = 64, Text = "Side Count:"))
let txtSideCount = new TextBox(Left = 102, Top = 72, Text = "0")
geometry.Controls.Add txtSideCount

// Angle
geometry.Controls.Add(new Label(Left = 25, Top = 101, Width = 64, Text = "Angle:"))
let txtAngle = new TextBox(Left = 102, Top = 98, Text = "0.00")
geometry.Controls.Add txtAngle
geometry.Controls.Add(new Label(Left = 157, Top = 101, Width = 64, Text = "Degrees"))

// Perimeter
geometry.Controls.Add(new Label(Left = 25, Top = 122, Width = 64, Text = "Perimeter:"))
let txtPerimeter = new TextBox(Left = 102, Top = 124, Text = "0.00")
geometry.Controls.Add txtPerimeter

// Area
geometry.Controls.Add(new Label(Left = 25, Top = 150, Width = 40, Text = "Area:"))
let txtArea = new TextBox(Left = 102, Top = 150, Text = "0.00")
geometry.Controls.Add txtArea

// Description
let lblDescription = new Label(Left = 25, Top = 182, Width = 258, Height = 32, Text = "Description")
geometry.Controls.Add lblDescription

let btnCalculateClick e =
    let side = float txtSideValue.Text

    let oct = Octagon(side)
    let sides = (oct :> IPolygon).SidesCount
    let angle = (oct :> IPolygon).Angle
    let perimeter = (oct :> IPolygon).Perimeter
    let area = (oct :> IPolygon).Area

    let strNumberOfSides = sprintf "%i" sides
    let strAngle = sprintf "%0.0f" angle
    let strPerimeter = sprintf "%0.02f" perimeter
    let strArea = sprintf "%f" area

    txtSideCount.Text <- strNumberOfSides
    txtAngle.Text <- strAngle
    txtPerimeter.Text <- strPerimeter
    txtArea.Text <- strArea
    lblDescription.Text <- (oct :> IPolygon).Describe()

btnCalculate.Click.Add btnCalculateClick

// Button: Close
let btnClose = new Button(Left = 208, Top = 148, Text = "Close")
let btnCloseClick e = geometry.Close()
btnClose.Click.Add btnCloseClick
geometry.Controls.Add btnClose

do Application.Run geometry

Here is an example of executing the program:

Using an Interface

As another technique, when implementing an interface in a class, after defining a method of the interface, create another member function that holds the same name as the method. To define that other method, create an upcast and call the method of the interface. The formula to follow is:

member [this | Self | InterfaceName].Interface-Method() = ([this | Self | InterfaceName] :> InterfaceName).Interface-Method()

Start with the member keyword. It is followed by either the this keyword, a self identifier of your choice (which can be a letter or a word of your choice), or the name of the interface. This is followed by a period and the name of the method from the interface with its parentheses and the = sign. To case the interface, add some parentheses. In the parentheses, use the same option you chose for this, the Self identifier, or the name of the interface. This means that if you decide to use this, you must use this on both sides. If you decide to use a self-identifier, you must use the same on both sides. If you decide to use the name of the interface, you must use it on both sides. After the up-cast, access the name of the method using a period and end it with parentheses. Once this has been done, you can access the interface method from an object created from the class. Here are examples:

open System
open System.Windows.Forms

type WorkingStatus =
| New
| Used
| NeedsRepair

type IProduct =
    abstract ItemNumber : int    with get, set
    abstract ItemName   : string with get, set
    abstract UnitPrice  : float  with get, set

type MusicalInstrument(number, name, price, status) =
    let nbr  = ref number
    let nm   = ref name
    let cost = ref price
    let stt  = ref status
    let mp   = ref price
    
    interface IProduct with
        member this.ItemNumber with get() = !nbr  and set(value) = nbr  := value
        member this.ItemName   with get() = !nm   and set(value) = nm   := value
        member this.UnitPrice  with get() = !cost and set(value) = cost := value
    
    member this.Status with get() = !stt and set(value) = stt := value
    member this.MarkedPrice with get() =
                                if !stt = Used then !cost - (!cost * 20.00 / 100.00)
                                elif !stt = NeedsRepair then !cost - (!cost * 50.00 / 100.00)
                                else !cost
                                
    member this.ItemNumber with get() = (this :> IProduct).ItemNumber
    member this.ItemName   with get() = (this :> IProduct).ItemName
    member this.UnitPrice  with get() = (this :> IProduct).UnitPrice

let status = ref New

// Form: Music Store
let musicStore = new Form(Width = 385, Height = 215, Text = "Music Store")

// Label: Item Number
let lblItemNumber = new Label(Left = 23, Top = 21, Width = 64, Text = "Item #:")
musicStore.Controls.Add lblItemNumber

// Text Box: Item Number
let txtItemNumber = new TextBox(Left = 90, Top = 18, Width = 75)
let rndNumber = new Random()
txtItemNumber.Text <- string (rndNumber.Next(100001, 999999))
musicStore.Controls.Add txtItemNumber

// Label: Status
let lblStatus = new Label(Left = 203, Top = 21, Width = 54, Text = "Status:")
musicStore.Controls.Add lblStatus

// Combo Box: Status
let cbxStatus = new ComboBox(Left = 258, Top = 18,  Width = 95, Text = "New")
cbxStatus.Items.AddRange([| "New"; "Used"; "Needs Repair" |])

let cbxStatusChanged e =
    if cbxStatus.Text = "Used" then
        status := Used
    elif cbxStatus.Text = "Needs Repair" then
        status := NeedsRepair
    else
        status := New
cbxStatus.SelectedIndexChanged.Add cbxStatusChanged
musicStore.Controls.Add cbxStatus

// Label: Item Name
let lblItemName = new Label(Left = 23, Top = 50, Width = 64, Text = "Item Name:")
musicStore.Controls.Add lblItemName

// Text Box: Item Name
let txtItemName = new TextBox(Left = 90, Top = 47, Width = 262)
musicStore.Controls.Add txtItemName

// Label: Unit Price
let lblUnitPrice = new Label(Left = 23, Top = 79, Width = 64, Text = "Unit Price:")
musicStore.Controls.Add lblUnitPrice

// Text Box: Unit Price
let txtUnitPrice = new TextBox(Left = 90, Top = 76, Width = 75, Text = "0.00")
musicStore.Controls.Add txtUnitPrice

// Label: Sale Price
let lblSalePrice = new Label(Left = 23, Top = 108, Width = 64, Text = "Sale Price:")
musicStore.Controls.Add lblSalePrice

// Text Box: Sale Price
let txtSalePrice = new TextBox(Left = 90, Top = 105, Width = 75, Text = "0.00")
musicStore.Controls.Add txtSalePrice

// Label: Summary
let lblSummary = new Label(Left = 23, Top = 142, Width = 305, Height = 80, Text = "Summary")
musicStore.Controls.Add lblSummary

// Button: Calculate
let btnCalculate = new Button(Left = 277, Top = 76, Text = "Calculate")
let btnCalculateClick e =
    let number = int txtItemNumber.Text
    let price  = float txtUnitPrice.Text
    let item = new MusicalInstrument(number, txtItemName.Text, price, !status)

    let strSale = sprintf "%0.02f" item.MarkedPrice
    let summary = sprintf "%i: %s\nUnit Price: %0.02f, %A: %0.02f"
	item.ItemNumber item.ItemName item.UnitPrice item.Status item.MarkedPrice

    txtSalePrice.Text <- strSale
    lblSummary.Text <- summary
btnCalculate.Click.Add btnCalculateClick
musicStore.Controls.Add btnCalculate

// Button: Close
let btnClose = new Button(Left = 277, Top = 105, Text = "Close")
let btnCloseClick e = musicStore.Close()
btnClose.Click.Add btnCloseClick
musicStore.Controls.Add btnClose

do Application.Run musicStore

Here is an example of executing the program:

Using an Interface

Using an Interface

Options on Creating and Implementing Interfaces

 

Interfaces and Inheritance

One interface can be derived from another interface. Following the rules of inheritance, to indicate that an interface derives from another interface, use the inherit keyword followed by the parent interface. Here is an example:

type IProduct =
    abstract ItemNumber : int    with get, set
    abstract ItemName   : string with get, set
    abstract UnitPrice  : float  with get, set

type ISalesTax =
    inherit IProduct

Of course, the new interface is supposed to add some behavior using methods and/or properties. In any class that needs the behaviors of the new interface, you must implement all members of the new interface and its parents. Here is an example:

open System
open System.Windows.Forms

type WorkingStatus =
| New
| Used
| NeedsRepair

type IProduct =
    abstract StockNumber : int    with get, set
    abstract ItemName    : string with get, set
    abstract UnitPrice   : float  with get, set

type ISalesTax =
    inherit IProduct
    abstract TaxRate : float with get, set
    abstract TaxAmount : float with get

type BoatPurchase(number, name, price, tRate) =
    let nbr  = ref number
    let nm   = ref name
    let cost = ref price
    let tr   = ref tRate
    
    interface ISalesTax with
        member this.StockNumber with get() = !nbr  and set(value) = nbr  := value
        member this.ItemName    with get() = !nm   and set(value) = nm   := value
        member this.UnitPrice   with get() = !cost and set(value) = cost := value
        member this.TaxRate     with get() = !tr   and set(value) = tr   := value
        member this.TaxAmount   with get() = !cost * !tr / 100.00

    member this.MarkedPrice with get() = !cost + (this :> ISalesTax).TaxAmount
                    
    member this.StockNumber with get() = (this :> ISalesTax).StockNumber
    member this.ItemName    with get() = (this :> ISalesTax).ItemName
    member this.UnitPrice   with get() = (this :> ISalesTax).UnitPrice

let rndNumber = new Random()
let strStockNumber = string (rndNumber.Next(100, 999)) + "-" + string (rndNumber.Next(100, 999))

let item = BoatPurchase((rndNumber.Next(10000, 90000)), "Self-Propelled Gas Mower with Auto Choke", 399.95, 7.75)
let salesTax = (item :> ISalesTax).TaxAmount

// Form: Water Runner
let waterRunner = new Form(Width = 388, Height = 238, Text = "Water Runner")

// Label: Stock Number
waterRunner.Controls.Add(new Label(Left = 17, Top = 22, Width = 75, Text = "Stock #:"))

// Text Box: Stock Number
let txtStockNumber = new TextBox(Left = 113, Top = 19, Width = 78)
waterRunner.Controls.Add txtStockNumber

// Label: Status
waterRunner.Controls.Add(new Label(Left = 205, Top = 21, Width = 54, Text = "Status:"))

// Combo Box: Status
let cbxStatus = new ComboBox(Left = 275, Top = 19,  Width = 90, Text = "New")
cbxStatus.Items.AddRange([| "New"; "Used"; "Needs Repair" |])
waterRunner.Controls.Add cbxStatus

// Label: Boat Model
waterRunner.Controls.Add(new Label(Left = 17, Top = 51, Width = 75, Text = "Boat Model:"))

// Text Box: Model
let txtBoatModel = new TextBox(Left = 113, Top =  48, Width = 250)
waterRunner.Controls.Add txtBoatModel

// Label: Seating Capacity
waterRunner.Controls.Add(new Label(Left = 17, Top = 79, Width = 95, Text = "Seating Capacity:"))

// Text Box: Seating Capacity
let txtSeatingCapacity = new TextBox(Left = 113, Top = 77, Width = 75, Text = "1")
waterRunner.Controls.Add txtSeatingCapacity

// Label: Retail Price
waterRunner.Controls.Add(new Label(Left = 205, Top = 80, Width = 68, Text = "Retail Price:"))

// Text Box: Retail Price
let txtRetailPrice = new TextBox(Left = 275, Top = 77, Width = 90, Text = "0.00")
waterRunner.Controls.Add txtRetailPrice

// Label: Tax Rate
waterRunner.Controls.Add(new Label(Left = 18, Top = 106, Width = 78, Text = "Tax Rate:"))

// Text Box: Tax Rate
let txtTaxRate = new TextBox(Left = 114, Top =  106, Width = 60, Text = "7.75")
waterRunner.Controls.Add txtTaxRate

// Label: %
waterRunner.Controls.Add(new Label(Left = 175, Top = 109, Width = 14, Text = "%"))

// Label: Tax Amount
waterRunner.Controls.Add(new Label(Left = 17, Top = 138, Width = 85, Text = "Tax Amount:"))

// Text Box: Tax Amount
let txtTaxAmount = new TextBox(Left = 113, Top = 135, Width = 75, Text = "0.00")
waterRunner.Controls.Add txtTaxAmount

// Label: Sale Price
waterRunner.Controls.Add(new Label(Left = 205, Top = 138, Width = 62, Text = "Sale Price:"))

// Text Box: Sale Price
let txtSalePrice = new TextBox(Left = 275, Top = 135, Width = 90, Text = "0.00")
waterRunner.Controls.Add txtSalePrice

// Button: Calculate
let btnCalculate = new Button(Left = 275, Top = 106, Width = 90, Text = "Calculate")
let btnCalculateClick e =
    let boat = new BoatPurchase(int txtStockNumber.Text, txtBoatModel.Text, float txtRetailPrice.Text, float txtTaxRate.Text)

    let strTax = sprintf "%0.02f" (boat :> ISalesTax).TaxAmount
    let strSalePrice = sprintf "%0.02f" boat.MarkedPrice

    txtTaxAmount.Text <- strTax
    txtSalePrice.Text <- strSalePrice
btnCalculate.Click.Add btnCalculateClick
waterRunner.Controls.Add btnCalculate

// Button: Close
let btnClose = new Button(Left = 275, Top = 174, Width = 90, Text = "Close")
let btnCloseClick e = waterRunner.Close()
btnClose.Click.Add btnCloseClick
waterRunner.Controls.Add btnClose

do Application.Run waterRunner

Here is an example of running the program:

Using an Interface

Using an Interface

In the same way, an interface can inherit from as many interfaces as necessary.

Implementing Many Interfaces

F# doesn't support multiple inheritance, which is the ability to create a class that is directly based on many classes. As an alternative, you can create a class that implements more than one interface.

The formula to implement many interfaces is:

type Class-Name[(Parametere(s))] =
    Member(s)
    interface InterfaceName 1 with
  	Interface-Member(s) 1
    interface InterfaceName 2 with
  	Interface-Member(s) 2
    . . .
    interface InterfaceName n with
  	Interface-Member(s) n

In the body of the class, each interface has its implementation section. The individual implementations follow the techniques we have used so far. To access the member of an interface from the object created using the class, you can declare a variable that is up-cast from the interface. This can be done as follows:

open System
open System.Windows.Forms

type IProduct =
    abstract ItemNumber : int    with get, set
    abstract ItemName   : string with get, set
    abstract UnitPrice  : float  with get, set

type IDiscount =
    abstract DiscountRate : float with get, set
    abstract DiscountAmount : float with get

type StoreItem(number, make, category, subcat, name, size, price, discrate) =
    let nbr = ref number
    let mutable mk = make
    let mutable cat = category
    let mutable sub = subcat
    let nm = ref name
    let mutable sz = size
    let prc = ref price
    let dr = ref discrate
    
    interface IProduct with
        member this.ItemNumber with get() = !nbr and set(value) = nbr := value
        member this.ItemName   with get() = !nm  and set(value) = nm  := value
        member this.UnitPrice  with get() = !prc and set(value) = prc := value
    
    interface IDiscount with
        member this.DiscountRate   with get() = !dr and set(value) = dr := value
        member this.DiscountAmount with get() = !prc * !dr / 100.00

    member this.Manufacturer   with get() = mk   and set(value) = mk   <- value
    member this.Category       with get() = cat  and set(value) = cat  <- value
    member this.SubCategory    with get() = sub  and set(value) = sub  <- value
    member this.ItemSize       with get() = sz   and set(value) = sz   <- value
    
    member this.MarkedPrice    with get() = this.UnitPrice - (this :> IDiscount).DiscountAmount
    new() = StoreItem(0, "", "", "", "", "", 0.00, 0.00)
    new(number, make, category, subcategory, name, size, price) = StoreItem(number, make, category, subcategory, name, size, price, 0.00)

    override this.GetHashCode() = this.GetHashCode()
    
    override this.Equals(obj) =
            match obj with
            | :? StoreItem as si -> this.ItemNumber = si.ItemNumber
            | _ -> false

    override this.ToString() =
        "Item #: " + string this.ItemNumber + ", " + this.Manufacturer + ", " + this.Category + " " + this.SubCategory + ", Named " + this.ItemName + "(" + this.ItemSize + "), Price: " + string this.UnitPrice

    interface IComparable with
        member this.CompareTo(obj : Object) : int =
            match obj with
            | :? StoreItem as si -> compare this.ItemName si.ItemName
            | _ -> 0
                                
    member this.ItemNumber      with get() = (this :> IProduct).ItemNumber
    member this.ItemName        with get() = (this :> IProduct).ItemName
    member this.UnitPrice       with get() = (this :> IProduct).UnitPrice
    member this.DiscountRate    with get() = (this :> IDiscount).DiscountRate
    member this.DiscountAmount  with get() = (this :> IDiscount).DiscountAmount 

let rndNumber = new Random()

let si = new StoreItem(rndNumber.Next(100001, 999999), "Guess", "Women", "Dresses", "Zip Front Fit and Flare Dressr", "4", 135.75, float 25)

// Form: Department Store
let departmentStore = new Form(Width = 390, Height = 238, Text = "Department Store")

// Label: Item Number
departmentStore.Controls.Add(new Label(Left = 22, Top = 18, Width = 74, Text = "Item #:"))

// Text Box: Item Number
let txtItemNumber = new TextBox(Left = 101, Top = 15, Width = 64)
txtItemNumber.Text <- string si.ItemNumber
departmentStore.Controls.Add txtItemNumber

// Label: Manufacturer
departmentStore.Controls.Add(new Label(Left = 22, Top = 44, Width = 74, Text = "Manufacturer:"))

// Text Box: Manufacturer
let txtManufacturer = new TextBox(Left = 101, Top = 41, Width = 262)
txtManufacturer.Text  <- si.Manufacturer
departmentStore.Controls.Add txtManufacturer

// Label: Category
departmentStore.Controls.Add(new Label(Left = 22, Top = 70, Width = 52, Text = "Category:"))

// Text Box: Category
let txtCategory = new TextBox(Left = 101, Top = 67, Width = 87)
txtCategory.Text  <- si.Category
departmentStore.Controls.Add txtCategory

// Label: Sub-Category
let lblSubCategory = new Label(Left = 194, Top = 70, Width = 78, Text = "Sub-Category:")

departmentStore.Controls.Add lblSubCategory

// Text Box: Sub-Category
let txtSubCategory = new TextBox(Left = 274, Top =  67, Width =  90)
txtSubCategory.Text  <- si.SubCategory
departmentStore.Controls.Add txtSubCategory

// Label: Item Name
departmentStore.Controls.Add(new Label(Left = 22, Top = 96, Width = 68, Text = "Item Name:"))

// Text Box: Item Name
let txtItemName = new TextBox(Left = 101, Top =  93, Width = 262)
txtItemName.Text  <- si.ItemName
departmentStore.Controls.Add txtItemName

// Label: Item Size
departmentStore.Controls.Add(new Label(Left = 22, Top = 122, Width = 64, Text = "Item Size:"))

// Text Box: Item Size
let txtItemSize = new TextBox(Left = 101, Top = 119, Width = 87)
txtItemSize.Text <- si.ItemSize
departmentStore.Controls.Add txtItemSize

// Label: Unit Price
departmentStore.Controls.Add(new Label(Left = 194, Top = 122, Width = 75, Text = "Unit Price:"))

// Text Box: Unit Price
let txtUnitPrice = new TextBox(Left = 288, Top = 119, Width = 75)
txtUnitPrice.Text  <- sprintf "%g" si.UnitPrice
departmentStore.Controls.Add txtUnitPrice

// Label: Discount Rate
let lblDiscountRate = new Label(Left = 22, Top = 148, Width = 78, Text = "Discount Rate:")
departmentStore.Controls.Add lblDiscountRate

// Text Box: Discount Rate
let txtDiscountRate = new TextBox(Left = 101, Top = 145, Width =  87)
txtDiscountRate.Text  <- (sprintf "%g%c" si.DiscountRate '%')
departmentStore.Controls.Add txtDiscountRate

// Label: Discount Amount
let lblDiscountAmount = new Label(Left = 194, Top = 148, Width = 94, Text = "Discount Amount:")
departmentStore.Controls.Add lblDiscountAmount

// Text Box: Discount Amount
let txtDiscountAmount = new TextBox(Left = 288, Top = 145, Width = 75)
txtDiscountAmount.Text <- sprintf "%0.02f" si.DiscountAmount
departmentStore.Controls.Add txtDiscountAmount

// Label: Marked Price
departmentStore.Controls.Add(new Label(Left = 22, Top = 174, Width = 77, Text = "Marked Price:"))

// Text Box: MarkedPrice
let txtMarkedPrice = new TextBox(Left = 101, Top = 171, Width = 87)
txtMarkedPrice.Text <- sprintf "%0.02f" si.MarkedPrice
departmentStore.Controls.Add txtMarkedPrice

// Button: Close
let btnClose = new Button(Left = 288, Top = 174, Text = "Close")
let btnCloseClick e = departmentStore.Close()
btnClose.Click.Add btnCloseClick
departmentStore.Controls.Add btnClose

do Application.Run departmentStore

This would produce:

Mutating a Read-Write Property

Multiple Derivations

As we know already, you can create a class that is based on another class, and you can create a class that implements an interface. You are not allowed to create a class that is directly based on more than one class. Instead you can either use transitive inheritance, in which case one class is based on another class that itself is based on another class. Or you can create a class that implements many interfaces. That, you can create a class that implements more than one interface. In the body of the class, create a section for each interface and implement all its members. Here is an example:

open System
type IProduct =
    abstract ItemNumber : string with get, set
    abstract ItemName   : string with get, set
    abstract UnitPrice  : float  with get, set
type ISalesTax =
    abstract TaxRate : float with get, set
    abstract TaxAmount : float with get

type VehiclePurchase(number, name, price, tRate) =
    let nbr  = ref number
    let nm   = ref name
    let cost = ref price
    let tr   = ref tRate
    
    interface IProduct with
        member this.ItemNumber with get() = !nbr  and set(value) = nbr  := value
        member this.ItemName   with get() = !nm   and set(value) = nm   := value
        member this.UnitPrice  with get() = !cost and set(value) = cost := value
    
    interface ISalesTax with
        member this.TaxRate    with get() = !tr   and set(value) = tr   := value
        member this.TaxAmount  with get() = !cost * !tr / 100.00

    member this.MarkedPrice with get() = !cost + (this :> ISalesTax).TaxAmount
                    
    member this.ItemNumber   with get() = (this :> IProduct).ItemNumber
    member this.ItemName     with get() = (this :> IProduct).ItemName
    member this.UnitPrice    with get() = (this :> IProduct).UnitPrice
    //member this.TaxRate    with get() = (this :> ISalesTax).TaxRate
    //member this.TaxAmount  with get() = (this :> ISalesTax).TaxAmount

let rndNumber = new Random()
let strItemNumber = string (rndNumber.Next(100, 999)) + "-" + string (rndNumber.Next(100, 999)) + "-" + string (rndNumber.Next(100, 999))

let registrationFee = 128.00
let downPayment = 3000.00
let item = VehiclePurchase(strItemNumber, "Toyota Camry (2010)", 18750.00 - downPayment + registrationFee, 7.75)
let salesTax = (item :> ISalesTax).TaxAmount

Home Copyright © 2014-2015 FunctionX Home