Post

C# - Important Concepts in C#


Official Microsoft Learning Platform

In software development projects, the term state is used to describe the condition of the execution environment at a specific moment in time.

As your code executes line by line, values are stored in variables. At any moment during execution, the current state of the application is the collection of all values stored in memory.

Some methods don’t rely on the current state of the application to work properly. In other words, stateless methods are implemented so that they can work without referencing or changing any values already stored in memory. Stateless methods are also known as static methods.

Stateful versus stateless methods

  • In C#, methods can be stateful or stateless.
  • A stateless method is a method that does not modify the state of a value in memory.
  • For example, the Console.WriteLine() method doesn’t rely on any values stored in memory. It performs its function and finishes without impacting the state of the application in any way.
  • A stateful method is a method that modifies the state of a value in memory.They’re also known as instance methods.
  • Stateful (instance) methods keep track of their state in fields, which are variables defined on the class.
    • Each new instance of the class gets its own copy of those fields in which to store state.
    • A single class can support both stateful and stateless methods.
    • However, when you need to call stateful methods, you must first create an instance of the class so that the method can access state.

Overloaded methods

  • Many methods in the .NET Class Library have overloaded method signatures.
  • Among other things, this enables you to call the method with or without arguments specified in the calling statement.
  • An overloaded method is defined with multiple method signatures.
  • Overloaded methods provide different ways to call the method or provide different types of data.
  • In some cases, overloaded versions of a method are used to define a parameter using different data types.
  • For example, the Console.WriteLine() method has 19 different overloaded versions.
  • Most of those overloads allow the method to accept different types and then write the specified information to the console.
  • Consider the following code:
1
2
3
4
5
6
int number = 7;
string text = "seven";

Console.WriteLine(number);
Console.WriteLine();
Console.WriteLine(text);
  • In this example, you’re invoking three separate overloaded versions of the WriteLine() method.

    • The first WriteLine() method uses a method signature that defines an int parameter.
    • The second WriteLine() method uses a method signature that defines zero parameters.
    • The third WriteLine() method uses a method signature that defines a string parameter.
  • In other cases, overloaded versions of a method define a different number of parameters.
  • The alternative parameters can be used to provide more control over desired result.
  • For example, the Random.Next() method has overloaded versions that enable you to set various levels of constraint on the randomly generated number.
  • The following example calls the Random.Next() method to generate random integer values with different levels of constraint:
1
2
3
4
5
6
7
8
9
10
11
12
13
Random dice = new Random();
int roll1 = dice.Next();
int roll2 = dice.Next(101);
int roll3 = dice.Next(50, 101);

Console.WriteLine($"First roll: {roll1}");
Console.WriteLine($"Second roll: {roll2}");
Console.WriteLine($"Third roll: {roll3}");

// Output:
// First roll: 342585470
// Second roll: 43
// Third roll: 89
  • The first version of the Next() method doesn’t set an upper and lower boundary, so the method will return values ranging from 0 to 2,147,483,647, which is the maximum value an int can store.
  • The second version of the Next() method specifies the maximum value as an upper boundary, so in this case, you can expect a random value between 0 and 100.
  • The third version of the Next() method specifies both the minimum and maximum values, so in this case, you can expect a random value between 50 and 100.

Working with Arrays

  • An array is a sequence of individual data elements accessible through a single variable name.
  • You use a zero-based numeric index to access each element of an array.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int[] numbers = new int[5];
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;

Console.WriteLine(numbers[0]);
Console.WriteLine(numbers[1]);
Console.WriteLine(numbers[2]);
Console.WriteLine(numbers[3]);
Console.WriteLine(numbers[4]);

// Output:
// 10
// 20
// 30
// 40
// 50
  • In this example, you create an array of integers with five elements.

    • int[] is the data type of the array.
    • the new keyword is used to create a new array.
    • The int[5] syntax specifies that the array will contain five elements.
    • The numbers[0] = 10; syntax assigns the value 10 to the first element of the array.
  • Another way to create an array is to use an array initializer.
  • An array initializer is a comma-separated list of values enclosed in curly braces {}.
  • The following code creates an array of integers with five elements and initializes the array with values:
1
2
3
4
5
6
7
8
9
10
11
12
string[] names = { "John", "Paul", "George", "Ringo" };

Console.WriteLine(names[0]);
Console.WriteLine(names[1]);
Console.WriteLine(names[2]);
Console.WriteLine(names[3]);

// Output:
// John
// Paul
// George
// Ringo

Using the Length property

  • The Length property of an array returns the number of elements in the array.
  • The Length property is a read-only property, meaning you can’t change the value of the property.
  • The following code demonstrates how to use the Length property:
1
2
3
4
5
int[] numbers = new int[5];
Console.WriteLine(numbers.Length);

// Output:
// 5

Looping through an array using foreach

  • The foreach statement is used to iterate over the elements of an array.
  • Consider the following code:
1
2
3
4
5
6
7
8
9
10
int[] inventory = { 200, 450, 700, 175, 250 };
int sum = 0;
int bin = 0;
foreach (int items in inventory)
{
    sum += items;
    bin++;
    Console.WriteLine($"Bin {bin} = {items} items (Running total: {sum})");
}
Console.WriteLine($"We have {sum} items in inventory.");
  • In this example, you create an array of integers named inventory and initialize it with five elements.
  • You then use a foreach loop to iterate over the elements of the array.
  • The foreach loop assigns the value of each element in the array to the items variable.
  • The loop then calculates the sum of all the elements in the array and displays the running total as it iterates over the array.
  • Finally, the loop displays the total number of items in the inventory.
  • The output of this code is as follows:
1
2
3
4
5
6
Bin 1 = 200 items (Running total: 200)
Bin 2 = 450 items (Running total: 650)
Bin 3 = 700 items (Running total: 1350)
Bin 4 = 175 items (Running total: 1525)
Bin 5 = 250 items (Running total: 1775)
We have 1775 items in inventory.

A simple challenge

  • Declare an array and initialize it to contain the following elements:
1
2
3
4
5
6
7
8
9
B123
C234
A345
C15
B177
G3003
C235
B179
  • Create a foreach statement to iterate through each element of your array.
  • Report the Order IDs that start with the letter “B”.
  • You need to evaluate each element of the array. Report the potentially fraudulent Order IDs by detecting the orders that start with the letter “B”.
  • Your output should match the following:
1
2
3
B123
B177
B179

Writing meaningful comments

  • Comments are used to document your code.
  • Comments are ignored by the compiler and are not executed.
  • Comments are used to explain the purpose of the code, describe the algorithm, or provide additional information which is not obvious from the code itself.
  • Below are some examples of comments:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// This is an example of low quality comment

Random random = new Random();
string[] orderIDs = new string[5];
// Loop through each blank orderID
for (int i = 0; i < orderIDs.Length; i++)
{
    // Get a random value that equates to ASCII letters A through E
    int prefixValue = random.Next(65, 70);
    // Convert the random value into a char, then a string
    string prefix = Convert.ToChar(prefixValue).ToString();
    // Create a random number, pad with zeroes
    string suffix = random.Next(1, 1000).ToString("000");
    // Combine the prefix and suffix together, then assign to current OrderID
    orderIDs[i] = prefix + suffix;
}
// Print out each orderID
foreach (var orderID in orderIDs)
{
    Console.WriteLine(orderID);
}
  • There are two main problems with these comments:

    • The code comments unnecessarily explain the obvious functionality of individual lines of code. These are considered low-quality comments because they merely explain how C# or methods of the .NET Class Library work. If the reader is unfamiliar with these ideas, they can look them up using learn.microsoft.com or IntelliSense.
    • The code comments don’t provide any context to the problem being solved by the code. These are considered low-quality comments because the reader doesn’t gain any insight into the purpose of this code, especially as it relates to the larger system.
  • Below are some examples of high-quality comments:
  • High-quality comments provide context to the problem being solved by the code. They explain the purpose of the code, describe the algorithm, or provide additional information which is not obvious from the code itself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
  The following code creates five random OrderIDs
  to test the fraud detection process.  OrderIDs
  consist of a letter from A to E, and a three
  digit number. Ex. A123.
*/
Random random = new Random();
string[] orderIDs = new string[5];

for (int i = 0; i < orderIDs.Length; i++)
{
    int prefixValue = random.Next(65, 70);
    string prefix = Convert.ToChar(prefixValue).ToString();
    string suffix = random.Next(1, 1000).ToString("000");

    orderIDs[i] = prefix + suffix;
}

foreach (var orderID in orderIDs)
{
    Console.WriteLine(orderID);
}
  • Here is another example of high-quality comments:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
   This code reverses a message, counts the number of times
   a particular character appears, then prints the results
   to the console window.
 */

string originalMessage = "The quick brown fox jumps over the lazy dog.";

char[] message = originalMessage.ToCharArray();
Array.Reverse(message);

int letterCount = 0;

foreach (char letter in message)
{
    if (letter == 'o')
    {
        letterCount++;
    }
}

string newMessage = new String(message);

Console.WriteLine(newMessage);
Console.WriteLine($"'o' appears {letterCount} times.");

Increasing code readability with whitespace

  • Whitespace is used to separate code into logical sections.
  • Whitespace is ignored by the compiler and is not executed.
  • It can be created with spaces, tabs, and newlines (enter key).

  • Below are some examples of code with poor whitespace:
1
2
3
4
5
6
7
8
9
10
11
// Example 1:
Console
.
WriteLine
(
"Hello Example 1!"
)
;

// Example 2:
string firstWord="Hello";string lastWord="Example 2";Console.WriteLine(firstWord+" "+lastWord+"!");
  • Here is another example of code with no whitespace:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Random dice = new Random();
int roll1 = dice.Next(1, 7);
int roll2 = dice.Next(1, 7);
int roll3 = dice.Next(1, 7);
int total = roll1 + roll2 + roll3;
Console.WriteLine($"Dice roll: {roll1} + {roll2} + {roll3} = {total}");
if ((roll1 == roll2) || (roll2 == roll3) || (roll1 == roll3)) {
    if ((roll1 == roll2) && (roll2 == roll3)) {
        Console.WriteLine("You rolled triples!  +6 bonus to total!");
        total += 6;
    } else {
        Console.WriteLine("You rolled doubles!  +2 bonus to total!");
        total += 2;
    }
}
  • The code is difficult to read because there is no whitespace between the lines of code.
  • Below are some examples of code with good usse of whitespace:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Random dice = new Random();

int roll1 = dice.Next(1, 7);
int roll2 = dice.Next(1, 7);
int roll3 = dice.Next(1, 7);

int total = roll1 + roll2 + roll3;
Console.WriteLine($"Dice roll: {roll1} + {roll2} + {roll3} = {total}");

if ((roll1 == roll2) || (roll2 == roll3) || (roll1 == roll3))
{
    if ((roll1 == roll2) && (roll2 == roll3))
    {
        Console.WriteLine("You rolled triples!  +6 bonus to total!");
        total += 6;
    }
    else
    {
        Console.WriteLine("You rolled doubles!  +2 bonus to total!");
        total += 2;
    }
}
  • The code is easier to read because there is whitespace between the lines of code.
  • The code is separated into logical sections, making it easier to understand.
  • Notice that the curly braces are aligned to the same column, making it easier to see where each block of code begins and ends.

Sample application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
This C# console application is designed to:
- Use arrays to store student names and assignment scores.
- Use a `foreach` statement to iterate through the student names as an outer program loop.
- Use an `if` statement within the outer loop to identify the current student name and access that student's assignment scores.
- Use a `foreach` statement within the outer loop to iterate though the assignment scores array and sum the values.
- Use an algorithm within the outer loop to calculate the average exam score for each student.
- Use an `if-elseif-else` construct within the outer loop to evaluate the average exam score and assign a letter grade automatically.
- Integrate extra credit scores when calculating the student's final score and letter grade as follows:
    - detects extra credit assignments based on the number of elements in the student's scores array.
    - divides the values of extra credit assignments by 10 before adding extra credit scores to the sum of exam scores.
- use the following report format to report student grades:

    Student         Grade

    Sophia:         92.2    A-
    Andrew:         89.6    B+
    Emma:           85.6    B
    Logan:          91.2    A-
*/

int examAssignments = 5;

string[] studentNames = new string[] { "Sophia", "Andrew", "Emma", "Logan" };

int[] sophiaScores = new int[] { 90, 86, 87, 98, 100, 94, 90 };
int[] andrewScores = new int[] { 92, 89, 81, 96, 90, 89 };
int[] emmaScores = new int[] { 90, 85, 87, 98, 68, 89, 89, 89 };
int[] loganScores = new int[] { 90, 95, 87, 88, 96, 96 };

int[] studentScores = new int[10];

string currentStudentLetterGrade = "";

// display the header row for scores/grades
Console.Clear();
Console.WriteLine("Student\t\tGrade\n");

/*
The outer foreach loop is used to:
- iterate through student names
- assign a student's grades to the studentScores array
- sum assignment scores (inner foreach loop)
- calculate numeric and letter grade
- write the score report information
*/
foreach (string name in studentNames)
{
    string currentStudent = name;

    if (currentStudent == "Sophia")
        studentScores = sophiaScores;

    else if (currentStudent == "Andrew")
        studentScores = andrewScores;

    else if (currentStudent == "Emma")
        studentScores = emmaScores;

    else if (currentStudent == "Logan")
        studentScores = loganScores;

    int sumAssignmentScores = 0;
    decimal currentStudentGrade = 0;
    int gradedAssignments = 0;

    /*
    the inner foreach loop sums assignment scores
    extra credit assignments are worth 10% of an exam score
    */
    foreach (int score in studentScores)
    {
        gradedAssignments += 1;

        if (gradedAssignments <= examAssignments)
            sumAssignmentScores += score;
        else
            sumAssignmentScores += score / 10;
    }

    currentStudentGrade = (decimal)(sumAssignmentScores) / examAssignments;

    if (currentStudentGrade >= 97)
        currentStudentLetterGrade = "A+";
    else if (currentStudentGrade >= 93)
        currentStudentLetterGrade = "A";
    else if (currentStudentGrade >= 90)
        currentStudentLetterGrade = "A-";
    else if (currentStudentGrade >= 87)
        currentStudentLetterGrade = "B+";
    else if (currentStudentGrade >= 83)
        currentStudentLetterGrade = "B";
    else if (currentStudentGrade >= 80)
        currentStudentLetterGrade = "B-";
    else if (currentStudentGrade >= 77)
        currentStudentLetterGrade = "C+";
    else if (currentStudentGrade >= 73)
        currentStudentLetterGrade = "C";
    else if (currentStudentGrade >= 70)
        currentStudentLetterGrade = "C-";
    else if (currentStudentGrade >= 67)
        currentStudentLetterGrade = "D+";
    else if (currentStudentGrade >= 63)
        currentStudentLetterGrade = "D";
    else if (currentStudentGrade >= 60)
        currentStudentLetterGrade = "D-";
    else
        currentStudentLetterGrade = "F";

    // Student         Grade
    // Sophia:         92.2    A-

    Console.WriteLine($"{currentStudent}\t\t{currentStudentGrade}\t{currentStudentLetterGrade}");
}

// required for running in VS Code (keeps the Output windows open to view results)
Console.WriteLine("\n\rPress the Enter key to continue");
Console.ReadLine();

This post is licensed under CC BY 4.0 by the author.