Consider the following class: type BillCollector(item) = member this.GottenBack = item The constructor of this class takes takes one parameter and assigns it to a member variable in the body of the class. Neither the constructor nor the member variable specifies the type of value the parameter will carry or use. In fact, here is a way to run the program: type BillCollector(item) = member this.GottenBack = item let coll = BillCollector(2250.75); printfn "Money Collected: %.2F" coll.GottenBack; This would produce: Money Collected: 2250.75 Here is another run of the program: type BillCollector(item) = member this.GottenBack = item let coll1 = BillCollector("Honda Accord 2002"); printfn "Vehicle Collected: %s" coll1.GottenBack; This would produce: Vehicle Collected: Honda Accord 2002 When creating a class, you may want to have one or more members whose type(s) is(are) not known at the time the class is created. This is typical for a class that would be used to create list. That is, you may want to create a list-based class (also called a collection class) that can be used to create lists of any kinds. A generic class is one who main member variable is not known in advance.
The above class indicates, as we saw with functions, that any class in F# is generic by default. Still, you can formally create a generic class when necessary. To create a generic class, after the name of the class, type <>. Inside the operator, type a letter or word preceded by '. This would represent the generic type, indicating that the data type that will be used is not determined at this time. In the parentheses of any constructor that uses a parameter that will use the generic type, follow the name of the parameter with ' and the generic letter or word. In the body of the class, use the parameter as you see fit. Here is an example: type BillCollector<'T>(item : 'T) = member this.GottenBack = item; When creating an object, you can call the constructor of the class by simply using its name. Here is an example: let coll = BillCollector(2250.75); printfn "Money Collected: %.2F" coll.GottenBack; As an alternative when declaring a variable from the class, to indicate that the constructor is using a generic parameter, on the right side of the name of the class, type <> and include the appropriate data type used by the parameter. Here is an example: type BillCollector<'T>(item : 'T) = member this.GottenBack = item let coll = BillCollector<float>(2250.75); printfn "Money Collected: %.2F" coll.GottenBack; Here is another version of creating an object from the class: type BillCollector<'T>(item : 'T) = class member this.GottenBack = item end let coll = BillCollector<string>("Honda Accord 2002"); printfn "Vehicle Collected: %s" coll.GottenBack; A generic class can have various members that use the same type. In this case, in the parentheses of the constructor, enter each paremeter followed by : ' and the generic letter or word. Eventually, in the body of the class, use each parameter as you see fit. type BillCollector<'T>(info : 'T, add : 'T) = class member this.GottenBack = info member this.Additional = add end let coll = BillCollector<string>("Honda Accord 2002", "Pass Due"); printfn "Vehicle Delinquency: %s" coll.GottenBack; printfn "Payment Status: %s" coll.Additional; This would produce: Vehicle Delinquency: Honda Accord 2002 Payment Status: Pass Due Press any key to continue . . . Here is another run of the program: type BillCollector<'T>(info : 'T, add : 'T) = class member this.GottenBack = info member this.Additional = add end let coll = BillCollector<bool>(false, true); printfn "Has been keeping payments: %b" coll.GottenBack; printfn "Time to collect: %b" coll.Additional; This would produce: Has been keeping payment: false Time to collect: true Press any key to continue . . .
A generic class can use different generic types. To specify those types, In the <> operator, type a lette or word preceded by ' for each paramater. In the parentheses of the constructor, apply the appropriate letter or word to the desired parameter. In the body of the class, use each parameter as you judge necessary. Here is an example: type BillCollector<'U, 'V>(item : 'U, add : 'V) = class member this.GottenBack = item member this.Additional = add end When you create an object from the class, on the right side of the constructor, add the <> operator. Inside that operator, enter the actual data types that the arguments use, separated by commas. In the parentheses of the constructor, pass the arguments, each conform to the corresponding type of the <> operator. Here is an example: type BillCollector<'U, 'V>(item : 'U, add : 'V) = class member this.GottenBack = item member this.Additional = add end let coll = BillCollector<string, float>("Honda Accord 2002", 6250.85); printfn "Vehicle Collected: %s" coll.GottenBack; printfn "Item Value: %.02F" coll.Additional; This would produce: Vehicle Collected: Honda Accord 2002 Item Value: 6250.85 Here is another run of the program: let coll = BillCollector<int, bool>(6, true); printfn "Delinquency Period: %d months" coll.GottenBack; printfn "Patience Run Out: %b" coll.Additional; This would produce: Delinquency Period: 6 months Patience Run Out: true Press any key to continue . . . In the same way, when you create a class, you can use as many generic types as you want in your class. You can also use a mixture of generic and non-generic parameters. In this case, create the generic types in the <> operator. In the parentheses of the class, include as many parameters as you want. Each generic parameter must be accompanied by its generic type. In the body of the class, use the parameters as you judge necessary. Here is an example: type BillCollector<'U, 'V>(cat : 'U, item : string, add : 'V) = class member this.Category = cat member this.GottenBack = item member this.Additional = add end When creating the object, after starting to declare the variable, on the right side of the constructor, add <> in which you must specify (only) the actual type(s) of the generic parameter(s). Here are examples: type BillCollector<'U, 'V>(cat : 'U, item : string, add : 'V) = class member this.Category = cat member this.GottenBack = item member this.Additional = add end let coll = BillCollector<int, bool>(1, "Honda Accord 2002", true); printfn "Item Category: %d" coll.Category; printfn "Vehicle Collected: %s" coll.GottenBack; printfn "Collection Started: %b" coll.Additional; This would produce: Item Category: 1 Vehicle Collected: Honda Accord 2002 Collection Started: true Press any key to continue . . . Here is another run of the program: type BillCollector<'U, 'V>(cat : 'U, item : string, add : 'V) = class member this.Category = cat member this.GottenBack = item member this.Additional = add end let coll = BillCollector<string, float>("Unresponsive", "Boat - Yamaha 212SS", 22480.00); printfn "Item Category: %s" coll.Category; printfn "Item to Collect: %s" coll.GottenBack; printfn "Remaining Value: %.02F" coll.Additional; This would produce: Item Category: Unresponsive Item to Collect: Boat - Yamaha 212SS Remaining Value: 22480.00 Press any key to continue . . .
A method is referred to as generic if it takes a generic parameter. Of course, the generic type must have been defined in the name of the class. On the right side of the name of the method, apply the same generic type. Here is an example: type BillCollector<'T>(item : 'T) = class member this.GottenBack = item member this.Show<'T> (i : 'T) = printfn "%A" i; end When calling the method, pass the same type of argument you pass to the constructor when creating an object. Here is an example: type BillCollector<'T>(item : 'T) =
class
member this.GottenBack = item
member this.Show<'T> (i : 'T) =
printfn "%A" i;
end
let coll = BillCollector<string>("Honda Accord 2002");
printfn "Vehicle Collected: %s" coll.GottenBack;
coll.Show "Reason: Payment Delinquency";
This would produce: Vehicle Collected: Honda Accord 2002 "Reason: Payment Delinquency" Press any key to continue . . .
In all places where we have used generic types so far, almost any value could be used. In some cases when creating a generic function or a generic class, you may want to put a restriction on the types of values that can be used on the generic parameter. Putting a restriction on a generic parameter is referred to as constraining the generic type. Generic constraining is done using a keyword named when. You have various options. To indicate that a parameter type of a function or class must be based on an object created from a structure, in the <> operator of the function name or of the class name, after the generic letter or word, type when followed by the same generic type, followed by : struct>. Here are examples: let display<'T when 'T : struct> value = ...; type Calculation<'T when 'T : struct>(x : 'T, y : 'T) = class end To indicate that a parameter type of a function or class must be based on an object created from a class, in the <> operator, use not struct. Here are examples: let display<'T when 'T : not struct> value = ...; type Calculation<'T when 'T : not struct>(x : 'T, y : 'T) = class end To indicate that a parameter type can allow null values, in the <> operator, instead of struct, use null. Here are examples: let display<'T when 'T : null> value = ...; type Calculation<'T when 'T : null>(x : 'T, y : 'T) = class end To indicate that Boolean comparisons can be performed on the values of the generic type, use comparison. Here are examples: let display<'T when 'T : comparison> value = ...; type Calculation<'T when 'T : comparison>(x : 'T, y : 'T) = class end To indicate that the comparison for equality can be performed on the values of the generic type, use equality. Here are examples: let display<'T when 'T : equality> value = ...; type Calculation<'T when 'T : equality>(x : 'T, y : 'T) = class end You can specify that only values or objects of a certain class can be used as parameters for a certain function or class. Of course, you must have a class. You can use an existing class or you can first create one. Once you know the class you want to use as the basis for a generic type, to put the constraint, in the <> operator of the class name, after the generic letter or word, type when followed by the same generic type, the :> operator, and the name of the class. Here are examples: type Numeric(a, b) = class member this.Add = a + b member this.Multiply = a * b member this.Subtract = a - b member this.Divide = if b <> 0 then a / b else 0 end type Calculation<'T when 'T :> Numeric> (u : 'T) = class member this.Show = u; end When creating an object, specify the data type as the class you had specified as the generic constraint. Here is an example: type Numeric(a, b) = class member this.Add = a + b member this.Multiply = a * b member this.Subtract = a - b member this.Divide = if b <> 0 then a / b else 0 end type Calculation<'T when 'T :> Numeric> (u : 'T) = class member this.Show = u; end let oper = Numeric(15, 49); let result = Calculation<Numeric>(oper); |
|
|||||||||||||
|