Nullity Fundamental Operators

Introduction

We already know that, before using a variable you have declared or are declaring, you must initialize that variable. Here is an example:

using static System.Console;

int number = 100;

WriteLine("Number: {0}", number);

In this case, you know for sure that your variable has a value, because you made sure to assign a value to the variable before using it.

Practical LearningPractical Learning: Introducing Nullity

  1. Start Microsoft Visual Studio
  2. Create a C# Console App that supports the .NET 8.0 (Long-Term Support). Name the project as Chemistry7
  3. To create a new folder, in the Solution Explorer, right-click Chemistry7 -> Add -> New Folder
  4. Type Models as the name of the folder
  5. On the main menu, click Models -> Add -> Class...
  6. Change the file name to Element
  7. Click Add
  8. Change the document and class as follows:
    namespace Chemistry7.Models
    {
        public enum Phase { Gas, Liquid, Solid, Unknown }
    
        internal class Element(int number, string symbol, string name, double mass, Phase phase)
        {
            internal string ElementName
            {
                get { return name;   }
            }
            
            internal string Symbol
            {
                get { return symbol; }
            }
            
            internal int AtomicNumber
            {
                get { return number; }
            }
            
            internal double AtomicWeight
            {
                get { return mass;   }
            }
            
            internal Phase Phase
            {
                get { return phase;  }
    
            }
        }
    }
  9. In the Solution Explorer, right-click Program.cs and click Rename
  10. Type PeriodicTable (to get PeriodicTable.cs) and press enter
  11. Click the PeriodicTable.cs tab and change the document as follows:
    using Chemistry6.Models;
    using static System.Console;
    
    void Present(Element obj)
    {
        if (obj != null)
        {
            WriteLine("Chemistry");
            WriteLine("--------------------------");
            WriteLine("Symbol:        " + obj.Symbol);
            WriteLine($"Atomic Number: {obj.AtomicNumber}");
            WriteLine("Element Name:  " + obj.ElementName);
            WriteLine($"Atomic Weight: " + obj.AtomicWeight);
            WriteLine("Phase/State:   " + obj.Phase);
            WriteLine("==========================");
        }
    }
    
    Element Prepare()
    {
        Element h  = new Element(1, "H", "Hydrogen", 1.008, Phase.Gas);
        Element he = new Element(2, "He", "Helium", 4.002602, Phase.Gas);
        Element li = new Element(3, "Li", "Lithium", 6.94, Phase.Solid);
        Element be = new Element(4, "Be", "Beryllium", 9.0121831, Phase.Solid);
        Element b  = new Element(5, "B", "Boron", 10.81, Phase.Solid);
        Element c  = new(name: "Carbon", mass: 12.011, symbol: "C", number: 6, phase: Phase.Solid);
        Element n  = new(7, "N", "Nitrogen", 14.007, Phase.Gas);
        Element o  = new(8, "O", "Oxygen", 15.999, Phase.Gas);
        Element f  = new(9, "F", "Fluorine", 18.998, Phase.Gas);
        Element ne = new(10, "Ne", "Neon", 20.180, Phase.Gas);
        Element na = new(11, "Na", "Sodium", 22.990, Phase.Solid);
        Element mg = new(12, "Mg", "Magnesium", 24.305, Phase.Solid);
        Element al = new(phase : Phase.Solid, symbol : "Al", number : 13, name : "Aluminium", mass : 26.982);
        Element si = new(14, "Si", "Silicon", 28.085, Phase.Solid);
        Element p  = new(symbol : "P", mass : 30.974, number : 15, name : "Phosphorus", phase : Phase.Solid);
        Element s  = new(16, "S", "Sulfur", 32.06, Phase.Solid);
        Element cl = new(17, "Cl", "Chlorine", 35.45, Phase.Gas);
        Element ar = new Element(18, "Ar", "Argon", 39.948, Phase.Gas);
        Element k  = new Element(phase: Phase.Solid, number: 19, symbol: "K", name: "Potassium", mass: 39.098);
        Element ca = new(20, "Ca", "Calcium", 40.078, Phase.Solid);
        Element sc = new(number: 21, symbol: "Sc", phase: Phase.Solid, name: "Scandium", mass: 44.956);
        Element ti = new(name: "Titanium", number: 22, symbol: "Ti", mass: 47.867, phase: Phase.Solid);
        Element v  = new Element(number : 23, symbol : "V", phase : Phase.Solid, mass : 50.942, name : "Vanadium");
        Element cr = new(number: 24, symbol: "Cr", phase: Phase.Solid, name: "Chromium", mass: 51.996);
        Element mn = new Element(name: "Manganese", number: 25, symbol: "Mn", mass: 54.938, phase: Phase.Solid);
        Element fe = new Element(mass: 55.845, symbol: "Fe", name: "Iron", number: 26, phase: Phase.Solid);
        Element co = new Element(symbol: "Co", number: 27, name: "Cobalt", mass: 58.933, phase: Phase.Solid);
        Element ni = new Element(number: 28, symbol: "Ni", mass: 58.6934, phase: Phase.Solid, name: "Nickel");
        Element cu = new Element(phase: Phase.Solid, number: 29, name: "Copper", symbol: "Cu", mass: 63.546);
        Element zn = new Element(30, "Zn", "Zinc", 65.38, Phase.Solid);
    
        return zn;
    }
    
    Element? elm = null;
    
    WriteLine("Presenting a chemical element...");
    Present(elm);
    
    elm = Prepare();
    
    WriteLine("Presenting a chemical element...");
    Present(elm);
  12. Notice a warning on the "Present(elm);" line.
    To execute, on the main menu, click Debug -> Start Without Debugging:
    Presenting a chemical element...
    Presenting a chemical element...
    Chemistry
    --------------------------
    Symbol:        Zn
    Atomic Number: 30
    Element Name:  Zinc
    Atomic Weight: 65.38
    Phase/State:   Solid
    ==========================
    
    Press any key to close this window . . .
  13. Return to your programming environment

The Null-Forgiving Operator

In some cases, the value of a variable may come by other means, such as from user (who may receive a request or prompt to type a value) or from a stream (computer memory, network, internet, etc). When that's the case, the compiler may issue a warning just to ask you whether you know for sure that the value of the variable or object will be provided. In most cases, this is a soft warning that will not produce an error but should be taken care of. In many cases, you can ignore that warning. The rule of thumb is that you should not ignore warnings, or at least you should always know the reason for a warning and probably take care of it.

The null-forgiving operator allows you to indicate to the compiler that you are aware that the variable or object may be holding a null value and you know what you are doing. The null-forgiving operator is !. To apply it, simply type it on the right side of the object or value. Here is an example:

using static System.Console;

int number = 100!;

WriteLine("Number: {0}", number);

Normally, the null-forgiving operator has no effect on a variable of primitive type when the variable is initialized immediately after you have declared it. If the value is supposed to be provided to the object or variable other than when initializing the object or variable, then the compiler would issue a warning. That's when you should apply the null-forgiving operator.

Practical LearningPractical Learning: Comparing an Object for Non-Nullity

  1. Change the PeriodicTable.cs document as follows:
    using static System.Console;
    using Chemistry7.Models;
    
    void Present(Element obj)
    {
        if (obj != null)
        {
            WriteLine("Chemistry");
            WriteLine("--------------------------");
            WriteLine("Symbol:        " + obj.Symbol);
            WriteLine($"Atomic Number: {obj.AtomicNumber}");
            WriteLine("Element Name:  " + obj.ElementName);
            WriteLine($"Atomic Weight: " + obj.AtomicWeight);
            WriteLine("Phase/State:   " + obj.Phase);
            WriteLine("==========================");
        }
    }
    
    Element Prepare()
    {
        Element h  = new Element(1, "H", "Hydrogen", 1.008, Phase.Gas);
        Element he = new Element(2, "He", "Helium", 4.002602, Phase.Gas);
        Element li = new Element(3, "Li", "Lithium", 6.94, Phase.Solid);
        Element be = new Element(4, "Be", "Beryllium", 9.0121831, Phase.Solid);
        Element b  = new Element(5, "B", "Boron", 10.81, Phase.Solid);
        Element c  = new(name: "Carbon", mass: 12.011, symbol: "C", number: 6, phase: Phase.Solid);
        Element n  = new(7, "N", "Nitrogen", 14.007, Phase.Gas);
        Element o  = new(8, "O", "Oxygen", 15.999, Phase.Gas);
        Element f  = new(9, "F", "Fluorine", 18.998, Phase.Gas);
        Element ne = new(10, "Ne", "Neon", 20.180, Phase.Gas);
        Element na = new(11, "Na", "Sodium", 22.990, Phase.Solid);
        Element mg = new(12, "Mg", "Magnesium", 24.305, Phase.Solid);
        Element al = new(phase : Phase.Solid, symbol : "Al", number : 13, name : "Aluminium", mass : 26.982);
        Element si = new(14, "Si", "Silicon", 28.085, Phase.Solid);
        Element p  = new(symbol : "P", mass : 30.974, number : 15, name : "Phosphorus", phase : Phase.Solid);
        Element s  = new(16, "S", "Sulfur", 32.06, Phase.Solid);
        Element cl = new(17, "Cl", "Chlorine", 35.45, Phase.Gas);
        Element ar = new Element(18, "Ar", "Argon", 39.948, Phase.Gas);
        Element k  = new Element(phase: Phase.Solid, number: 19, symbol: "K", name: "Potassium", mass: 39.098);
        Element ca = new(20, "Ca", "Calcium", 40.078, Phase.Solid);
        Element sc = new(number: 21, symbol: "Sc", phase: Phase.Solid, name: "Scandium", mass: 44.956);
        Element ti = new(name: "Titanium", number: 22, symbol: "Ti", mass: 47.867, phase: Phase.Solid);
        Element v  = new Element(number : 23, symbol : "V", phase : Phase.Solid, mass : 50.942, name : "Vanadium");
        Element cr = new(number: 24, symbol: "Cr", phase: Phase.Solid, name: "Chromium", mass: 51.996);
        Element mn = new Element(name: "Manganese", number: 25, symbol: "Mn", mass: 54.938, phase: Phase.Solid);
        Element fe = new Element(mass: 55.845, symbol: "Fe", name: "Iron", number: 26, phase: Phase.Solid);
        Element co = new Element(symbol: "Co", number: 27, name: "Cobalt", mass: 58.933, phase: Phase.Solid);
        Element ni = new Element(number: 28, symbol: "Ni", mass: 58.6934, phase: Phase.Solid, name: "Nickel");
        Element cu = new Element(phase: Phase.Solid, number: 29, name: "Copper", symbol: "Cu", mass: 63.546);
        Element zn = new Element(30, "Zn", "Zinc", 65.38, Phase.Solid);
        Element ga = new Element(31, "Ga", "Gallium", 69.723, Phase.Solid);
    
        return ga;
    }
    
    Element? elm = null;
    
    WriteLine("Presenting a chemical element...");
    Present(elm!);
    
    elm = Prepare();
    
    WriteLine("Presenting a chemical element...");
    Present(elm);
  2. Notice that the warning on the "Present(elm);" line has appeared.
    To execute, on the main menu, click Debug -> Start Without Debugging:
    Presenting a chemical element...
    Presenting a chemical element...
    Chemistry
    --------------------------
    Symbol:        Ga
    Atomic Number: 31
    Element Name:  Gallium
    Atomic Weight: 69.723
    Phase/State:   Solid
    ==========================
    
    Press any key to close this window . . .
  3. Press Enter to close the window and return to your programming environment
  4. Create a new Console App that supports .NET 8.0 (Long-Term support) and named StellarWaterPoint2
  5. Change the Program.cs document as follows:
    using static System.Console;
    
    WriteLine("Stellar Water Point");
    WriteLine("======================================================");
    WriteLine("To prepare an invoice, enter the following information");
    WriteLine("Types of Accounts");
    WriteLine("1. Residential Household");
    WriteLine("2. Laudromat");
    WriteLine("3. Health Clinic");
    WriteLine("4. Place of Worship/Youth Center");
    WriteLine("5. Government Agency");
    WriteLine("6. Other Business");
    Write("Enter Type of Account:         ");
    string answer = ReadLine();
    WriteLine("------------------------------------------------------");
    
    Write("Counter Reading Start:         ");
    double counterReadingStart = double.Parse(ReadLine()!);
    Write("Counter Reading End:           ");
    double counterReadingEnd = double.Parse(ReadLine()!);
    
    double first25Therms, next15Therms, above40Therms;
    
    double gallons = counterReadingEnd - counterReadingStart;
    double HCFTotal = gallons / 748;
    
    if (HCFTotal is > 40.00)
    {
        first25Therms = 25.00 * 1.5573;
        next15Therms = 15.00 * 1.2264;
        above40Therms = (HCFTotal - 40.00) * 0.9624;
    }
    else if (HCFTotal is > 25.00)
    {
        first25Therms = 25.00 * 1.5573;
        next15Therms = (HCFTotal - 25.00) * 1.2264;
        above40Therms = 0.00;
    }
    else // if (HCFTotal <= 25.00)
    {
        first25Therms = HCFTotal * 1.5573;
        next15Therms = 0.00;
        above40Therms = 0.00;
    }
    
    double waterUsageCharges = first25Therms + next15Therms + above40Therms;
    double sewerCharges = waterUsageCharges * 0.252;
    
    double environmentCharges = (answer == "1") ? (waterUsageCharges * 0.0025) : (waterUsageCharges * 0.0125);
    
    double serviceCharges = (answer == "1") ? (waterUsageCharges * 0.0328)
                                                        : (waterUsageCharges * 0.11643);
    
    double totalCharges = waterUsageCharges + sewerCharges + environmentCharges + serviceCharges;
    
    double localTaxes = (answer == "1") ? (totalCharges * 0.035) : (totalCharges * 0.235);
    double stateTaxes = (answer == "1") ? (totalCharges * 0.115) : (totalCharges * 0.815);
    
    double amountDue = totalCharges + localTaxes + stateTaxes;
    
    WriteLine("======================================================");
    WriteLine("Stellar Water Point - Customer Invoice");
    WriteLine("======================================================");
    WriteLine("Meter Reading");
    WriteLine("------------------------------------------------------");
    WriteLine("Counter Reading Start:      {0,10}", counterReadingStart);
    WriteLine("Counter Reading End:        {0,10}", counterReadingEnd);
    WriteLine("Total Gallons Consumed:     {0,10}", gallons);
    WriteLine("======================================================");
    WriteLine("Therms Evaluation");
    WriteLine("------------------------------------------------------");
    WriteLine("HCF Total:                  {0,10:n}", HCFTotal);
    WriteLine("First 35 Therms (* 1.5573): {0,10:n}", first25Therms);
    WriteLine("First 20 Therms (* 1.2264): {0,10:n}", next15Therms);
    WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", above40Therms);
    WriteLine("------------------------------------------------------");
    WriteLine("Water Usage Charges:        {0,10:n}", waterUsageCharges);
    WriteLine("Sewer Charges:              {0,10:n}", sewerCharges);
    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);
    Write("======================================================");
  6. To execute, on the main menu, click Debug -> Start Without Debugging
  7. When requested, for the type of account, type 2 and press Enter
  8. For the Counter Reading Start, type 2205 and press Enter
  9. For the Counter Reading End, type 72877 and press Enter
    Stellar Water Point
    ======================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. Laudromat
    3. Health Clinic
    4. Place of Worship/Youth Center
    5. Government Agency
    6. Other Business
    Enter Type of Account:         2
    ------------------------------------------------------
    Counter Reading Start:         2205
    Counter Reading End:           72877
    ======================================================
    Stellar Water Point - Customer Invoice
    ======================================================
    Meter Reading
    ------------------------------------------------------
    Counter Reading Start:            2205
    Counter Reading End:             72877
    Total Gallons Consumed:          70672
    ======================================================
    Therms Evaluation
    ------------------------------------------------------
    HCF Total:                       94.48
    First 35 Therms (* 1.5573):      38.93
    First 20 Therms (* 1.2264):      18.40
    Above 40 Therms (* 0.9624):      52.43
    ------------------------------------------------------
    Water Usage Charges:            109.76
    Sewer Charges:                   27.66
    ======================================================
    Bill Values
    ------------------------------------------------------
    Environment Charges:              1.37
    Service Charges:                 12.78
    Total Charges:                  151.57
    Local Taxes:                     35.62
    State Taxes:                    123.53
    ------------------------------------------------------
    Amount Due:                     310.72
    ======================================================
    Press any key to close this window . . .
  10. Return to your programming environment
  11. To open a previous project, on the main menu of Microsoft Visual Studio, click File -> Recent Projects and Solutions -> ...\Chemistry7

The Null-Conditional Operator

Consider a class used to describe some houses:

public class House
{
    public int Bedrooms;

    public House()
    {
        Bedrooms = 1;
    }
}

We already saw how to find out whether an object of such a class is currently valid. Here is an example:

int beds = -1;
House habitat = null;
Description desc = new Description();

if (habitat == null)
{
    beds = 0;
}
else
{
    beds = habitat.Bedrooms;
}

This would produce:

Make and Model:    Farberware FMO12AHTBSG
Capacity:          1.2
Microwave Options: 9
==========================================

Press any key to close this window . . .

In this case, we are trying to access a member of the class (getting the value of a property) only if the object is null. To simplify this operation, you can use the null-conditional operator represented as ?. (the question mark immediately followed by a period). To apply it, use it to access the member of a class. Here is an example:

int ? beds = 0;
House habitat = null;
Description desc = new Description();

beds = habitat?.Bedrooms;

Here is another version of the code:

int ?beds = -1;
Description desc = new Description();
House habitat = new House();
habitat.Bedrooms = 5;

beds = habitat?.Bedrooms;

The ?. operator checks whether the variable on its left is currently holding a null value. If it is, nothing happens. If it doesn't, then the member on its right is accessed and the operation that must be performed proceeds.

The Null Coalescing Operator

The ?. operator is used to simply check the nullity of a variable. You must apply the subsequent statement separately. To combine the ?. operation and its subsequent statement, you use the null coalescent operator represented as ?? (Two question marks). To apply it, type it after the condition followed by the desired statement. Here is an example:

int ?beds = -1;
Description desc = new Description();
House habitat = null;

beds = habitat?.Bedrooms ?? 0;

This code states that if the habitat object is null, the beds variable should be assigned 0. If the habitat object is not null, then the beds variable should receive a value from the designated property of the House class:

int ?beds = -1;
Description desc = new Description();
House habitat = new House();
habitat.Bedrooms = 5;

beds = habitat?.Bedrooms ?? 0;

Value and Object Casting

Introduction

An application typically deals with various values. Value compatibility is the ability of a certain value or a value of one type to be used as a value of another type. In many cases, this is not possible. In some other cases, you can convert one value into another. This conversion is referred to as casting. There are various ways you can cast a value, depending on its type.

Primitive Values and Memory Use

So far, we have use two data types for numeric variables. The types were int and double. A variable of type int can hold a value between -2,147,483,648 and 2,147,484,647. A variable of type double can hold a value between 1.79769313486232e308 and 1.79769313486232e308. This situation is based on memory usage. This means that the value held by an int variable can fit in the memory used by a double variable. As a result, you can assign an int variable to a double variable. Here is an example:

using static System.Console;

int natural = 938_482;
double real;

real = natural;

WriteLine("=========================");
WriteLine("Numbers");
WriteLine("-------------------------");
WriteLine("Natural: {0}", natural);
WriteLine("Decimal: {0}", real);

This would produce:

=========================
Numbers
-------------------------
Natural: 938482
Decimal: 938482

Press any key to close this window . . .

The amount of memory used to store the value of a double variable is larger than the amount of memory used to store the value of an int variable. As a result, you cannot assign a double variable to an int variable. If you do, you would receive an error. Consider the following an example:

using static System.Console;

int natural = 938_482;
double dec = 824.66;
double real;

real = natural;

WriteLine("=========================");
WriteLine("Numbers");
WriteLine("-------------------------");
WriteLine("Natural: {0}", natural);
WriteLine("Decimal: {0}", real);
WriteLine("Decimal: {0}", dec);

natural = dec;

WriteLine("Number: {0}", natural);
WriteLine("=========================");

This would produce:

Severity	Code	Description	Project	File	Line	Suppression State
Error	CS0266	Cannot implicitly convert type 'double' to 'int'...

Casting a Value

Converting one value into another is referred to as casting. To make this possible, the value held by one variable must be able to fit in the memory area used by another variable. As we saw in the section before the previous, you can use the assignment operator. As an alternative, you precede the value with parentheses. In the parentheses, enter the data type of the holding variable. This can be done as follows:

using static System.Console;

int natural = 938_482;
double real;

real = (double)natural;

WriteLine("=========================");
WriteLine("Numbers");
WriteLine("-------------------------");
WriteLine("Natural: {0}", natural);
WriteLine("Decimal: {0}", real);

In the section before the last, we saw that, by defualt, the memory of a double variable is too large for the memory of an int variable and, by default, if you assign a double variable to an int variable, the compiler would produce an error. Sometimes, as a programmer of your own application, or an application with which you are familiar, you know with certainty that the value held by a double variable can fit in the memory of an int variable, but the compiler doesn't know. Therefore, to let the compiler know that you know that the value of one variable can fit in the memory of another certain variable, precede the value with parentheses and, in the parentheses, enter the data type of the holding variable. Consider a previous program that produced an error:

using static System.Console;

int natural = 938_482;
double dec = 824.66;
double real;

real = (int)natural;

WriteLine("=========================");
WriteLine("Numbers");
WriteLine("-------------------------");
WriteLine("Natural: {0}", natural);
WriteLine("Decimal: {0}", real);
WriteLine("Decimal: {0}", dec);

natural = (int)dec;

WriteLine("Number:  {0}", natural);
WriteLine("=========================");

This would produce:

=========================
Numbers
-------------------------
Natural: 938482
Decimal: 938482
Decimal: 824.66
Number:  824
=========================

Press any key to close this window . . .

Conversion of an Object AS Another

One of the characteristics of object-oriented programming is that, when a child class is based on a parent class, the parent class tends to be compatible with its child(ren) class(es). This is because the parent class usually has the same number, types, and names of members as the child(ren) class(es). In many or most cases, the child(ren) class(es) has (have) more members than the parent class (or the parent class has fewer members than the child(ren) class(es).

Imagine that, at one time, you have a variable or object of a parent class but for some reason, you need to treat that object as if it were an object of the child class. The first operation you must perform is to let the compiler know that you want your variable to be mapped to the type of the parent class. To perform this operation, you use an operator named as. The basic formula to use it is:

start-expression as end-expression

The start-expression can be an object, in which case you can use that section to declare a variable. Initialize that variable with the object or value to be converted. The start-expression is followed by the as keyword. The end-expression is the type to which to convert. In most cases, it should be the same type used in the start-expression. After the conversion, you can use the object or the declared variable as if it were the type of the start-expression. Here is an example:

using static System.Console;

void Present(Appliance apl)
{
    CofeeMaker? cm = apl as CofeeMaker;

    WriteLine("Barrington World");
    WriteLine("------------------------------------------");
    WriteLine("Make and Model: " + cm?.Make + " " + cm?.Model);
    WriteLine("Capacity:       {0}", cm?.Capacity);
    WriteLine("Serving Size:   {0}", cm?.ServingSize);
    WriteLine("Filter Type:    {0}", cm?.FilterType);
    WriteLine("==========================================");
    
}

CofeeMaker? machine;

machine = new CofeeMaker()
{
    Make        = "GE",
    Capacity    = "12 Cups",
    Model       = "JES1095SMSS",
    ServingSize = "4-12 Cups (20-60oz)",
    FilterType  = "12-Cup Basket Style"
};

Present(machine);

public class Appliance
{
    public required string Make  { get; set; }
    public required string Model { get; set; }
}

public class CofeeMaker : Appliance
{
    public required string Capacity { get; set; }
    public required string ServingSize { get; set; }
    public required string FilterType { get; set; }
}

This would produce:

Barrington World
------------------------------------------
Make and Model: GE JES1095SMSS
Capacity:       12 Cups
Serving Size:   4-12 Cups (20-60oz)
Filter Type:    12-Cup Basket Style
==========================================

Press any key to close this window . . .

One of the most convenient ways to deal with objects compatibility is to use the object (or Object) class as your common type because it is the ultimate parent of all classes. You can then convert it to any object of your choice using the as operator. Here is an example:

using static System.Console;

void Present(object obj)
{
    Microwave? mw = obj as Microwave;

    WriteLine("Barrington World");
    WriteLine("------------------------------------------");
    WriteLine("Make and Model:    " + mw?.Make + " " + mw?.Model);
    WriteLine("Capacity:          {0} Cubic Feet", mw?.Capacity);

    if(mw?.MenuOptions == 0)
        WriteLine("Microwave Options: N/A");
    else
        WriteLine("Microwave Options: {0}", mw?.MenuOptions);
    WriteLine("Wattage:           {0} Watts", mw?.Wattage);
    WriteLine("==========================================");

}

Microwave? machine;

machine = new Microwave()
{
    MenuOptions = 0,
    Capacity = 0.9,
    Wattage = 900,
    Make = "GE",
    Model = "JES1095SMSS"
};

Present(machine);

public class Microwave
{
    public required string Make { get; set; }
    public required string Model { get; set; }
    public required double Capacity { get; set; }
    public required int MenuOptions { get; set; }
    public required int Wattage { get; set; }
}

This would produce:

Barrington World
------------------------------------------
Make and Model:    GE JES1095SMSS
Capacity:          0.9 Cubic Feet
Microwave Options: N/A
Wattage:           900 Watts
==========================================

Press any key to close this window . . .

The Nullity of Members of a Class

The Nullity of a Field

We already know that when you are creating a class, if you judge it necessary, you can create one or more fields in the body of the class. This can be done as follows:

using static System.Console;

Letter let = new Letter();

let.Present();

WriteLine("======================");

public class Letter
{
    int pos;
    string name;

    public Letter()
    {
        pos  = 1;
        name = "a";
    }

    public void Present()
    {
        WriteLine("Letter Characteristics");
        WriteLine("----------------------");
        WriteLine("Name:     {0}", name);
        WriteLine("Position: {0}", pos);
    }
}

This would produce:

Letter Characteristics
----------------------
Name:     a
Position: 1
======================

Press any key to close this window . . .

When you add a field to a class, the compiler would check the type of the field. If the type is nullable, the compiler may (or may not) present a warning. If you want to indicate to the compiler that you anticipate that the value of the field will be null or not in some cases, apply the ? operator to the nullable data type of the field. This can be done as follows:

using static System.Console;

Letter let = new Letter();

let.Present();

WriteLine("======================");

public class Letter
{
    int pos;
    string? name;

    public Letter()
    {
        pos  = 1;
        name = "a";
    }

    public void Present()
    {
        WriteLine("Letter Characteristics");
        WriteLine("----------------------");
        WriteLine("Name:     {0}", name);
        WriteLine("Position: {0}", pos);
    }
}

The Nullity of a Complete Property

Like a field of a class, a property can be a nullable type, such as a string. If you are creating a complete property, you know that the property and its corresponding field must be of the same type. Here is an example:

using static System.Console;

Letter let = new Letter();

let.Position = 2;
let.Name = "b";

let.Present();

WriteLine("======================");

public class Letter
{
    int pos;
    string name;

    public int Position
    {
        get { return pos; }
        set { pos = value; }
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public void Present()
    {
        WriteLine("Letter Characteristics");
        WriteLine("----------------------");
        WriteLine("Name:     {0}", Name);
        WriteLine("Position: {0}", Position);
    }
}

This would produce:

Letter Characteristics
----------------------
Name:     b
Position: 2
======================

Press any key to close this window . . .

When you create a property of a nullable type, the compiler wants you to know that the value(s) of the property could be null when the program eventually runs, and the compiler would issue a warning. To eliminate the warning, you have many solutions or options. One solution is to immediately initialize the property. If you are creating a complete property, you can initialize it corresponding field. This can be done as follows:

using static System.Console;

Letter let = new Letter();

let.Present();

WriteLine("======================");

public class Letter
{
    int pos = 3;
    string name = "c";

    public int Position
    {
        get { return pos; }
        set { pos = value; }
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public void Present()
    {
        WriteLine("Letter Characteristics");
        WriteLine("----------------------");
        WriteLine("Name:     {0}", Name);
        WriteLine("Position: {0}", Position);
    }
}

This would produce:

Letter Characteristics
----------------------
Name:     c
Position: 3
======================

Press any key to close this window . . .

In the above example, we initialized the field of the property with a constant value. Sometimes, or most of the time, you don't have, don't know, or can't predict the primary constant value. The solution is to apply the null-conditional operator. This can be done as follows:

public class Letter
{
    int pos;
    string? name;

    public int Position
    {
        get { return pos; }
        set { pos = value; }
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

Additionally, you can initialize the field of the property with the null. Either way, if you are creating a complete property, you should also apply the null conditional operator to the property. This can be done as follows:

using static System.Console;

Letter let = new();

let.Name = "d";
let.Position = 4;

let.Present();

WriteLine("======================");

public class Letter
{
    int pos;
    string? name = null;

    public int Position
    {
        get { return pos; }
        set { pos = value; }
    }

    public string? Name
    {
        get { return name; }
        set { name = value; }
    }

    public void Present()
    {
        WriteLine("Letter Characteristics");
        WriteLine("----------------------");
        WriteLine("Name:     {0}", Name);
        WriteLine("Position: {0}", Position);
    }
}

This would produce:

Letter Characteristics
----------------------
Name:     d
Position: 4
======================

Press any key to close this window . . .

The Nullity of an Automatic Property

Normally, if you are creating a simple property, you can make it automatic. Here is an example:

public class House
{
    public string PropertyNumber { get; set; }
    public int Bedrooms { get; set; }
    public float Bathrooms { get; set; }
    public double MarketValue { get; set; }
}

If the type of property is nullable, you should indicate to the compiler that you are aware it. To do this, apply the null conditional operator to the type. This can be done as follows:

public class House
{
    public string? PropertyNumber { get; set; }
    public int Bedrooms { get; set; }
    public float Bathrooms { get; set; }
    public double MarketValue { get; set; }
}

Alternatively, you can initialize the property after its curly bracket. Here is an example:

public class House
{
    public string PropertyNumber { get; set; } = null;
    public int Bedrooms { get; set; }
    public float Bathrooms { get; set; }
    public double MarketValue { get; set; }
}

Practical LearningPractical Learning: Applying the Nullity to Propeties

  1. Click the Element.cs tab and change its class as follows:
    namespace Chemistry7
    {
        public enum Phase { Gas, Liquid, Solid, Unknown }
    
        internal class Element
        {
            internal string ? ElementName { get; set; }
            internal string ? Symbol { get; set; }
            internal int      AtomicNumber { get; set; }
            internal double   AtomicWeight { get; set; }
            internal Phase    Phase { get; set; }
    
            public Element() { }
            public Element(string symbol) => Symbol = symbol;
            public Element(int number) => AtomicNumber = number;
    
            public Element(int number, string symbol, string name, double mass, Phase phase)
            {
                ElementName  = name;
                AtomicWeight = mass;
                Phase        = phase;
                Symbol       = symbol;
                AtomicNumber = number;
            }
        }
    }
  2. To execute the application and make sure there is no error, press Ctrl + F5
  3. Close the window and return to your programming environment
  4. Change the Program.cs document as follows:
    using static System.Console;
    
    WriteLine("Stellar Water Point");
    WriteLine("======================================================");
    WriteLine("To prepare an invoice, enter the following information");
    WriteLine("Types of Accounts");
    WriteLine("1. Residential Household");
    WriteLine("2. Laudromat");
    WriteLine("3. Health Clinic");
    WriteLine("4. Place of Worship/Youth Center");
    WriteLine("5. Government Agency");
    WriteLine("6. Other Business");
    Write("Enter Type of Account:         ");
    string answer = ReadLine()!;
    WriteLine("------------------------------------------------------");
    
    Write("Counter Reading Start:         ");
    double counterReadingStart = double.Parse(ReadLine()!);
    Write("Counter Reading End:           ");
    double counterReadingEnd = double.Parse(ReadLine()!);
    
    double first25Therms, next15Therms, above40Therms;
    
    double gallons = counterReadingEnd - counterReadingStart;
    double HCFTotal = gallons / 748;
    
    if (HCFTotal is > 40.00)
    {
        first25Therms = 25.00 * 1.5573;
        next15Therms = 15.00 * 1.2264;
        above40Therms = (HCFTotal - 40.00) * 0.9624;
    }
    else if (HCFTotal is > 25.00)
    {
        first25Therms = 25.00 * 1.5573;
        next15Therms = (HCFTotal - 25.00) * 1.2264;
        above40Therms = 0.00;
    }
    else // if (HCFTotal <= 25.00)
    {
        first25Therms = HCFTotal * 1.5573;
        next15Therms = 0.00;
        above40Therms = 0.00;
    }
    
    double waterUsageCharges = first25Therms + next15Therms + above40Therms;
    double sewerCharges = waterUsageCharges * 0.252;
    
    double environmentCharges = (answer == "1") ? (waterUsageCharges * 0.0025) : (waterUsageCharges * 0.0125);
    
    double serviceCharges = (answer == "1") ? (waterUsageCharges * 0.0328)
                                                        : (waterUsageCharges * 0.11643);
    
    double totalCharges = waterUsageCharges + sewerCharges + environmentCharges + serviceCharges;
    
    double localTaxes = (answer == "1") ? (totalCharges * 0.035) : (totalCharges * 0.235);
    double stateTaxes = (answer == "1") ? (totalCharges * 0.115) : (totalCharges * 0.815);
    
    double amountDue = totalCharges + localTaxes + stateTaxes;
    
    WriteLine("======================================================");
    WriteLine("Stellar Water Point - Customer Invoice");
    WriteLine("======================================================");
    WriteLine("Meter Reading");
    WriteLine("------------------------------------------------------");
    WriteLine("Counter Reading Start:      {0,10}", counterReadingStart);
    WriteLine("Counter Reading End:        {0,10}", counterReadingEnd);
    WriteLine("Total Gallons Consumed:     {0,10}", gallons);
    WriteLine("======================================================");
    WriteLine("Therms Evaluation");
    WriteLine("------------------------------------------------------");
    WriteLine("HCF Total:                  {0,10:n}", HCFTotal);
    WriteLine("First 35 Therms (* 1.5573): {0,10:n}", first25Therms);
    WriteLine("First 20 Therms (* 1.2264): {0,10:n}", next15Therms);
    WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", above40Therms);
    WriteLine("------------------------------------------------------");
    WriteLine("Water Usage Charges:        {0,10:n}", waterUsageCharges);
    WriteLine("Sewer Charges:              {0,10:n}", sewerCharges);
    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);
    Write("======================================================");
  5. Notice that the warnings have disappeared.
    To execute, on the main menu, click Debug -> Start Without Debugging
  6. When requested, for the type of account, type 2 and press Enter
  7. For the Counter Reading Start, type 2205 and press Enter
  8. For the Counter Reading End, type 72877 and press Enter
    Stellar Water Point
    ======================================================
    To prepare an invoice, enter the following information
    Types of Accounts
    1. Residential Household
    2. Laudromat
    3. Health Clinic
    4. Place of Worship/Youth Center
    5. Government Agency
    6. Other Business
    Enter Type of Account:         2
    ------------------------------------------------------
    Counter Reading Start:         2205
    Counter Reading End:           72877
    ======================================================
    Stellar Water Point - Customer Invoice
    ======================================================
    Meter Reading
    ------------------------------------------------------
    Counter Reading Start:            2205
    Counter Reading End:             72877
    Total Gallons Consumed:          70672
    ======================================================
    Therms Evaluation
    ------------------------------------------------------
    HCF Total:                       94.48
    First 35 Therms (* 1.5573):      38.93
    First 20 Therms (* 1.2264):      18.40
    Above 40 Therms (* 0.9624):      52.43
    ------------------------------------------------------
    Water Usage Charges:            109.76
    Sewer Charges:                   27.66
    ======================================================
    Bill Values
    ------------------------------------------------------
    Environment Charges:              1.37
    Service Charges:                 12.78
    Total Charges:                  151.57
    Local Taxes:                     35.62
    State Taxes:                    123.53
    ------------------------------------------------------
    Amount Due:                     310.72
    ======================================================
    Press any key to close this window . . .
  9. Return to your programming environment
  10. Change the PeriodicTable class as follows:
    using static System.Console;
    using Chemistry7.Models;
    
    void Present(Element obj)
    {
        if (obj is not null)
        {
            WriteLine("Chemistry");
            WriteLine("--------------------------");
            WriteLine("Symbol:        " + obj.Symbol);
            WriteLine($"Atomic Number: {obj.AtomicNumber}");
            WriteLine("Element Name:  " + obj.ElementName);
            WriteLine($"Atomic Weight: " + obj.AtomicWeight);
            WriteLine("Phase/State:   " + obj.Phase);
            Write("==========================");
        }
    }
    
    Element Prepare()
    {
        . . .
        Element mn = new(25, "Mn", "Manganese", 54.938, Phase.Solid);
    
        return mn;
    }
    
    Element elm = Prepare();
    
    Present(elm);
  11. To execute, on the main menu, click Debug -> Start Without Debugging:
    Chemistry
    --------------------------
    Symbol:        Mn
    Atomic Number: 25
    Element Name:  Manganese
    Atomic Weight: 54.938
    Phase/State:   Solid
    ==========================
    Press any key to close this window . . .
  12. Press W to close the window and return to your programming environment
  13. To create a new folder, in the Solution Explorer, right-click StellarWaterPoint2 -> Add -> New Folder
  14. Type Models as the name of the folder
  15. To create a class, in the Solution Explorer, right-click Models -> Add -> Class...
  16. Type WaterBill as the name of the file and class
  17. Click Add
  18. Change the document as follows:
    namespace StellarWaterPoint2.Models
    {
        internal enum Classification { Residential, NonProfit, WaterIntensive, GeneralUse, Other }
    
        internal class WaterBill
        {
            private readonly string typeOfAccount;
    
            public WaterBill(string category) => typeOfAccount = category;
    
            public double CounterReadingStart { get; set; }
            public double CounterReadingEnd   { get; set; }
    
            public (Classification @class, string name) AccountType
            {
                get
                {
                    if (typeOfAccount == "1")
                        return (Classification.Residential, "Residential Household");
                    else if (typeOfAccount == "2")
                        return (Classification.NonProfit, "Social/Non-Profit Organization");
                    else if (typeOfAccount == "3")
                        return (Classification.WaterIntensive, "Water Intensive Business (Laudromat, Hair Salon, etc)");
                    else if (typeOfAccount == "4")
                        return (Classification.GeneralUse, "General Business, Government");
                    else
                        return (Classification.Other, "Other");
                }
            }
    
            public double Gallons => CounterReadingEnd - CounterReadingStart;
    
            public double HCFTotal => Gallons / 748;
    
            public (double First25Therms, double Next15Therms, double Above40Therms) Therms
            {
                get
                {
                    double first, next, above;
    
                    if (HCFTotal is > 40.00)
                    {
                        first = 25.00 * 1.5573;
                        next  = 15.00 * 1.2264;
                        above = (HCFTotal - 40.00) * 0.9624;
                    }
                    else if (HCFTotal > 25.00)
                    {
                        first = 25.00 * 1.5573;
                        next  = (HCFTotal - 25.00) * 1.2264;
                        above = 0.00;
                    }
                    else
                    {
                        first = HCFTotal * 1.5573;
                        next  = 0.00;
                        above = 0.00;
                    }
    
                    return (first, next, above);
                }
            }
    
            public double WaterUsageCharges => Therms.First25Therms + Therms.Next15Therms + Therms.Above40Therms;
    
            public double SewerCharges
            {
                get
                {
                    double result = AccountType.@class switch
                    {
                        Classification.Residential     => WaterUsageCharges * 0.2528,
                        Classification.NonProfit       => WaterUsageCharges * 0.0915,
                        Classification.WaterIntensive  => WaterUsageCharges * 1.5865,
                        Classification.GeneralUse      => WaterUsageCharges * 0.6405,
                        _                              => WaterUsageCharges * 1.2125
                    };
    
                    return result;
                }
            }
    
            public double EnvironmentCharges
            {
                get
                {
                    double result = AccountType.@class switch
                    {
                        Classification.Residential    => WaterUsageCharges * 0.0025,
                        Classification.NonProfit      => WaterUsageCharges * 0.0015,
                        Classification.WaterIntensive => WaterUsageCharges * 0.0105,
                        Classification.GeneralUse     => WaterUsageCharges * 0.0045,
                        _                             => WaterUsageCharges * 0.0125
                    };
    
                    return result;
                }
            }
    
            public double ServiceCharges
            {
                get => AccountType.@class switch
                {
                    Classification.Residential        => WaterUsageCharges * 0.0328,
                    Classification.NonProfit          => WaterUsageCharges * 0.0115,
                    Classification.WaterIntensive     => WaterUsageCharges * 0.1015,
                    Classification.GeneralUse         => WaterUsageCharges * 0.0808,
                    _                                 => WaterUsageCharges * 0.1164
                };
            }
    
            public double TotalCharges => WaterUsageCharges + SewerCharges + EnvironmentCharges + ServiceCharges;
    
            public double LocalTaxes
            {
                get => AccountType.@class switch
                {
                    Classification.Residential        => TotalCharges * 0.115,
                    Classification.NonProfit          => TotalCharges * 0.085,
                    Classification.WaterIntensive     => TotalCharges * 0.825,
                    Classification.GeneralUse         => TotalCharges * 0.315,
                    _                                 => TotalCharges * 0.625
                };
            }
    
            public double StateTaxes => AccountType.@class switch
            {
                Classification.Residential            => TotalCharges * 0.035,
                Classification.NonProfit              => TotalCharges * 0.015,
                Classification.WaterIntensive         => TotalCharges * 0.105,
                Classification.GeneralUse             => TotalCharges * 0.065,
                _                                     => TotalCharges * 0.815
            }; 
    
            public double AmountDue => TotalCharges + LocalTaxes + StateTaxes;
        }
    }
  19. Click the Program.cs tab and change the document as follows:
    using StellarWaterPoint2.Models;
    using static System.Console;
    
    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("-----------------------------------------------------------");
    
    WaterBill bill = new WaterBill(answer);
    
    Write("Counter Reading Start:         ");
    bill.CounterReadingStart = int.Parse(ReadLine()!);
    Write("Counter Reading End:           ");
    bill.CounterReadingEnd = int.Parse(ReadLine()!);
    
    WriteLine("===========================================================");
    WriteLine("Stellar Water Point - Customer Invoice");
    WriteLine("Account Type:               {0}", bill.AccountType.name);
    WriteLine("===========================================================");
    WriteLine("Meter Reading");
    WriteLine("-----------------------------------------------------------");
    WriteLine("Counter Reading Start:      {0,10}", bill.CounterReadingStart);
    WriteLine("Counter Reading End:        {0,10}", bill.CounterReadingEnd);
    WriteLine("Total Gallons Consumed:     {0,10}", bill.Gallons);
    WriteLine("===========================================================");
    WriteLine("Therms Evaluation");
    WriteLine("-----------------------------------------------------------");
    WriteLine("HCF Total:                  {0,10:n}", bill.HCFTotal);
    WriteLine("First 35 Therms (* 1.5573): {0,10:n}", bill.Therms.First25Therms);
    WriteLine("First 20 Therms (* 1.2264): {0,10:n}", bill.Therms.Next15Therms);
    WriteLine("Above 40 Therms (* 0.9624): {0,10:n}", bill.Therms.Above40Therms);
    WriteLine("-----------------------------------------------------------");
    WriteLine("Water Usage Charges:        {0,10:n}", bill.WaterUsageCharges);
    WriteLine("Sewer Charges:              {0,10:n}", bill.SewerCharges);
    WriteLine("===========================================================");
    WriteLine("Bill Values");
    WriteLine("-----------------------------------------------------------");
    WriteLine("Environment Charges:        {0,10:n}", bill.EnvironmentCharges);
    WriteLine("Service Charges:            {0,10:n}", bill.ServiceCharges);
    WriteLine("Total Charges:              {0,10:n}", bill.TotalCharges);
    WriteLine("Local Taxes:                {0,10:n}", bill.LocalTaxes);
    WriteLine("State Taxes:                {0,10:n}", bill.StateTaxes);
    WriteLine("-----------------------------------------------------------");
    WriteLine("Amount Due:                 {0,10:n}", bill.AmountDue);
    Write("===========================================================");
  20. To execute, on the main menu, click Debug -> Start Without Debugging
  21. When requested, for the type of account, type 1 and press Enter
  22. For the Counter Reading Start, type 26526 and press Enter
  23. For the Counter Reading Start, type 42528 and press Enter
    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:         1
    -----------------------------------------------------------
    Counter Reading Start:         26526
    Counter Reading End:           42528
    ===========================================================
    Stellar Water Point - Customer Invoice
    Account Type:               Residential Household
    ===========================================================
    Meter Reading
    -----------------------------------------------------------
    Counter Reading Start:           26526
    Counter Reading End:             42528
    Total Gallons Consumed:          16002
    ===========================================================
    Therms Evaluation
    -----------------------------------------------------------
    HCF Total:                       21.39
    First 35 Therms (* 1.5573):      33.32
    First 20 Therms (* 1.2264):       0.00
    Above 40 Therms (* 0.9624):       0.00
    -----------------------------------------------------------
    Water Usage Charges:             33.32
    Sewer Charges:                    8.42
    ===========================================================
    Bill Values
    -----------------------------------------------------------
    Environment Charges:              0.08
    Service Charges:                  1.09
    Total Charges:                   42.91
    Local Taxes:                      4.94
    State Taxes:                      1.50
    -----------------------------------------------------------
    Amount Due:                      49.35
    ===========================================================
    Press any key to close this window . . .
  24. Press E to close the window and return to your programming environment

A Property of a Reference Type

You know that you can create a type that is the type of a class. You can create the property fully implemented. Here is an example:

public class Camera
{
    public string? Location { get; set; }
}

public class TrafficTicket
{
    public Camera Recorder { get; set; }
}

In this case, the compiler would issue a warning to let you know that the value(s) of the property could be null, which is a bad thing. To avoid the warning, you can apply the null conditional operator on the property. This can be done as follows:

public class Camera
{
    public string? Location { get; set; }
}

public class TrafficTicket
{
    public Camera? Recorder { get; set; }
}

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2001-2024, FunctionX Sunday 30 April 2023, 13:18 Next