Videos to Watch - Week 3

  1. Understanding Scope and Accessibility Modifiers
  2. Understanding Namespaces and Working with the .NET Class Library
  3. Creating and Adding References to Assemblies
  4. Working with Collections
  5. Working with LINQ
  6. Enumerations and the Switch Decision Statement
  7. Gracefully Handling Exceptions
  8. Understanding Events and Event-Driven Programming
  9. (Optional) Where to Go From Here

Problems - Week 3

Problem 1

Create a console application. Notice that the Program class is created inside of the Namespace which matches the name of our project. Create two additional classes, each called Person, but create one inside of a namespace ns1, while the other will be inside of a namespace ns2. Each person class should have a single method, Print, which prints out "I am from ns2" in the ns2 namespace, and "I am from ns1" inside the ns1 namespace.

In the Main method of your program class, create an instance of each type of Person and call the Print method.

public class Program
{
    public static void Main(string[] args)
    {
        ns1.Person person1 = new ns1.Person();
        ns2.Person person2 = new ns2.Person();
        person1.Print();
        person2.Print();
        Console.ReadLine();
    }
}
                      

Problem 2

We're going to modify our Week 2 : Problem 11 program to make our Employee class part of a Human Resources Payroll system. We'll make a slight change to the existing employee class. Instead of "Salary" being marked as a public property, we're going to modify it using the Private keyword access modifier:

private decimal Salary {get; set;}

By marking the salary property private, only methods and properties defined inside of the Employee class can change the Salary property. You can read more about access modifiers here: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers

We'll be adding two other methods to our employee class:

Dispense()
Returns a decimal of the Salary divided by 26. This is the bi-monthly paycheck amount the employee receives.
Promote(decimal additionalPay, string newJobTitle)
Adds an amount to an employee's salary, and changes their job title.

Here's some barebones code of our new HR System:

using System;
using System.Collections.Generic;  //I need this to use List<Employee> instead of Employee[]

public class Program
{
    public static void Main()
    {
        List<Employee> employees = new List<Employee>;()
        employees.Add(new Employee("Rick", new DateTime(1981, 1, 16), "Software Engineer", 65000m));
        employees.Add(new Employee("Tina", new DateTime(1978, 7, 4), "Senior Software Engineer", 85000m));
        employees.Add(new Employee("Chris", new DateTime(1993, 2, 13), "Associate Software Engineer", 55000m));
        employees.Add(new Employee("Mindy", new DateTime(1996, 8, 3), "Chief Technology Officer", 155000m));
        
        foreach(Employee employee in employees)
        {
            Console.WriteLine(employee.Dispense());
        }
        
        //employees[0].Salary = 1000000000m  //This doesn't compile
        employees[2].Promote(10000m, "Software Engineer");   //But this does
        
        Console.ReadLine();
    }

}
                      

While this is an oversimplified example of how developers use the private keyword, in practice it forces us to interact with code through a well-defined contract. This is the principal of Encapsulation.

Problem 3

We're going to re-write Week 1 - Problem 9 to use the "switch" keyword". Here's an implementation of the Problem to start with:

                      
Console.WriteLine("Enter your score");
string input = Console.ReadLine();
int grade = int.Parse(input);
int leadingDigit = grade / 10;

if(leadingDigit == 10)
{
    Console.WriteLine("A");
}
else if(leadingDigit == 9)
{
    Console.WriteLine("A");
}
else if(leadingDigit == 8)
{
    Console.WriteLine("B");
}
else if(leadingDigit == 7)
{
    Console.WriteLine("C");
}
else if(leadingDigit == 6)
{
    Console.WriteLine("D");
}
else
{
    Console.WriteLine("F");
}
                      

Change the if/else block to use the switch, case, default, and break keywords.

Problem 4

We're going to modify our Week 2 - Problem 9 to enforce some additional rules about our Vehicle. For the purposes of this exercise, we want to limit our set of "makes" to the following: GM, Ford, Lincoln, Chrysler, Cadillac, Jeep, Tesla, Nissan, Mitsubishi, Infiniti, Honda, Toyota, MercedesBenz, Audi, BMW, Volkswagen. Rather than marking the Make property as a string, we're going to use an Enum structure to create this restriction. Add the following code to your project:

public class Program
{
    public static void Main()
    {
        Vehicle v1 = new Vehicle();
        v1.Make	= "Ford";   //This line is going to need to change
        v1.Model = "T";
        v1.Color = "Brown";
        v1.Miles = 1000m;
        v1.Year = 1925;
        
    }
    
}

public class Vehicle
{  
    //...Your existing code should still be in here
    public VehicleMake Make { get; set;}

}

public enum VehicleMake
{
    GM, 
    Ford, 
    Lincoln, 
    Chrysler, 
    Cadillac, 
    Jeep, 
    Tesla, 
    Nissan, 
    Mitsubishi, 
    Infiniti, 
    Honda, 
    Toyota, 
    MercedesBenz, 
    Audi, 
    BMW, 
    Volkswagen
}
                      

Using the supplied code, modify the Program's Main method so that the Make is set correctly. Visual Studio's IntelliSense capability should "suggest" the correct way to write this.

Problem 5

We're going to re-run our Week 1 : Problem 3 code, but pass in some different parameters:

Console.WriteLine("Enter a number");
string input = Console.ReadLine();
int a = int.Parse(input);


Console.WriteLine("Enter another number");
input = Console.ReadLine();
int b = int.Parse(input)

Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
Console.WriteLine("{0} - {1} = {2}", a, b, a - b);
Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
Console.WriteLine("{0} / {1} = {2}", a, b, a / b);
Console.WriteLine("{0} mod {1} = {2}", a, b, a % b);

Console.ReadLine();
                      

What happens if you enter the value "0" for your second parameter? Alternatively, what happens if you type "hot dogs" instead of a number? In either case, this will cause what is referred to as an exception, sometimes referred to as a runtime error. Even though your code compiled, the user was able to enter input which your code did not expect. Professional developers are expected to practice defensive programming to guard against this. In C#, an unhandled exception or runtime error will immediately terminate execution of the program, so as to avoid executing code which is in an unexpected state and causing undesirable side effects to your system.

One of the basic techniques to harden your code against these sort of situations is to introduce some basic exception handling structures in your code:

int a = 20;
int b = 10;
int c = 0;

try
{
    int result1 = a / b;
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

try
{
    int result2 = a / c;
}
catch(System.DivideByZeroException ex)  //This will only catch "DivideByZeroException" objects
{
    Console.WriteLine(ex.Message);
}

                        
                      

Modify the problem above to handle passing 0 as the second number. If you're feeling ambitious, try handling the int.Parse exception that occurs when a user types in something that isn't a number, as well.

Problem 6

Take a look at the following code:

                      
public class Rectangle
{
    //The underscore is a "style guideline"
    //but has no effect on how this compiles or runs.
    private double _length;     
    private double _width;

    public Rectangle(double length, double width)
    {
        _length = length;
        _width = width;
    }
    
    public double Perimeter
    {  
        get
        {
            return 0;   //This isn't correct
        }
        
    }
    
    public double Area
    {
        get
        {
            return 0;   //Neither is this
        } 
    }
}
                

In this case, we don't supply a way to set Area or Perimeter, since these values are derived from the length and width. Notice that the Perimeter and Area methods don't return the correct values:

Rectangle r = new Rectangle(4, 5);
Console.WriteLine(r.Area); //Should be 20, but prints 0

This is an interesting example of code which compiles, runs without generating any exceptions, but is functionally incorrect. While you might notice this if you spent some time looking at the code, these sorts of defects are often much more subtle. To improve the quality of code, professional developers rely on automated Unit Tests to verify that their code not only compiles, but functionally works as expected

Visual Studio provides support for several Unit Test frameworks. Take a second to review this information here: https://docs.microsoft.com/en-us/visualstudio/test/unit-test-basics

Once you've read up on the Tools and Processes associated with Unit Tests, create two Unit Tests for the Perimeter and Area properties, respectively. Use the Test Explorer Window to execute your Unit Tests: https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer

Continue to What's Next