Details on Pattern Matching
Details on Pattern Matching
The Nullity and Type of Objects
Checking the Type of an Object
We have already been introduced to a data type named object. This is the most fundamental type in C# (and the .NET Framework) and, in some cases, it can be used in place of any data type. As a matter of fact, if you are creating a function that would treat various types of objects, you can pass an object type to it. Then, in the body of the function, before doing anything, find out what type was passed as argument. To do that, use the is operator to compare the argument to the desired type. Here is an example:
using static System.Console;
void Present(object obj)
{
if (obj is Microwave)
{
Microwave mw = (Microwave)obj;
WriteLine("Make and Model: " + mw.Make + " " + mw.Model);
WriteLine("Capacity: {0}", mw.Capacity);
WriteLine("Microwave Options: {0}", mw.MenuOptions);
WriteLine("==========================================");
}
}
Microwave machine = null;
Present(machine);
machine = new Microwave()
{
Make = "Farberware",
Model = "FMO12AHTBSG",
Capacity = 1.2,
MenuOptions = 9
};
Person friend = new Person();
Present(friend);
Present(machine);
Bicycle voyage = new Bicycle();
Present(voyage);
public class Microwave
{
public string Make { get; set; }
public string Model { get; set; }
public double Capacity { get; set; }
public int MenuOptions { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public class Bicycle
{
public int Speeds { get; set; }
public double Price { get; set; }
}
Practical Learning: Introducing Nullity of Objects
using static System.Console; (Classification iClass, string name) IdentifyAccount(string aType) { if (aType == "1") return (Classification.Residential, "Residential Household"); else if (aType == "2") return (Classification.NonProfit, "Social/Non-Profit Organization"); else if (aType == "3") return (Classification.WaterIntensive, "Water Intensive Business (Laudromat, Hair Salon, etc)"); else if (aType == "4") return (Classification.GeneralUse, "General Business, Government"); else return (Classification.Other, "Other"); } double Subtract(in double a, in double b) { return a - b; } double CalculateHCFTotal(double gallons) { return gallons / 748.00; } (double a, double b, double c) EvaluateTherms(in double hcf) { double first; double next; double above; if (hcf is > 40.00) { first = 25.00 * 1.5573; next = 15.00 * 1.2264; above = (hcf - 40.00) * 0.9624; } else if (hcf > 25.00) { first = 25.00 * 1.5573; next = (hcf - 25.00) * 1.2264; above = 0.00; } else { first = hcf * 1.5573; next = 0.00; above = 0.00; } return (first, next, above); } double EvaluateSewerCharges(in double charges, in Classification @class) { double result; switch (@class) { case Classification.Residential: result = charges * 0.2528; break; case Classification.NonProfit: result = charges * 0.0915; break; case Classification.WaterIntensive: result = charges * 1.5865; break; case Classification.GeneralUse: result = charges * 0.6405; break; default: result = charges * 1.2125; break; } return result; } double EvaluateEnvironmentCharges(in double charges, in Classification @class) { double result; switch (@class) { case Classification.Residential: result = charges * 0.0025; break; case Classification.NonProfit: result = charges * 0.0015; break; case Classification.WaterIntensive: result = charges * 0.0105; break; case Classification.GeneralUse: result = charges * 0.0045; break; default: result = charges * 0.0125; break; } return result; } double EvaluateServiceCharges(in double charges, in Classification @class) { double result; switch (@class) { case Classification.Residential: result = charges * 0.0328; break; case Classification.NonProfit: result = charges * 0.0115; break; case Classification.WaterIntensive: result = charges * 0.1015; break; case Classification.GeneralUse: result = charges * 0.0808; break; default: result = charges * 0.1164; break; } return result; } double AddTriple(in (double a, double b, double c) triple) { return triple.a + triple.b + triple.c; } double Add3(in double a, in double b, in double c) { return a + b + c; } double Add4(in double a, in double b, in double c, in double d) { return a + b + c + d; } double CalculateLocalTaxes(in double charges, in Classification @class) { double result; switch (@class) { case Classification.Residential: result = charges * 0.115; break; case Classification.NonProfit: result = charges * 0.085; break; case Classification.WaterIntensive: result = charges * 0.825; break; case Classification.GeneralUse: result = charges * 0.315; break; default: result = charges * 0.625; break; } return result; } double CalculateStateTaxes(in double charges, in Classification @class) { double result; switch (@class) { case Classification.Residential: result = charges * 0.035; break; case Classification.NonProfit: result = charges * 0.015; break; case Classification.WaterIntensive: result = charges * 0.105; break; case Classification.GeneralUse: result = charges * 0.065; break; default: result = charges * 0.815; break; } return result; } string GetCustomerType() { WriteLine("Stellar Water Point"); WriteLine("==========================================================="); WriteLine("To prepare an invoice, enter the following information"); WriteLine("Types of Accounts"); WriteLine("1. Residential Household"); WriteLine("2. Social/Non-Profit Organization"); WriteLine("3. Water Intensive Business (Laudromat, etc)"); WriteLine("4. General Business"); WriteLine("0. Other"); Write("Enter Type of Account: "); string answer = ReadLine()!; WriteLine("-----------------------------------------------------------"); return answer; } int ReadCounter(out int start, out int end) { end = 0; start = 0; try { Write("Counter Reading Start: "); start = int.Parse(ReadLine()!); } catch(Exception ex) when (ex is FormatException fex) { WriteLine("The counter reading start value you entered is not valid."); WriteLine("The error produced is: " + fex.Message); } try { Write("Counter Reading End: "); end = int.Parse(ReadLine()!); } catch (Exception ex) when (ex is FormatException fex) { WriteLine("The counter reading end value you entered is not valid."); WriteLine("The error produced is: " + fex.Message); } return 1_000; } int readingEnd; int readingStart; string acntType = GetCustomerType(); _ = ReadCounter(out readingStart, out readingEnd); var account = IdentifyAccount(acntType); double gallons = Subtract(readingEnd, readingStart); double HCFValue = CalculateHCFTotal(gallons); (double First25Therms, double Next15Therms, double Above40Therms) therms = EvaluateTherms(HCFValue); double water = AddTriple(therms); double sewer = EvaluateSewerCharges(water, account.iClass); double environmentCharges = EvaluateEnvironmentCharges(water, account.iClass); double serviceCharges = EvaluateServiceCharges(water, account.iClass); double totalCharges = Add4(water, sewer, environmentCharges, serviceCharges); double localTaxes = CalculateLocalTaxes(totalCharges, account.iClass); double stateTaxes = CalculateStateTaxes(totalCharges, account.iClass); double amountDue = Add3(totalCharges, localTaxes, stateTaxes); WriteLine("==========================================================="); WriteLine("Stellar Water Point - Customer Invoice"); WriteLine("Account Type: {0}", account.name); WriteLine("==========================================================="); WriteLine("Meter Reading"); WriteLine("-----------------------------------------------------------"); WriteLine("Counter Reading Start: {0,10}", readingStart); WriteLine("Counter Reading End: {0,10}", readingEnd); WriteLine("Total Gallons Consumed: {0,10}", gallons); WriteLine("==========================================================="); WriteLine("Therms Evaluation"); WriteLine("-----------------------------------------------------------"); WriteLine("HCF Total: {0,10:n}", HCFValue); WriteLine("First 35 Therms (* 1.5573): {0,10:n}", therms.First25Therms); WriteLine("First 20 Therms (* 1.2264): {0,10:n}", therms.Next15Therms); WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", therms.Above40Therms); WriteLine("-----------------------------------------------------------"); WriteLine("Water Usage Charges: {0,10:n}", water); WriteLine("Sewer Charges: {0,10:n}", sewer); WriteLine("==========================================================="); WriteLine("Bill Values"); WriteLine("-----------------------------------------------------------"); WriteLine("Environment Charges: {0,10:n}", environmentCharges); WriteLine("Service Charges: {0,10:n}", serviceCharges); WriteLine("Total Charges: {0,10:n}", totalCharges); WriteLine("Local Taxes: {0,10:n}", localTaxes); WriteLine("State Taxes: {0,10:n}", stateTaxes); WriteLine("-----------------------------------------------------------"); WriteLine("Amount Due: {0,10:n}", amountDue); WriteLine("==========================================================="); internal enum Classification { Residential, NonProfit, WaterIntensive, GeneralUse, Other }
Stellar Water Point =========================================================== To prepare an invoice, enter the following information Types of Accounts 1. Residential Household 2. Social/Non-Profit Organization 3. Water Intensive Business (Laudromat, etc) 4. General Business 0. Other Enter Type of Account: 3 ----------------------------------------------------------- Counter Reading Start: 41 Counter Reading End: 246885 =========================================================== Stellar Water Point - Customer Invoice Account Type: Water Intensive Business (Laudromat, Hair Salon, etc) =========================================================== Meter Reading ----------------------------------------------------------- Counter Reading Start: 41 Counter Reading End: 246885 Total Gallons Consumed: 246844 =========================================================== Therms Evaluation ----------------------------------------------------------- HCF Total: 330.01 First 35 Therms (* 1.5573): 38.93 First 20 Therms (* 1.2264): 18.40 Above 40 Therms (* 0.9624): 279.10 ----------------------------------------------------------- Water Usage Charges: 336.43 Sewer Charges: 533.75 =========================================================== Bill Values ----------------------------------------------------------- Environment Charges: 3.53 Service Charges: 34.15 Total Charges: 907.86 Local Taxes: 748.98 State Taxes: 95.32 ----------------------------------------------------------- Amount Due: 1,752.16 =========================================================== Press any key to close this window . . .
Type and Inheritance
The objects you use in your applications have many origins. Some of them are from classes you create and some others are from existing classes. Another issue is that many classes are either derived from other classes, include abstract parent classes, and other classes implement one or more interfaces. Here is an example of different classes that implement the same interface:
public interface IAppliance { public string? Make { get; set; } public string? Model { get; set; } public (double width, double height, double depth) Dimensions { get;set; } public double Price { get; set; } } public class Microwave : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public double Capacity { get; set; } public int MenuOptions { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public double Price { get; set; } } public class Dishwasher : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public int NumberOfRacks { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public double Price { get; set; } } public class Refrigerator : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public (double freezer, double refrigerator) Storage { get; set; } public bool VoiceAssisted { get; set; } public double Price { get; set; } }
You surely remember (from previous lessons) that from an abstract class or an interface, you can declare a variable but initialize it with a class derived from the abstract class or a class that implements the interface. Here are examples:
IAppliance micro = new Microwave() { Make = "Chefman", Model = "RJ55-SS-9", Capacity = 0.9, // Cubic Feet Dimensions = (19.09, 11.54, 14.76), // W x H x D Price = 88.98 }; IAppliance washer = new Dishwasher() { Make = "SPT", Model = "SD-9254W", NumberOfRacks = 2, Dimensions = (17.6, 32.4, 22.6), // W x H x D Price = 459.95 }; IAppliance cold = new Refrigerator() { Make = "SMETA", Model = "N/A", Dimensions = (35.9, 69.9, 29), // W x H x D Storage = (6.61, 15.89), // cu. ft. VoiceAssisted = false, Price = 1724.98 };
Some objects can have unpredictable origins, such as objects returned from functions, methods, libraries, etc. As a result, when a function or method receives an object as argument, it may be a good idea to first check the type of the object before performing any operation on it. You have many options.
As seen earlier, you can use the is keyword to check the type of an object that is passed as argument. This operator is even more valuable for object that share an ancestry. Consider the following example:
using static System.Console; IAppliance micro = new Microwave() { Make = "Chefman", Model = "RJ55-SS-9", Capacity = 0.9, // Cubic Feet Dimensions = (19.09, 11.54, 14.76), // W x H x D Price = 88.98 }; IAppliance washer = new Dishwasher() { Make = "SPT", Model = "SD-9254W", NumberOfRacks = 2, Dimensions = (17.6, 32.4, 22.6), // W x H x D Price = 459.95 }; IAppliance cold = new Refrigerator() { Make = "SMETA", Model = "N/A", Dimensions = (35.9, 69.9, 29), // W x H x D Storage = (6.61, 15.89), // cu. ft. VoiceAssisted = false, Price = 1724.98 }; void Present(IAppliance iApp) { if(iApp is Microwave) { WriteLine("Microwave Oven"); WriteLine("---------------------------------------"); WriteLine("Make: {0}", iApp.Make); WriteLine("Model: {0}", iApp.Model); WriteLine("Dimensions: {0}*W x {1}*H x {2}*D", iApp.Dimensions.width, iApp.Dimensions.height, iApp.Dimensions.depth); WriteLine("Price: {0}", iApp.Price); WriteLine("======================================="); } else { WriteLine("There is no machine to show."); } } WriteLine("======================================="); Present(micro); public interface IAppliance { public string? Make { get; set; } public string? Model { get; set; } public (double width, double height, double depth) Dimensions { get;set; } public double Price { get; set; } } public class Microwave : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public double Capacity { get; set; } public int MenuOptions { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public double Price { get; set; } } public class Dishwasher : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public int NumberOfRacks { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public double Price { get; set; } } public class Refrigerator : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public (double freezer, double refrigerator) Storage { get; set; } public bool VoiceAssisted { get; set; } public double Price { get; set; } }
This would produce:
======================================= Microwave Oven --------------------------------------- Make: Chefman Model: RJ55-SS-9 Dimensions: 19.09*W x 11.54*H x 14.76*D Price: 88.98 ======================================= Press any key to close this window . . .
As we have already seen, if you pass an interface as argument, in the body of the function or method, you can access only the members of the interfaces. If you want to access the members of a class derived from an abstract class or a class that implements the interface, you should pass the type of the argument as object. Then, in the body of the function or method, you can use a conditional statement that checks the type of the argument. This can be done exactly as done above with the interface. A better alternative is the concept of a type pattern.
The primary formula to test the compatibility of an object is as follow:
condition-keyword(existing-object is class-type new-object) statement(s);
Once again, you start with a condition-keyword, usually if, followed by parentheses. In the parentheses, access the object that was passed as argument. Add a space and the is keyword. Specify the class of the class of which the argument is supposed to be. End with a name for an object. After the parentheses, you can create one or more statements that use the new object. In the statement(s), use the new object and access its members from it. Here are examples:
using static System.Console; IAppliance micro = new Microwave() { Make = "Chefman", Model = "RJ55-SS-9", Capacity = 0.9, // Cubic Feet MenuOptions = 16, Dimensions = (19.09, 11.54, 14.76), // W x H x D Price = 88.98 }; IAppliance washer = new Dishwasher() { Make = "SPT", Model = "SD-9254W", NumberOfRacks = 2, Dimensions = (17.6, 32.4, 22.6), // W x H x D Price = 459.95 }; IAppliance cold = new Refrigerator() { Make = "SMETA", Model = "N/A", Dimensions = (35.9, 69.9, 29), // W x H x D Storage = (6.61, 15.89), // Cubic Feet VoiceAssisted = false, Price = 1724.98 }; void Present(object iApp) { if(iApp is Microwave mw) { WriteLine("Microwave Oven"); WriteLine("----------------------------------------------"); WriteLine("Make: {0}", mw.Make); WriteLine("Model: {0}", mw.Model); WriteLine("Capacity: {0} cu. ft.", mw.Capacity); WriteLine("Menu Options: {0}", mw.MenuOptions); WriteLine("Dimensions: {0}*W x {1}*H x {2}*D", mw.Dimensions.width, mw.Dimensions.height, mw.Dimensions.depth); WriteLine("Price: ${0}", mw.Price); WriteLine("=============================================="); } else if(iApp is Dishwasher dw) { WriteLine("Dishwasher"); WriteLine("----------------------------------------------"); WriteLine("Make: {0}", dw.Make); WriteLine("Model: {0}", dw.Model); WriteLine("Number of Racks: {0}", dw.NumberOfRacks); WriteLine("Dimensions: {0}*W x {1}*H x {2}*D", dw.Dimensions.width, dw.Dimensions.height, dw.Dimensions.depth); WriteLine("Price: ${0}", dw.Price); WriteLine("=============================================="); } else if(iApp is Refrigerator frigo) { WriteLine("Refrigerator"); WriteLine("----------------------------------------------"); WriteLine("Make: {0}", frigo.Make); WriteLine("Model: {0}", frigo.Model); WriteLine("Dimensions: {0}*W x {1}*H x {2}*D", frigo.Dimensions.width, frigo.Dimensions.height, frigo.Dimensions.depth); WriteLine("Storage"); WriteLine(" Freezer: {0} cu. ft.", frigo.Storage.freezer); WriteLine(" Refrigerator: {0} cu. ft.", frigo.Storage.refrigerator); WriteLine("Price: ${0}", frigo.Price); WriteLine("=============================================="); } else { WriteLine("There is no object to show."); } } WriteLine("============================================"); Present(micro); Present(washer); Present(cold); public interface IAppliance { public string? Make { get; set; } public string? Model { get; set; } public (double width, double height, double depth) Dimensions { get;set; } public double Price { get; set; } } public class Microwave : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public double Capacity { get; set; } public int MenuOptions { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public double Price { get; set; } } public class Dishwasher : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public int NumberOfRacks { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public double Price { get; set; } } public class Refrigerator : IAppliance { public string? Make { get; set; } public string? Model { get; set; } public (double width, double height, double depth) Dimensions { get; set; } public (double freezer, double refrigerator) Storage { get; set; } public bool VoiceAssisted { get; set; } public double Price { get; set; } }
This would produce:
============================================ Microwave Oven ---------------------------------------------- Make: Chefman Model: RJ55-SS-9 Capacity: 0.9 cu. ft. Menu Options: 16 Dimensions: 19.09*W x 11.54*H x 14.76*D Price: $88.98 ============================================== Dishwasher ---------------------------------------------- Make: SPT Model: SD-9254W Number of Racks: 2 Dimensions: 17.6*W x 32.4*H x 22.6*D Price: $459.95 ============================================== Refrigerator ---------------------------------------------- Make: SMETA Model: N/A Dimensions: 35.9*W x 69.9*H x 29*D Storage Freezer: 6.61 cu. ft. Refrigerator: 15.89 cu. ft. Price: $1724.98 ============================================== Press any key to close this window . . .
Switching a Pattern
Introduction
As we saw in the previous lessons, C# makes extensive use of the switch statement. Besides the way the switch keyword is used like the if...else conditional statements, C# adds some extended functionalities referred to as pattern matching.
Practical Learning: Processing a Switching Return
using static System.Console; (Classification iClass, string name) IdentifyAccount(string aType) { switch (aType) { case "1": return (Classification.Residential, "Residential Household"); case "2": return (Classification.NonProfit, "Social/Non-Profit Organization"); case "3": return (Classification.WaterIntensive, "Water Intensive Business (Laudromat, Hair Salon, etc)"); case "4": return (Classification.GeneralUse, "General Business, Government"); default: return (Classification.Other, "Other"); } } double Subtract(in double a, in double b) => a - b; double CalculateHCFTotal(double gallons) => gallons / 748.00; (double a, double b, double c) EvaluateTherms(in double hcf) { switch(hcf) { case > 40.00: return (25.00 * 1.5573, 15.00 * 1.2264, (hcf - 40.00) * 0.9624); case > 25.00: return (25.00 * 1.5573, (hcf - 25.00) * 1.2264, 0.00); default: return (hcf * 1.5573, 0.00, 0.00); } } double EvaluateSewerCharges(in double charges, in Classification @class) { switch (@class) { case Classification.Residential: return charges * 0.2528; case Classification.NonProfit: return charges * 0.0915; case Classification.WaterIntensive: return charges * 1.5865; case Classification.GeneralUse: return charges * 0.6405; default: return charges * 1.2125; } } double EvaluateEnvironmentCharges(in double charges, in Classification @class) { switch (@class) { case Classification.Residential: return charges * 0.0025; case Classification.NonProfit: return charges * 0.0015; case Classification.WaterIntensive: return charges * 0.0105; case Classification.GeneralUse: return charges * 0.0045; default: return charges * 0.0125; } } double EvaluateServiceCharges(in double charges, in Classification @class) { switch (@class) { case Classification.Residential: return charges * 0.0328; case Classification.NonProfit: return charges * 0.0115; case Classification.WaterIntensive: return charges * 0.1015; case Classification.GeneralUse: return charges * 0.0808; default: return charges * 0.1164; } } double AddTriple(in (double a, double b, double c) triple) => triple.a + triple.b + triple.c; double Add3(in double a, in double b, in double c) => a + b + c; double Add4(in double a, in double b, in double c, in double d) => a + b + c + d; double CalculateLocalTaxes(in double charges, in Classification @class) { switch (@class) { case Classification.Residential: return charges * 0.115; case Classification.NonProfit: return charges * 0.085; case Classification.WaterIntensive: return charges * 0.825; case Classification.GeneralUse: return charges * 0.315; default: return charges * 0.625; } } double CalculateStateTaxes(in double charges, in Classification @class) { switch (@class) { case Classification.Residential: return charges * 0.035; case Classification.NonProfit: return charges * 0.015; case Classification.WaterIntensive: return charges * 0.105; case Classification.GeneralUse: return charges * 0.065; default: return charges * 0.815; } } string GetCustomerType() { WriteLine("Stellar Water Point"); WriteLine("==========================================================="); WriteLine("To prepare an invoice, enter the following information"); WriteLine("Types of Accounts"); WriteLine("1. Residential Household"); WriteLine("2. Social/Non-Profit Organization"); WriteLine("3. Water Intensive Business (Laudromat, etc)"); WriteLine("4. General Business"); WriteLine("0. Other"); Write("Enter Type of Account: "); string answer = ReadLine()!; WriteLine("-----------------------------------------------------------"); return answer; } int ReadCounter(out int start, out int end) { end = 0; start = 0; try { Write("Counter Reading Start: "); start = int.Parse(ReadLine()!); } catch(Exception ex) when (ex is FormatException fex) { WriteLine("The counter reading start value you entered is not valid."); WriteLine("The error produced is: " + fex.Message); } try { Write("Counter Reading End: "); end = int.Parse(ReadLine()!); } catch (Exception ex) when (ex is FormatException fex) { WriteLine("The counter reading end value you entered is not valid."); WriteLine("The error produced is: " + fex.Message); } return 1_000; } int readingEnd; int readingStart; string acntType = GetCustomerType(); _ = ReadCounter(out readingStart, out readingEnd); var account = IdentifyAccount(acntType); double gallons = Subtract(readingEnd, readingStart); double HCFValue = CalculateHCFTotal(gallons); (double First25Therms, double Next15Therms, double Above40Therms) therms = EvaluateTherms(HCFValue); double water = AddTriple(therms); double sewer = EvaluateSewerCharges(water, account.iClass); double environmentCharges = EvaluateEnvironmentCharges(water, account.iClass); double serviceCharges = EvaluateServiceCharges(water, account.iClass); double totalCharges = Add4(water, sewer, environmentCharges, serviceCharges); double localTaxes = CalculateLocalTaxes(totalCharges, account.iClass); double stateTaxes = CalculateStateTaxes(totalCharges, account.iClass); double amountDue = Add3(totalCharges, localTaxes, stateTaxes); WriteLine("==========================================================="); WriteLine("Stellar Water Point - Customer Invoice"); WriteLine("Account Type: {0}", account.name); WriteLine("==========================================================="); WriteLine("Meter Reading"); WriteLine("-----------------------------------------------------------"); WriteLine("Counter Reading Start: {0,10}", readingStart); WriteLine("Counter Reading End: {0,10}", readingEnd); WriteLine("Total Gallons Consumed: {0,10}", gallons); WriteLine("==========================================================="); WriteLine("Therms Evaluation"); WriteLine("-----------------------------------------------------------"); WriteLine("HCF Total: {0,10:n}", HCFValue); WriteLine("First 35 Therms (* 1.5573): {0,10:n}", therms.First25Therms); WriteLine("First 20 Therms (* 1.2264): {0,10:n}", therms.Next15Therms); WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", therms.Above40Therms); WriteLine("-----------------------------------------------------------"); WriteLine("Water Usage Charges: {0,10:n}", water); WriteLine("Sewer Charges: {0,10:n}", sewer); WriteLine("==========================================================="); WriteLine("Bill Values"); WriteLine("-----------------------------------------------------------"); WriteLine("Environment Charges: {0,10:n}", environmentCharges); WriteLine("Service Charges: {0,10:n}", serviceCharges); WriteLine("Total Charges: {0,10:n}", totalCharges); WriteLine("Local Taxes: {0,10:n}", localTaxes); WriteLine("State Taxes: {0,10:n}", stateTaxes); WriteLine("-----------------------------------------------------------"); WriteLine("Amount Due: {0,10:n}", amountDue); WriteLine("==========================================================="); internal enum Classification { Residential, NonProfit, WaterIntensive, GeneralUse, Other }
Stellar Water Point =========================================================== To prepare an invoice, enter the following information Types of Accounts 1. Residential Household 2. Social/Non-Profit Organization 3. Water Intensive Business (Laudromat, etc) 4. General Business 0. Other Enter Type of Account: 8 ----------------------------------------------------------- Counter Reading Start: 882 Counter Reading End: 14729 =========================================================== Stellar Water Point - Customer Invoice Account Type: Other =========================================================== Meter Reading ----------------------------------------------------------- Counter Reading Start: 882 Counter Reading End: 14729 Total Gallons Consumed: 13847 =========================================================== Therms Evaluation ----------------------------------------------------------- HCF Total: 18.51 First 35 Therms (* 1.5573): 28.83 First 20 Therms (* 1.2264): 0.00 Above 40 Therms (* 0.9624): 0.00 ----------------------------------------------------------- Water Usage Charges: 28.83 Sewer Charges: 34.95 =========================================================== Bill Values ----------------------------------------------------------- Environment Charges: 0.36 Service Charges: 3.36 Total Charges: 67.50 Local Taxes: 42.19 State Taxes: 55.01 ----------------------------------------------------------- Amount Due: 164.70 =========================================================== Press any key to close this window . . .
Introduction to Switch Expressions
Consider the following function from things we learned in previous lessons:
using static System.Console;
int GetPayrollFrequency()
{
int time;
WriteLine("Enter the information to evaluate taxes");
WriteLine("Frequency by which the payroll is processed:");
WriteLine("1 - Weekly");
WriteLine("2 - Biweekly");
WriteLine("3 - Semimonthly");
WriteLine("4 - Monthly");
Write("Enter Payroll Frequency: ");
time = int.Parse(ReadLine());
return time;
}
double GetGrossSalary()
{
double sal;
Write("Gross Salary: ");
sal = double.Parse(ReadLine());
return sal;
}
double EvaluateIncomeTax(int frequency, double sal)
{
double incomeTax = 0.00;
switch (frequency)
{
case 1:
incomeTax = 271.08 + (sal * 24 / 100);
break;
case 2:
incomeTax = 541.82 + (sal * 24 / 100);
break;
case 3:
incomeTax = 587.12 + (sal * 24 / 100);
break;
case 4:
incomeTax = 1174.12 + (sal * 24 / 100);
break;
}
return incomeTax;
}
WriteLine("Payroll Evaluation");
WriteLine("============================================");
int period = GetPayrollFrequency();
WriteLine("--------------------------------------------");
double gs = GetGrossSalary();
double incomeTax = EvaluateIncomeTax(period, gs);
WriteLine("============================================");
WriteLine("Payroll Evaluation");
WriteLine("--------------------------------------------");
WriteLine("Gross Salary: {0,10:n}", gs);
WriteLine("Income Tax: {0,10:n}", incomeTax);
WriteLine("============================================");
Instead of creating individual cases in a switch statement, you can use the => operator. To proceed, instead of a switch(expression) keyword with parentheses, replace it with the following formula:
return expression switch
Start the section with the return and the switch keyword without parentheses. Between those keywords, type the same type of expression we have used so far, such as a variable. Follow the line with a body delimited by curly brackets as seen so far with any switch statement. In that body, create a section for each possible case. This time, instead of the case keyword followed by a colon, use the => operator. On the right side the => operator, write the expression that the case clause is supposed to return. End the expression with a comma and proceed with the next case. After the closing curly bracket, you must end the whole statement with a semicolon. Here is an example:
using static System.Console;
int GetPayrollFrequency()
{
int time;
WriteLine("Enter the information to evaluate taxes");
WriteLine("Frequency by which the payroll is processed:");
WriteLine("1 - Weekly");
WriteLine("2 - Biweekly");
WriteLine("3 - Semimonthly");
WriteLine("4 - Monthly");
Write("Enter Payroll Frequency: ");
time = int.Parse(ReadLine());
return time;
}
double GetGrossSalary()
{
double sal;
Write("Gross Salary: ");
sal = double.Parse(ReadLine());
return sal;
}
double EvaluateIncomeTax(int frequency, double sal)
{
return frequency switch
{
1 => 271.08 + (sal * 24 / 100),
2 => 541.82 + (sal * 24 / 100),
3 => 587.12 + (sal * 24 / 100),
4 => 1174.12 + (sal * 24 / 100)
};
}
WriteLine("Payroll Evaluation");
WriteLine("============================================");
int period = GetPayrollFrequency();
WriteLine("--------------------------------------------");
double gs = GetGrossSalary();
double incomeTax = EvaluateIncomeTax(period, gs);
WriteLine("============================================");
WriteLine("Payroll Evaluation");
WriteLine("--------------------------------------------");
WriteLine("Gross Salary:{0,20:n}", gs);
WriteLine("Income Tax: {0,20:n}", incomeTax);
WriteLine("============================================");
Here is an example of executing the program:
Payroll Evaluation ============================================ Enter the information to evaluate taxes Frequency by which the payroll is processed: 1 - Weekly 2 - Biweekly 3 - Semimonthly 4 - Monthly Enter Payroll Frequency: 1 -------------------------------------------- Gross Salary: 2466.88 ============================================ Payroll Evaluation -------------------------------------------- Gross Salary: 2,466.88 Income Tax: 863.13 ============================================ Press any key to close this window . . .
The Default Case of a Switch Expression
As seen in the previous lesson, a typical switch statement can lead to a situation that doesn't fit any of the addressed cases, in which case you should have a default clause. If you are creating a switch expression, to create a default clause, use the underscore operator.
Practical Learning: Processing Switching Returns
using static System.Console; (Classification iClass, string name) IdentifyAccount(string aType) { switch (aType) { case "1": return (Classification.Residential, "Residential Household"); case "2": return (Classification.NonProfit, "Social/Non-Profit Organization"); case "3": return (Classification.WaterIntensive, "Water Intensive Business (Laudromat, Hair Salon, etc)"); case "4": return (Classification.GeneralUse, "General Business, Government"); default: return (Classification.Other, "Other"); } } double Subtract(in double a, in double b) => a - b; double CalculateHCFTotal(double gallons) => gallons / 748.00; (double a, double b, double c) EvaluateTherms(in double hcf) { switch(hcf) { case > 40.00: return (25.00 * 1.5573, 15.00 * 1.2264, (hcf - 40.00) * 0.9624); case > 25.00: return (25.00 * 1.5573, (hcf - 25.00) * 1.2264, 0.00); default: return (hcf * 1.5573, 0.00, 0.00); } } double EvaluateSewerCharges(in double charges, in Classification @class) { return @class switch { Classification.Residential => charges * 0.2528, Classification.NonProfit => charges * 0.0915, Classification.WaterIntensive => charges * 1.5865, Classification.GeneralUse => charges * 0.6405, _ => charges * 1.2125, }; } double EvaluateEnvironmentCharges(in double charges, in Classification @class) { return @class switch { Classification.Residential => charges * 0.0025, Classification.NonProfit => charges * 0.0015, Classification.WaterIntensive => charges * 0.0105, Classification.GeneralUse => charges * 0.0045, _ => charges * 0.0125, }; } double EvaluateServiceCharges(in double charges, in Classification @class) { return @class switch { Classification.Residential => charges * 0.0328, Classification.NonProfit => charges * 0.0115, Classification.WaterIntensive => charges * 0.1015, Classification.GeneralUse => charges * 0.0808, _ => charges * 0.1164, }; } double AddTriple(in (double a, double b, double c) triple) => triple.a + triple.b + triple.c; double Add3(in double a, in double b, in double c) => a + b + c; double Add4(in double a, in double b, in double c, in double d) => a + b + c + d; double CalculateLocalTaxes(in double charges, in Classification @class) { return @class switch { Classification.Residential => charges * 0.115, Classification.NonProfit => charges * 0.085, Classification.WaterIntensive => charges * 0.825, Classification.GeneralUse => charges * 0.315, _ => charges * 0.625, }; } double CalculateStateTaxes(in double charges, in Classification @class) { return @class switch { Classification.Residential => charges * 0.035, Classification.NonProfit => charges * 0.015, Classification.WaterIntensive => charges * 0.105, Classification.GeneralUse => charges * 0.065, _ => charges * 0.815, }; } string GetCustomerType() { WriteLine("Stellar Water Point"); WriteLine("==========================================================="); WriteLine("To prepare an invoice, enter the following information"); WriteLine("Types of Accounts"); WriteLine("1. Residential Household"); WriteLine("2. Social/Non-Profit Organization"); WriteLine("3. Water Intensive Business (Laudromat, etc)"); WriteLine("4. General Business"); WriteLine("0. Other"); Write("Enter Type of Account: "); string answer = ReadLine()!; WriteLine("-----------------------------------------------------------"); return answer; } int ReadCounter(out int start, out int end) { end = 0; start = 0; try { Write("Counter Reading Start: "); start = int.Parse(ReadLine()!); } catch(Exception ex) when (ex is FormatException fex) { WriteLine("The counter reading start value you entered is not valid."); WriteLine("The error produced is: " + fex.Message); } try { Write("Counter Reading End: "); end = int.Parse(ReadLine()!); } catch (Exception ex) when (ex is FormatException fex) { WriteLine("The counter reading end value you entered is not valid."); WriteLine("The error produced is: " + fex.Message); } return 1_000; } int readingEnd; int readingStart; string acntType = GetCustomerType(); _ = ReadCounter(out readingStart, out readingEnd); var account = IdentifyAccount(acntType); double gallons = Subtract(readingEnd, readingStart); double HCFValue = CalculateHCFTotal(gallons); (double First25Therms, double Next15Therms, double Above40Therms) therms = EvaluateTherms(HCFValue); double water = AddTriple(therms); double sewer = EvaluateSewerCharges(water, account.iClass); double environmentCharges = EvaluateEnvironmentCharges(water, account.iClass); double serviceCharges = EvaluateServiceCharges(water, account.iClass); double totalCharges = Add4(water, sewer, environmentCharges, serviceCharges); double localTaxes = CalculateLocalTaxes(totalCharges, account.iClass); double stateTaxes = CalculateStateTaxes(totalCharges, account.iClass); double amountDue = Add3(totalCharges, localTaxes, stateTaxes); WriteLine("==========================================================="); WriteLine("Stellar Water Point - Customer Invoice"); WriteLine("Account Type: {0}", account.name); WriteLine("==========================================================="); WriteLine("Meter Reading"); WriteLine("-----------------------------------------------------------"); WriteLine("Counter Reading Start: {0,10}", readingStart); WriteLine("Counter Reading End: {0,10}", readingEnd); WriteLine("Total Gallons Consumed: {0,10}", gallons); WriteLine("==========================================================="); WriteLine("Therms Evaluation"); WriteLine("-----------------------------------------------------------"); WriteLine("HCF Total: {0,10:n}", HCFValue); WriteLine("First 35 Therms (* 1.5573): {0,10:n}", therms.First25Therms); WriteLine("First 20 Therms (* 1.2264): {0,10:n}", therms.Next15Therms); WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", therms.Above40Therms); WriteLine("-----------------------------------------------------------"); WriteLine("Water Usage Charges: {0,10:n}", water); WriteLine("Sewer Charges: {0,10:n}", sewer); WriteLine("==========================================================="); WriteLine("Bill Values"); WriteLine("-----------------------------------------------------------"); WriteLine("Environment Charges: {0,10:n}", environmentCharges); WriteLine("Service Charges: {0,10:n}", serviceCharges); WriteLine("Total Charges: {0,10:n}", totalCharges); WriteLine("Local Taxes: {0,10:n}", localTaxes); WriteLine("State Taxes: {0,10:n}", stateTaxes); WriteLine("-----------------------------------------------------------"); WriteLine("Amount Due: {0,10:n}", amountDue); WriteLine("==========================================================="); internal enum Classification { Residential, NonProfit, WaterIntensive, GeneralUse, Other }
Stellar Water Point =========================================================== To prepare an invoice, enter the following information Types of Accounts 1. Residential Household 2. Social/Non-Profit Organization 3. Water Intensive Business (Laudromat, etc) 4. General Business 0. Other Enter Type of Account: 4 ----------------------------------------------------------- Counter Reading Start: 55755 Counter Reading End: 253579 =========================================================== Stellar Water Point - Customer Invoice Account Type: General Business, Government =========================================================== Meter Reading ----------------------------------------------------------- Counter Reading Start: 55755 Counter Reading End: 253579 Total Gallons Consumed: 197824 =========================================================== Therms Evaluation ----------------------------------------------------------- HCF Total: 264.47 First 35 Therms (* 1.5573): 38.93 First 20 Therms (* 1.2264): 18.40 Above 40 Therms (* 0.9624): 216.03 ----------------------------------------------------------- Water Usage Charges: 273.36 Sewer Charges: 175.09 =========================================================== Bill Values ----------------------------------------------------------- Environment Charges: 1.23 Service Charges: 22.09 Total Charges: 471.76 Local Taxes: 148.61 State Taxes: 30.66 ----------------------------------------------------------- Amount Due: 651.03 =========================================================== Press any key to close this window . . .
Simplifying a Switch Expression
In our introduction to functions, we saw that, when a function returns a value, you can simplify its body by using the => operator. This functionality is also available for a switch expression. To apply it, after the parentheses of the function, remove the curly brackets of the function, replace the return keyword by the => operator, create the whole switch section, and end the whole statement with a semicolon. Here i5 an example:
using static System.Console;
int GetPayrollFrequency()
{
int time;
WriteLine("Enter the information to evaluate taxes");
WriteLine("Frequency by which the payroll is processed:");
WriteLine("1 - Weekly");
WriteLine("2 - Biweekly");
WriteLine("3 - Semimonthly");
WriteLine("4 - Monthly");
Write("Enter Payroll Frequency: ");
time = int.Parse(ReadLine());
return time;
}
double GetGrossSalary()
{
double sal;
Write("Gross Salary: ");
sal = double.Parse(ReadLine());
return sal;
}
double EvaluateIncomeTax(int frequency, double sal) => frequency switch
{
1 => 271.08 + (sal * 24 / 100),
2 => 541.82 + (sal * 24 / 100),
3 => 587.12 + (sal * 24 / 100),
4 => 1174.12 + (sal * 24 / 100),
_ => 0.00
};
WriteLine("Payroll Evaluation");
WriteLine("============================================");
int period = GetPayrollFrequency();
WriteLine("--------------------------------------------");
double gs = GetGrossSalary();
double incomeTax = EvaluateIncomeTax(period, gs);
WriteLine("============================================");
WriteLine("Payroll Evaluation");
WriteLine("--------------------------------------------");
WriteLine("Gross Salary:{0,20:n}", gs);
WriteLine("Income Tax: {0,20:n}", incomeTax);
WriteLine("============================================");
Here is an example of running the program:
Payroll Evaluation ============================================ Enter the information to evaluate taxes Frequency by which the payroll is processed: 1 - Weekly 2 - Biweekly 3 - Semimonthly 4 - Monthly Enter Payroll Frequency: 2 -------------------------------------------- Gross Salary: 3317.93 ============================================ Payroll Evaluation -------------------------------------------- Gross Salary: 3,317.93 Income Tax: 1,338.12 ============================================ Press any key to close this window . . .
Practical Learning: Simplifying a Switch Expression
using static System.Console; . . . double EvaluateSewerCharges(in double charges, in Classification @class) => @class switch { Classification.Residential => charges * 0.2528, Classification.NonProfit => charges * 0.0915, Classification.WaterIntensive => charges * 1.5865, Classification.GeneralUse => charges * 0.6405, _ => charges * 1.2125, }; double EvaluateEnvironmentCharges(in double charges, in Classification @class) => @class switch { Classification.Residential => charges * 0.0025, Classification.NonProfit => charges * 0.0015, Classification.WaterIntensive => charges * 0.0105, Classification.GeneralUse => charges * 0.0045, _ => charges * 0.0125, }; double EvaluateServiceCharges(in double charges, in Classification @class) => @class switch { Classification.Residential => charges * 0.0328, Classification.NonProfit => charges * 0.0115, Classification.WaterIntensive => charges * 0.1015, Classification.GeneralUse => charges * 0.0808, _ => charges * 0.1164, }; . . . double CalculateLocalTaxes(in double charges, in Classification @class) => @class switch { Classification.Residential => charges * 0.115, Classification.NonProfit => charges * 0.085, Classification.WaterIntensive => charges * 0.825, Classification.GeneralUse => charges * 0.315, _ => charges * 0.625 }; double CalculateStateTaxes(in double charges, in Classification @class) => @class switch { Classification.Residential => charges * 0.035, Classification.NonProfit => charges * 0.015, Classification.WaterIntensive => charges * 0.105, Classification.GeneralUse => charges * 0.065, _ => charges * 0.815, }; . . .
Stellar Water Point =========================================================== To prepare an invoice, enter the following information Types of Accounts 1. Residential Household 2. Social/Non-Profit Organization 3. Water Intensive Business (Laudromat, etc) 4. General Business 0. Other Enter Type of Account: 5 ----------------------------------------------------------- Counter Reading Start: 104 Counter Reading End: 285474 =========================================================== Stellar Water Point - Customer Invoice Account Type: Other =========================================================== Meter Reading ----------------------------------------------------------- Counter Reading Start: 104 Counter Reading End: 285474 Total Gallons Consumed: 285370 =========================================================== Therms Evaluation ----------------------------------------------------------- HCF Total: 381.51 First 35 Therms (* 1.5573): 38.93 First 20 Therms (* 1.2264): 18.40 Above 40 Therms (* 0.9624): 328.67 ----------------------------------------------------------- Water Usage Charges: 386.00 Sewer Charges: 468.02 =========================================================== Bill Values ----------------------------------------------------------- Environment Charges: 4.82 Service Charges: 44.93 Total Charges: 903.78 Local Taxes: 564.86 State Taxes: 736.58 ----------------------------------------------------------- Amount Due: 2,205.21 =========================================================== Press any key to close this window . . .
|
|||
Previous | Copyright © 2001-2024, FunctionX | Thursday 07 December 2024, 08:06 | Next |
|