Home

The Properties of a Class

 

Fundamentals of Properties

 

Introduction

A let variable is used to manage values inside a class. Such values are private and cannnot be accessed outside the class. Here is an example of of a (private) let variable:

type Payroll() =
    let summary = string

On the other hand, member variables can be used inside the class or can communicate with the outside world. The problem is that member variables are not equipped to validate or reject the values that are given to them or that they present to the clients of the class. On modern object-oriented programming, the solution is to use a special class member called a property.

A property is a member of a class that plays an intermediary role between a class and the outside objects that need to access its value(s).

Creating a Property

A property is primarily created as a member variable. It starts with the member keyword and a name. With regards to their role, there are three categories of properties.

Read-Only Properties

 

Getting the Value of the Property

A property is referred to as read-only if its role is only to make available a value from the class. To create a read-only property, you use the a function named get. The formula to create a read-only property is:

member [this|Self-Identifier|Class-Name].Property-Name with get() : Data-Type = Some-Value

You start with the member key word. This is followed by either the this keyword, a self-identifier of your choice (which can be a letter or a word), or the name of the class. This is followed by a period and the desired name of the property. The name should start in uppercase and contain letters, digits, and/or underscores. The name is followed by the with get() expression.

If you want to specify the data type of the property, precede it with a colon and the name of the type (we will come back to this issue in a later section). This means that, in most cases, you can omit the data type. This is because, as we will see and because F# is an inferred language, there is a mechanism that will allow the compiler to apply the appropriate type to the property.

The colon or data type is followed by = and a value. You can get the value from a constructor of the class and assign it to the property. Here is an example of a read-only property:

type Payroll(emplNbr) =
    member me.EmployeeNumber with get() = emplNbr

In the same way, you can add as many properties as necessary. To access the value of the property outisde, create an object of the class, apply the period to the object followed by the name of the property. Here are examples:

type Payroll(salary, weekTime) =
    member me.WeeklySalary with get() = salary * weekTime

let pay = Payroll(26.85, 38.50)

printfn "Payroll Information"
printfn "=--------------------------="
printfn "Net Pay:    %0.02f\n" pay.WeeklySalary

This would produce:

Payroll Information
=--------------------------=
Net Pay:    1033.73

Press any key to continue . . .

Mutating the Value of a Property

Consider the following program that contains a number-based property:

type Circle(radius) =
    member Circle.Radius with get() = radius
    
let pieceOfPaper = Circle(-25.68)

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius: %0.02f\n" pieceOfPaper.Radius

This would produce:

Circle Characteristics
----------------------
Radius: -25.68 

Press any key to continue . . .

Instead of passing a value of the constructor directly to a property, you can create a let binding member that can act between the constructor and the property. Such a member variable can get the value from the constructor to the property. In this case, assign the argument of the contructor to the let member variable. Then assign that member variable to the property. This can be done as follows:

type Circle(radius) =
    let r = radius
    member Circle.Radius with get() = r
    
let pieceOfPaper = Circle(-25.68)

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius: %0.02f" pieceOfPaper.Radius 

This would produce the same as previously. Notice that a negative value was passed to the constructor, but a circle cannot have a negative value. To address this issue, you can use a mutable let binding member to accept or reject an undesired value. This can be done as follows:

type Circle(radius) =
    let mutable r = 0.00
    do
        if radius < 0.00 then
            r <- 0.00
        else
            r <- radius
    member Circle.Radius with get() = r

let show (circ : Circle) =
    printfn "Circle Characteristics"
    printfn "----------------------"
    printfn "Radius: %0.02f" circ.Radius
    
let round = Circle(-25.68)
show round
printfn "=================================="
let currency = Circle(17.39)
show currency

This would produce:

Circle Characteristics
----------------------
Radius: 0.00
==================================
Circle Characteristics
----------------------
Radius: 17.39

Press any key to continue . . .

Using a Reference Cell on the Value of a Property

Instead of a mutable member variable, you can use a reference cell to refer to the memory area where the member is located and change the value of the property accordingly. To start, you can assign an argument of the constructor to the member. Here is an example:

type Square(side) =
    let s = ref side
    member Square.Side with get() = s

To access the value of the member in the class, you can use precede its name with the ! operator. Here is an example:

type Square(side) =
    let s = ref side
    member Square.Side with get() = s
    member Square.Perimeter with get() = !s * 4.00

To get the value of the property outside the class, precede it with the ! operator. Here is an example:

type Square(side) =
    let s = ref side
    member Square.Side with get() = s
    member Square.Perimeter with get() = !s * 4.00
    member Square.Area with get() = !s * !s

let show (sqr : Square) =
    printfn "Square Characteristics"
    printfn "----------------------"
    printfn "Side:      %0.02f" !sqr.Side
    printfn "Perimeter: %0.04f" sqr.Perimeter
    printfn "Area:      %0.04f" sqr.Area
    
let paper = Square(34.06)
show paper

This would produce:

Square Characteristics
----------------------
Side:      34.06
Perimeter: 136.2400
Area:      1160.0836

Press any key to continue . . .

If necessary, anywhere in your class, you can change the value of the member using the := operator. Here is an example:

type Square(side) =
    let s = ref 0.00
    do
        if side < 0.00 then
            s := 0.00
        else
            s := side

    member Square.Side with get() = s
    member Square.Perimeter with get() = !s * 4.00
    member Square.Area with get() = !s * !s

let show (sqr : Square) =
    printfn "Square Characteristics"
    printfn "----------------------"
    printfn "Side:      %0.02f" !sqr.Side
    printfn "Perimeter: %0.04f" sqr.Perimeter
    printfn "Area:      %0.04f\n" sqr.Area
    
let piece = Square(-34.06)
show piece
printfn "=================================="
let paper = Square(34.06)
show paper

This would produce:

Square Characteristics
----------------------
Side:      0.00
Perimeter: 0.0000
Area:      0.0000
==================================
Square Characteristics
----------------------
Side:      34.06
Perimeter: 136.2400
Area:      1160.0836
Press any key to continue . . .

A Read-Only Property With No Body

The F# language provides an even simpler technique to create a read-only property. It consists of simply assigning something to the property. The formula to follow is:

member [ this | Self-Identifier | Class-Name ].Property-Name : Data-Type = Some-Value

You can use an argument passed to a constructor of the class and assign it to the property. Here is an example:

type Circle(radius) =
    member me.Radius = radius
    member this.Diameter with get() = radius * 2.00
    member this.Circumference with get() = this.Diameter * 3.14156
    member this.Area with get() = radius * radius * 3.14156

let round = Circle(48.06)

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius:        %0.02f" round.Radius
printfn "Diameter:      %0.02f" round.Diameter
printfn "Circumference: %f" round.Circumference
printfn "Area:          %f\n" round.Area

This would produce:

Kolo
Press any key to continue . . .

Write-Only Properties

 

Setting a Value to a Property

A property is referred to as write-only if it can only get a value from outside the class but cannot make that value available to the clients of the class. A write-only property is created using the set() that takes one argument. The formula to create a write-only property is:

member [ this | Self-Identifier | Class-Name ].Property-Name with set(Parameter) = body

Start with the member [ this | Self-Identifier | Class-Name ].Property-Name section as we described for the read-only property. This is followed by with set(). In its parentheses, enter unit or a name for a parameter. You can then assign a constructor parameter to it. This can be done as follows:

type Circle(radius) =
    member Circle.Radius with set(unit) = radius

Mutating the Value of a Read-Only Property

The primary role of a setter is to set (specify or change) the value of a property. One way to control it is to use a mutable let binding member that would control the value of the property. To do this, declare a let mutable member variable and assign an appropriate value to it. Create the property but assign its argument to the mutable member. Here is an example:

type Circle() =
    let mutable rad = 0.00
    member me.Radius with set(value) = rad <- value

To specify the value of the property outside the class, treat the property as a mutable variable. That is, use the <- operator to assign a value to it. Here is an example:

type Circle() =
    let mutable rad = 0.00
    member me.Radius with set(value) = rad <- value
    member this.Diameter with get() = rad * 2.00

let round = Circle()
round.Radius <- 48.06;

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Diameter: %0.02f\n" round.Diameter

This would produce:

Circle Characteristics
----------------------
Diameter: 96.12

Press any key to continue . . .

An alternative is to use a reference cell to access the location of the variable. You can start by declaring the member variable and assigning it a ref value. Here is an example:

type Square() =
    let s = ref 0.00

To apply a value to the setter, use the := operator. Here is an example:

type Square() =
    let s = ref 0.00

    member Square.Side with set(value) = s := value

To specify the value of the property outside the class, use the <- operator. Here is an example:

type Square() =
    let s = ref 0.00

    member Square.Side with set(value) = s := value
    member Square.Perimeter with get() = !s * 4.00
    member Square.Area with get() = !s * !s

let show (sqr : Square) =
    printfn "Square Characteristics"
    printfn "----------------------"
    printfn "Perimeter: %0.04f" sqr.Perimeter
    printfn "Area:      %0.04f\n" sqr.Area
    
let paper = Square()
paper.Side <- 34.06
show paper

This would produce:

Square Characteristics
----------------------
Perimeter: 136.2400
Area:      1160.0836

Press any key to continue . . .

Remember that a write-only property can only receive a value. This means that you cannot retrieve its value outside the class.

Read/Write Properties

 

Introduction

A property can be used both to write a value and/or to read one from a class. Such a property is referred to as read-write. The formula to create a read-write property is a combination of those we used already:

member [ this | Self-Identifier | Class-Name ].Property-Name
    with get() : Data-Type = Some-Value
    and set(Parameter) = body

The new addition to this formula is the and keyword. While following the rules we reviewed for the other sections, the value you assign to the getter (Some-Value) is the same used in the setter. Outside the class, you can create an object and access the property to both assign a value to use and to get its value.

Mutating a Read-Write Property

If you want a property whose value can be changed outside the class, you should use an intermediary mutable member. As seen in previous sections, you have two options. You can add a let mutable member. Here an example:

type Circle() =
    let mutable radius = 0.00
    member me.Radius with get() = radius and set(value) = radius <- value

By the way, if the whole code of the property can fit in one line, create it on a line. Here is an example:

type Circle() =
    let mutable radius = 0.00
    member me.Radius with get() = radius and set(value) = radius <- value

To specify or change the value of the property outside the class, use the <- operator. Here is an example:

type Circle() =
    let mutable radius = 0.00
    member me.Radius with get() = radius and set(value) = radius <- value
    member this.Diameter with get() = radius * 2.00
    member this.Circumference with get() = this.Diameter * 3.14156
    member this.Area with get() = radius * radius * 3.14156
    
let round = Circle()
round.Radius <- 48.06

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius:        %0.02f" round.Radius
printfn "Diameter:      %0.02f" round.Diameter
printfn "Circumference: %f" round.Circumference
printfn "Area:          %f\n" round.Area

This would produce:

Circle Characteristics
----------------------
Radius:        48.06
Diameter:      96.12
Circumference: 301.966747
Area:          7256.260935

Press any key to continue . . .

If necessary, you can use many lines to create a property. In this case, you can add the with get() ... section on its own line and the setter, if available, on its own line. Remember to indent. Here are examples:

type Circle() =
    let mutable radius = 0.00
    member me.Radius
        with get() = radius
        and set(value) = radius <- value
    member this.Diameter with get() = radius * 2.00
    member this.Circumference
        with get() = this.Diameter * 3.14156
    member this.Area with get() = radius * radius * 3.14156

In the same way, you can create as many read-write properties as you judge necessary. Here are examples:

type Vehicle(tag : string, make : string, model : string, year : int) =
    let mutable tagNbr = tag
    let mutable manufacturer = make
    let mutable mdl = model
    let mutable yr = year
    member this.TagNumber
        with get() = tagNbr
        and set(value) = tagNbr <- value
    member this.Make
        with get() = manufacturer
        and set(value) = manufacturer <- value
    member this.Model
        with get() = mdl
        and set(value) = mdl <- value
    member this.Year
        with get() = yr
        and set(value) = yr <- value
    new() = Vehicle("", "", "", 1960)

let car = Vehicle()
car.TagNumber <- "502-484"
car.Make <- "Dodge"
car.Model <- "Charger"
car.Year <- 2014

printfn "Vehicle Information"
printfn "Tag #: %s" car.TagNumber
printfn "Make:  %s" car.Make
printfn "Model: %s" car.Model
printfn "Year:  %i\n" car.Year

This would produce:

Vehicle Information
Tag #: 502-484
Make:  Dodge
Model: Charger
Year:  2014

Press any key to continue . . .

The above formula allows you to create a read-write property in one block. As an alternative, you can create a  read-write property in two different sections. The read property would be created in its own section and the write property in its section. Here is an example:

type Vehicle() =
    let mutable tagNbr = string
    member this.TagNumber with get() = tagNbr
    member this.TagNumber with set(value) = tagNbr <- value

Using a Reference Cell

Instead of an explicit mutable member, you can use a reference cell to control the value of a property outside the class. In the class, start by assigning a ref value to the member variable. You can use a value of your choice or an argument from the constructor of the class. Here is an example:

type Square(side) =
    let s = ref side;

When implementing the property, to assign a value to the get() function, get a reference to the member variable using the ! operator. To assign the argument of the set() function, use the := operator. To specify or set the value of property outside the class, use the <- operator. Here are examples:

type Square(side) =
    let s = ref side

    member Square.Side with get() = !s and set(value) = s := value
    member Square.Perimeter with get() = !s * 4.00
    member Square.Area with get() = !s * !s

let show (sqr : Square) =
    printfn "Square Characteristics"
    printfn "----------------------"
    printfn "Side:      %0.02f" sqr.Side
    printfn "Perimeter: %0.04f" sqr.Perimeter
    printfn "Area:      %0.04f" sqr.Area
    printfn "==========================="
    
let paper = Square(25.75)
show paper
paper.Side <- 48.68
show paper

This would produce:

Square Characteristics
----------------------
Side:      25.75
Perimeter: 103.0000
Area:      663.0625

===========================
Square Characteristics
----------------------
Side:      48.68
Perimeter: 194.7200
Area:      2369.7424
===========================

Press any key to continue . . .
 
   
 

Types of Properties

 

Automatic Properties

The F# (like the C#) language, provides an easy and fast way to create a read-write property. To create such a property, replace the this keyword, the self-identifier, or that class name applied to the name of the property, with the val keyword, assign the default value to the name of the property, and use the with get, set clause in place of defining the separate sections of the property. The formula to follow is:

member val PropertyName = DefautValue = with get, set

Here is an example:

type Vehicle() =
    member val TagNumber = "" with get, set

A better alternative is to assign a constructor parameter to the property name. Here is an example:

type Vehicle(tag) =
    member val TagNumber = tag with get, set

After defining the property, you can use it as you see fit, such as displaying its value outside the class. Here is an example:

type Vehicle(tag) =
    member val TagNumber = tag with get, set

let small = Vehicle("208048")
printfn "Vehicle Registration - Vehicle Information"
printfn "Tag Number: %s" small.TagNumber

Properties of Primitive Types

Because F# is an inferred language, its compiler works tremendously behind the scenes to find out the appropriate data type to apply to a variable or to a property if a data type is not specified. Still, when creating a property, you may prefer to specify the desired data type. As a reminder, the formula to create a read-only property is:

member [ this | Self-Identifier | Class-Name ].Property-Name with get() : Data-Type = Some-Value

In this case, specify the data type after the with get() expression. Here is an example:

type Circle(radius) =
    let r = radius
    member Circle.Radius with get() : double = r

In a write-only property, you specif the data type on the parameter of the set() member. The formula to follow is:

member [ this | Self-Identifier | Class-Name ].Property-Name with set(Parameter : Data-Type) = body

Here is an example:

type Circle() =
    let mutable rad : double = 0.00
    member me.Radius with set(value : double) = rad <- value

To specify the data type of a read-write property, the formula to follow is:

member [ this | Self-Identifier | Class-Name ].Property-Name
    with get() : Data-Type = Some-Value
    and set(Parameter : Data-Type) = body

You can specify the data type of getter only, to the setter only, or to both. Here are examples:

type Vehicle(tag : string, make : string, model : string, year : int) =
    let mutable tagNbr = tag
    let mutable manufacturer = make
    let mutable mdl = model
    let mutable yr = year
    member this.TagNumber
        with get() : string = tagNbr
        and set(value) = tagNbr <- value
    member this.Make
        with get() = manufacturer
        and set(value : string) = manufacturer <- value
    member this.Model
        with get() : string = mdl
        and set(value : string) = mdl <- value
    member this.Year
        with get() : int = yr
        and set(value : int) = yr <- value
    new() = Vehicle("", "", "", 1960)
 
 
   
 

Previous Copyright © 2014-2015 FunctionX Next