Introduction to the Properties of a Class
Introduction to 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 a (private) let variable:
type Payroll() =
let summary = string
On the other hand, member variables can be used inside a class or can communicate with the objects that are outside the class. 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. 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 roles, there are three categories of properties.
Introduction to Read-Only Properties
Getting the Value of the Property
A property is described as read-only if its only job is to present a value from the class. To create a read-only property, you use a function named get. The formula to create a read-only property is:
member self-identifier | class-name.property-name with get() = some-value
Start with the member keyword and an empty space. This is followed by a self-identifier. The self-identifier is followed by a period.
The self-identifier which can be any readable letter in lowercase (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, v, w, x, y, z) or uppercase. Here are examples
type Vehicle() =
member a.
member a.
member a.
member a.
The self-identifier can also be any word of your choice. If you decide to use a word, that word must follow the rules we saw for names of variables. If you want to do like Python programmers, you can use the word self as the self-identifier. If you want to do like Visual Basic programmers, you can use the word me, Me, my, or My as the self-identifier. If you want to do like C++/Java/C# programmers, you can use the word this (or This) as the self-identifier. The self-identifier can also be the name of the class. Here are examples:
type Vehicle() = member Vehicle. member Vehicle. member Vehicle. member Vehicle.
and the desired name of the property. The name of the property follows the rules we saw for names of classes (start in uppercase; add letters, digits, and/or underscores). The name is followed by the with get() expression.
After the with get() expression, type = followed by 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 you want. To access the value of the property outisde, create an object of the class, apply the period operator 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 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 result 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 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 self-identifier | class-name.property-name : = 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 . . .
Introduction to Write-Only Properties
A property is described 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 a function named set. This function takes one argument. The formula to create a write-only property is:
member self-identifier | class-name.property-name with set(parameter) = body
As seen already, start with the member keyword followed by a space and a self-identifier or the name of the class. This is followed by a period and a name for the property as we described for the read-only property. This is followed by the with set() expression. In the parentheses of the set() function, 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 write-only property 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 . . .
Using a Reference Cell with a Read-Only Property
Instead of a mutable variable, you can 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 write-only property, 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
A property can be used both to write a value and/or to read one from a class. Such a property is described to as read-write. The formula to create a read-write property is a combination of those we used already:
member self-identifier | class-name.property-name with get() : = 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 get() function is the same used in the set(some-value) clause. 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
Then create the property following the above formula. 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 the 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 . . .
The Type of a Primitive Property
Introduction
Because F# is an inferred language, its compiler works 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.
The Type of a Read-Only Property
If you are creating a ready-only property, the formula to follow is:
member 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
The Type of a Write-Only Property
If you want to specify the data type of a write-only property, the updated formula to follow is:
member 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 self-identifier | class-name.property-name with get() : data-type = some-value and set(parameter) data-type = body
The Type of a Read/Write Property
You can specify the data type to the getter only, to the setter only, or to both. To specify the data type of the property, after the with get/set() expression, type a colon followed by the desired name of the type. The colon or data type is followed by = and a value. 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)
Options on Creating 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 self-identifier | 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 expression in place of defining the separate sections of the property. The formula to follow is:
member val property-name = defaut-value = 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
A Simple Read-Only Property
The F# language makes it very easy to create a read-only property, which is a property whose value cannot be changed by the user. Remember that, to create a simple property, use the member, an empty space, a self-identifier, a period, and a name for the property. To create it as a read-only property, you can simply assign a value to this description of the property. Here are examples:
type Address() = member self.Recipient = "Melissa Durand" member self.StreetNumberAndName = "13828 Anderson Briar Road" member self.Suite = "D12" member self.City = "Lake City" member self.State = "South Carolina" member self.ZIPCode = "29560"
Remember that you can include the body of the class between class and end. The above example can be written as follows:
type Address() = class member self.Recipient = "Melissa Durand" member self.StreetNumberAndName = "13828 Anderson Briar Road" member self.Suite = "D12" member self.City = "Lake City" member self.State = "South Carolina" member self.ZIPCode = "29560" end
Remember that, to use a class, you can declare a variable an initialize it with the name of the class. Here is an example:
type Property() =
member my.MarketValue = 494_680
let sf = Property()
Then, to access a member of the class, type a period and the desired member. Here are examples:
type Address() = member self.Recipient = "Melissa Durand" member self.StreetNumberAndName = "13828 Anderson Briar Road" member self.Suite = "D12" member self.City = "Lake City" member self.State = "South Carolina" member self.ZIPCode = "29560" let adrs = Address() printfn "Personal Address" printfn "------------------------------------------" printfn "Recipient #: %s" adrs.Recipient printfn "Address: %s %s" adrs.StreetNumberAndName adrs.Suite printfn "City: %s" adrs.City printfn "State: %s" adrs.State printfn "ZIP-Code: %s" adrs.ZIPCode printfn "=========================================="
This would produce:
Personal Address ------------------------------------------ Recipient #: Melissa Durand Address: 13828 Anderson Briar Road D12 City: Lake City State: South Carolina ZIP-Code: 29560 ========================================== Press any key to close this window . . .
External Values for Class Members
We already saw that, one way to provide an external value to a property, or to let a user specify the value of a property, is to create a parameter in a constructor, and then assign that value to a simple property. Here is an example:
type Membership(cat) = member me.Category = cat
If you want, you can specify the data type of the parameter, and sometimes this is helpful or necessary. Here is an example:
type Vehicle(tag : string) = member this.TagNumber = tag
After doing this, to provide a value to the member variable, when creating an object, pass the desired value to the constructor of the class. After doing this, you can access the parameter of the constructor in the body of the class and use its value. Here is an example:
type Membership(cat) = do printfn "Membership Details" printfn "----------------------" printfn "Category: %s" cat member me.Category = cat let mbr = Membership("Executive")
This would produce:
Membership Details ---------------------- Category: Executive ========================================== Press any key to close this window . . .
In the same way, you can create a constructor with as many parameters as you want. Separate the parameters in the parentheses of the constructor with comas. Here is an example:
type Vehicle(tag, make, model) =
class
. . . Members
end
If you want, or to be more explicit, you can (should) specify the data types of the parameters. Here are examples:
type Vehicle(tag : string, make : string, model : string) = class . . . Members end
When creating an aobject, pass the desired value for each member. Then use those members as you see fit. Here are examples:
type Membership(name, cat, fee) = do printfn "Membership Details" printfn "---------------------------" printfn "Member Name: %s" name printfn "Category: %s" cat printfn "Annual Fee: $%i" fee // Assignments member me.Name = name member me.Category = cat member me.Fee = fee let mbr = Membership("Sandrine Beul", "Executive", 25_000) printfn "==========================="
This would produce:
Membership Details --------------------------- Member Name: Sandrine Beul Category: Executive Annual Fee: $25000 =========================== Press any key to close this window . . .
Remember that, when adding the parameters to a contructor, you can specify their typss. Here are examples:
type Vehicle(tag : string, make : string, model : string, yr : int) = member this.TagNumber = tag member this.Make = make member this.Model = model member this.Year = yr
Remember that you can create different constructors using the new member function with different numbers of parameters. Here are examples:
type Vehicle(tag : string, make : string, model : string, yr : int) =
member this.TagNumber = tag
member this.Make = make
member this.Model = model
member this.Year = yr
new() = Vehicle()
new(tag : string) = Vehicle(tag, "Unknown", "N/A", 1900)
new(tag : string, yr : int) = Vehicle(tag, "Unknown", "N/A", yr)
|
|||
Previous | Copyright © 2014-2024, FunctionX | Monday 14 February 2022 | Next |
|