CSharp Fundamentals. Quick Reference Essentials and Examples
CSharp Fundamentals. Quick Reference Essentials and Examples
and Examples
Adam Seebeck
United States
C# Fundamentals: Quick Reference Essentials and Examples
© 2018 unQbd
ALL RIGHTS RESERVED. No part of this book may be reproduced or distributed in any form or by any
means except as permitted by U.S. copyright law, without the prior written permission of the copyright
owner.
The information contained within this book is sold without warranty, either express or implied. The
authors, unQbd, dealers, and distributors will not be held liable for any damages caused or alleged to
have been caused by the contents of this book directly or indirectly.
Trademark information for all companies and products mentioned are notated by the appropriate use of
capitals. There is no guarantee from unQbd in the accuracy of this information.
For permission to use material from this text or product, please contact www.unQbd.com/Support or
email [email protected]
ISBN: 978-0-692-19899-5
Production Version: 1.01
unQbd
100 S. Belcher #5483
Clearwater, FL 33765
USA
unQbd (pronounced “un-cubed”) is an education technology company, providing both digital and print
material. Purchase our products at your local college store or at our online store www.unQbd.com.
TABLE OF CONTENTS
• Quick Reference Essentials give a brief explanation and include examples of code.
• Full Examples often follow Quick Reference Essentials and expand on the concept by displaying
complete real-world examples of usage.
• Walkthroughs provide step by step instructions on how to complete a specific task.
This book focuses on software development using the C# language and computer software
called Visual Studio (free to download). Both the language and software are covered within this book;
no prior software development knowledge is required. Flowcharts, screenshots, and additional visual
aids are used throughout the book to help explain concepts.
Page |2
INTRODUCTION TO C#
C# uses the .NET Platform that contains all the components needed to run an application; one
component is the Common Language Runtime (CLR). The CLR handles various tasks such as compiling
code to 1s and 0s (binary) for the computer to understand. The .NET Standard Library is also within the
.NET Platform and contains shared libraries of code that developers use when developing applications.
This helps developers so they do not have to write code from scratch. For example, if an application
needs to read information from a text file, there is already a built-in class for this and there is no need to
recreate it.
An additional benefit to using C# is its versatility. C# is general purpose language, meaning it can
be used to create many kinds of applications. Built on top of the .NET Standard Library are collections of
app models. These are libraries designed for a specific type of application such as computer software,
websites, or phone apps. The three major app models are: .NET framework, .NET Core, and Xamarin.
Page |3
Listed below are a few examples of apps that can be created using the C# language.
• Websites: Dynamic websites can be built using C# and ASP.NET in Visual Studio.
• Mobile Applications: Quickly create iOS, Android, and Universal Windows Platform (UWP)
applications using C# and Xamarin in Visual Studio.
• Games: Unity3d is a separate (free) software program used to design 2D, 3D, virtual, and mixed
reality games. While the Unity Game Engine is used to design the game, the coding is still
developed in Visual Studio with C#.
• Computer Software: Software for Windows can be created using WPF, Windows Forms, or
Universal Windows Platform.
Page |4
After downloading and beginning the installation process the screen below will be displayed.
This screen contains the components that can be installed. Components can always be added later by
going to top menu bar and selecting “Tools -> Get Tools and Features”.
For this book, make sure to have at least “.NET desktop development” and “.NET Core cross-
platform development” checked. Review all the additional components available to install to become
familiar with the options. Click the install or modify button to continue the installation process.
Page |5
The following are instructions on how to create a Console Application. Console Applications are
great for demonstrating concepts; they are simple text-based applications.
Walkthrough: Microsoft Windows setup for a Console Application **may vary for Mac computers
2. Once loaded, there will be a lot of content on the screen that can seem overwhelming. Ignore all
of that for now.
3. Click File -> New -> Project.
4. On the left menu, select the dropdown for Visual C# and click on .NET Core.
5. On the right menu, select Console App (.NET Core).
6. Toward the bottom of the window, you will see “Name:”, this is where you enter a name for
your program.
7. Click the “Browse” button next to “Location”; this changes the location of where the project is
saved.
8. Clicking “OK” in the bottom right corner will create the Console Application.
Page |6
When learning a new programming language, it is a tradition to display the words “Hello
World!” on the screen. Please note, this is the only example in the book where an explanation is not
provided about the coding and simply the instructions are given to produce the results.
1. Create a Console application named HelloWorld. The steps to create a Console application can
be found in the previous section “Visual Studio: Console Application Setup”.
2. A page full of unfamiliar code should appear and look like the image below.
Play Button
4. Press the play button or the F5 key to run the Console application.
COMMENTS
Program comments are essentially notes left on programs to explain or highlight the
functionality of code. Comments are non-executing code and will not affect the usage of a program.
Comment Types:
• Line comments are for a single line of code and begin with two forward slashes “//”. Anything
following the slashes on that line of code will be considered a comment and not execute.
• Block comments are for multiple lines of code and begin with one forward slash and an asterisk
“/*” and to complete the comment end with an asterisk and a forward slash “*/”.
// Anything after two forward slashes on this line will not execute.
Console.Write("Hello World!"); // A comment can also be placed after code.
Visual Studio has a convenient way to quickly comment or uncomment selected lines of code.
Snippet and shortcut keys can help save time while coding in Visual Studio. With snippet keys,
enter only a few letters or a keyword and then press tab twice to insert a code fragment.
// cw (tab)(tab) : Type cw and then press the tab key twice, the line below will display
Console.WriteLine();
A variable is a named location in computer memory that holds information for later use. The
information within the variable can change or can be used when needed. Think of a variable as a box
that has been named; the contents within the box can change, but the box name stays the same.
The contents within a variable can have different data types. The data type declares what type
of information is going to be contained within the variable. There are many kinds of data types, such as a
string for text (“Hello World”) or an integer for numbers (54) with which mathematical calculations can
be performed.
In the example below, a variable of data type string is declared with a variable name of
“myStringName”. Declaring a variable can be thought of as creating an empty box that is named.
string myStringName;
The variable “myStringName” now contains the value “Hello” within it. In the example below,
the value is changed to “Hello again”. Notice in the example below, the data type is not declared when
changing the value. Once the data type is declared, it is not declared again.
A string puts the two values together; an integer adds them together.
When declaring certain numeric data types, such as a float or a decimal (see page 11), it is
required to put a character behind the value. The example below demonstrates this.
int number1 = 5;
float number2 = 2.55f; // When declaring a float an "f" needs to be after the value
double number3 = 3.33;
decimal number4 = 4.66m; // When declaring a decimal an "m" needs to be after the value
Example: var
Every data type uses a specific amount of memory when declared. The smallest measurement is
a bit, and eight bits make up a byte. The chart below lists a few of the most common data types along
with how many bytes they use, their range, and an example of their data.
byte 1 0 to 255 7
ushort 2 0 to 65,535 7
uint 4 0 to 4,294,967,295 9
ulong 8 0 to 18,446,744,073,709,551,615 11
Integral types can be signed types or unsigned types. A signed type value can be positive or negative
and unsigned types can only be positive values. For example, a short, which is a signed type, can have a
value from -32,768 to 32,767. However, if negative numbers are not included, then the positive
numbers will double when making it unsigned. Using the unsigned data type for short (which is ushort),
the range will become 0 to 65,535
P a g e | 12
NAMING CONVENTIONS
Using proper naming and capitalization techniques can greatly increase the readability of code.
In C# the two popular capitalization techniques are Camel Casing and Pascal Casing for naming
variables, classes, methods, etc.
• Camel Casing: The first letter of the first word is lowercase, and all subsequent words
start with a capital letter.
• Pascal Casing: The first letter of every word is capitalized.
int myFavoriteNumber = 7;
Use Camel Casing for local variables and arguments. Pascal Casing is used in most other
situations. While moving ahead in this book take notice of which casing technique is used.
Names should not include hyphens, spaces, non-alphanumeric characters, and they do not start
with a numeric number. The name should also be self-documenting. For example, instead of calling a
variable “variable1”, which has no meaning, it should be “scoreTotal”, which describes what the variable
is.
P a g e | 13
OPERATORS
+ Add
- Subtract
Arithmetic + - * / % * Multiply
/ Divide
% Remainder
Example: 5 + 1 // Output: 6
Assignment operators assign a value
Assignment = += -= *= /= %=
Example: X = 5 // X is assigned the value 5
Console applications are great for demonstrating computer concepts, which is why this book
focuses on them. They can easily output text and accept user input.
“Write” and “WriteLine” can both be used to output text in a Console application. The example
below demonstrates how to enter text between the quotes for it to display on the console.
Console.Write("Hello "); // Write does not append a new line when finished
Console.Write("World!"); // Output: Hello World!
Below is a brief description of what each part of the above code represents.
Console.WriteLine("Hello World!");
“ReadLine” is used to input text in a Console application. “ReadLine” waits until text is entered
in the Console application and for the “Enter” key to be pressed.
Console.ReadLine();
User input from “Console.ReadLine()” is always a string. If the variable needs to be an integer,
for example, you would need to convert it from string to int (covered later in the “Parse and TryParse”
section).
P a g e | 15
There is a way to shorten the code from having to type “Console.WriteLine” to just “WriteLine”.
When creating a Console application, there is already some default code created. At the top of the
default “Program.cs” page, there is “using System;”. Add “using static System.Console;” below
“using System”. A more detailed explanation on this topic is covered later in “Namespaces & Using
Directives”.
ReadLine();
WriteLine(“Console. is no longer needed!”);
There are several different techniques to output data. In the examples below, the same
information is displayed in multiple ways. The last example uses the dollar sign before the quotes. This
denotes the usage of string interpolation. String interpolation is a feature that was introduced in C# 6; it
helps provide a more readable and convenient syntax and is the format this book will use going forward.
class ExampleOutput
{
static void Main(string[] args)
{
string todaysDay = "Tuesday";
ReadLine();
}
}
P a g e | 16
Escape characters are a special sequence of characters that are used to display something
different than the exact interpretation. For example, entering in quotes within “Writeline(“ ”Hello” ”)”
quotes will cause an issue. To get around this issue, use a backslash.
Escape characters can also be useful for starting a new line (“\n”), tab (“\t”) content, and many
other options.
The “@” symbol can be used before text to make the entire string ignore escape characters; the
technical name for this is verbatim string literal. This is often helpful with file names which would
require a lot of double backslashes (“//”).
Example: File name with backslashes compared to file name with “@” symbol
CASTING
The conversion from one type to another is called typecasting, usually referred to as casting.
The two types of casting are implicit casting and explicit casting.
• Implicit casting occurs without specific instructions saying to cast. In most cases this will occur
automatically when going from a narrow type to a wider type with no loss of data.
• Explicit casting occurs because instructions stated for the casting to happen. Going from a wider
type to a narrow type generally requires explicit casting to acknowledge there may be loss of
data.
When adding numbers together they both must be the same type. In the example below, a short
and an int are added together using implicit casting. There is no chance of data loss because a short is
going from a narrow type to a wider type. A short is numbers -32,768 to 32,767, and an int is numbers -
2,147,483,649 to 2,147,483,647.
Casting is ideal for going from similar data types, such as short to int. However, if you need to convert
from string to an int, that is covered in the upcoming “Parse and TryParse” section in this book.
P a g e | 18
Decision-making is an important part of computer programs. The “if”, “else if”, and “else”
statements identify which statement to run based on the value of a Boolean (true or false) expression.
The visual example below asks the question “Is today Monday?”; it must have a true or false answer.
Is today
True False
Monday?
Today is NOT
Today is Monday
Monday
The code within an “if” statement will only execute if the condition is true. In the example below
there is an “if” statement with an equality operator which is “==”. In a previous section called
“Operators”, there is a list of relational operators that can be used within “if” statements.
if ("Monday" == "Monday")
{
// Code within the curly brackets will execute because Monday equals Monday is TRUE
}
“If” statements should contain at least one variable. In the example above, Monday will always equal
Monday so there is no reason to use an “If” statement.
if (todaysDay == "Monday")
{
// Code within the curly brackets will execute because Monday equals Monday is TRUE
}
P a g e | 19
Using an “else” statement after an “if” statement is sort of a catch all. Meaning that if the “if”
statement is not true, execute the code within the “else” statement.
if (todaysDay == "Monday") // This will NOT execute, todaysDay does not equal Monday
{
WriteLine("Today is Monday");
}
else // This WILL execute, the previous if statement was not true
{
WriteLine("Today is NOT Monday"); // Output: “Today is NOT Monday”
}
Use an “else if” statement after an “if” statement to test additional conditions to be true. Only
one “if” statement and one “else” statement can be used. However, multiple “else if” statements are
allowed. As soon as an “if” or “else if” statement is true, only the code within that code block is
executed. Any additional statements following that one will not execute even if they are true. The order
in which the “else if” statements are placed is important.
if (todaysDay == "Monday") // This will not execute, todaysDay does not equal Monday
{
WriteLine("Today is Monday");
}
else if (todaysDay == "Tuesday") // This will execute, todaysDay equals Tuesday
{
WriteLine("Today is Tuesday"); // Output: “Today is Tuesday”
}
else if (todaysDay == "Wednesday")
{
WriteLine("Today is Wednesday");
}
else // This will not execute, a previous If statement was true
{
WriteLine("Today is NOT Monday, Tuesday, or Wednesday");
}
P a g e | 20
The curly brackets following a condition are not always needed. If the statement is exactly one
line of code they are not needed. They are only required for multiple lines of code.
if (todaysDay == "Monday")
WriteLine("Today is Monday");
else if (todaysDay == "Tuesday")
WriteLine("Today is Tuesday"); // Output: “Today is Tuesday”
else if (todaysDay == "Wednesday")
{
// Curly brackets are needed if there are more than 1 statement following a condition
WriteLine("Today is Wednesday");
WriteLine("It is the middle of the work week!");
}
else
WriteLine("Today is NOT Monday, Tuesday, or Wednesday");
if (myValue <= 3)
WriteLine("The value is low");
else if (myValue >= 7)
WriteLine("The value is high");
else // The else value is true because the first 2 conditions were not met
WriteLine("The value is in the middle"); // Output: The value is in the middle
ReadLine();
}
}
P a g e | 21
“If” statements can be nested, meaning they can be put inside other “if” statements.
if (gameActive == true)
{
WriteLine("Game is Active!");
Conditional operators (covered in the “Operators” section) are commonly used in “if” and “else
if” statements. In the example below the “&&” operator is used, meaning that both conditions must be
true.
SWITCH
The switch statement is like an “if” statement; it is useful if you have several options from which
to choose. Anything that can be done with a switch statement can also be done with an “if” statement.
Select whichever makes the code easiest to understand.
With switch statements each option is a case, and there is also a default option if none of the
cases match (like an “else” statement). Within the case curly brackets “{}” are not needed. A case is
ended with “break;”.
Example: Switch
int optionPicked = 3;
switch (optionPicked)
{
case 1:
WriteLine("My number is 1");
break;
case 2:
WriteLine("My number is 2");
break;
case 3: // case 3 is true
WriteLine("My number is 3"); // Output: My number is 3
break;
default:
WriteLine("Other option");
break;
}
If there is code within a case, “break” must be used. If the code within a case is empty the case
will “fall through” until a “break” is reached. In the example below several cases are combined into one
result.
int optionPicked = 2;
switch (optionPicked)
{
case 1:
case 2:
case 3:
// The text below will display
WriteLine("Low number selected"); // Output: “Low number selected”
break;
case 4:
P a g e | 23
case 5:
WriteLine("Medium number selected");
break;
default:
WriteLine("Other number selected");
break;
}
class ExampleSwitch
{
static void Main(string[] args)
{
WriteLine("Type the name of a number: One, Two, Three or Four");
string num1 = ReadLine();
switch (num1)
{
case "One":
case "Two":
WriteLine("Picked number 1 or 2");
break;
case "Three":
WriteLine("Picked number 3");
break;
case "Four":
WriteLine("Picked number 4");
break;
default:
WriteLine("Entered incorrect input");
break;
}
ReadLine();
}
}
P a g e | 24
Parse and TryParse both attempt to convert one data type to another. The example below
attempts to convert a string to an int. When using Parse, be 100% certain that it will convert
successfully, otherwise the program will crash. For example, if a user is asked to input a numeric number
and they type the word “Hello”, the program will crash.
TryParse is a safer approach because it checks if the value can convert to a numeric value. If
TryParse cannot convert the value, it sets the value to 0.
Example: TryParse using an Inline Out Variable (this is a new feature introduced in C# 7)
class ExampleParse
{
static void Main(string[] args)
{
WriteLine("Enter a number");
string text1 = ReadLine();
// Very unsafe to do this because if the user enters incorrect information the
// program will crash. Use TryParse instead.
int num1 = int.Parse(text1);
WriteLine($"The sum of first number {num1} and second number {num2} is {num1 + num2}");
// The Parse below is safe because you are 100% sure 6 will convert into an int
string text3 = "6";
int num3 = int.Parse(text3);
ReadLine();
}
}
class ExampleTryParse
{
static void Main(string[] args)
{
WriteLine("Enter a number");
string text1 = ReadLine();
int.TryParse(text1, out int num1);
WriteLine($"The sum of first number {num1} and second number {num2} is {num1 + num2}");
ReadLine();
}
}
P a g e | 26
LOOPS
A Loop can repeat a block of code multiple times. The four main types of loops are While, Do-
While, For, and ForEach.
False
int number = 0;
The above code will loop and output numbers from 0 to 9. Every time the loop executes it
increases the value by 1 until the number is equal to 10.
A Do-While Loop evaluates the condition after the loop has executed, meaning the code block
will always execute at least once.
int number = 0;
do
{
Write(number);
number++;
}
while (number < 10);
// Output: 0123456789
P a g e | 27
A For Loop is generally used when the number of iterations needed is known. The starting value,
test conditions, and the increment/decrement are all set in one compact line of code.
A ForEach Loop is used for collections of items, such as arrays (arrays covered in next section).
class ExampleLoop
{
static void Main(string[] args)
{
WriteLine("Enter a number to count up to it using a While loop");
int.TryParse(ReadLine(), out int num1);
int counter = 0;
while (counter <= num1)
{
WriteLine(counter);
counter++; // number++ is equivalent to using “number = number + 1”
}
ReadLine();
}
}
P a g e | 28
Putting a loop within another loop is called a nested loop. The outer loop is called the parent
loop, and the loop within is the child loop. In the example below the outer loop controls when to exit
the game.
counter = 0;
WriteLine("Press Enter to display again or type Quit to exit.");
playGame = ReadLine();
}
Exit a loop early by using the break keyword. In the example below, the program is ended when
the user guesses the correct number.
Example: Break
if (guessedNumber == computerNumber)
break;
}
There is another keyword like break called continue. Instead of exiting the loop, continue goes
directly to the start of the loop to check the condition again. In the example below, continue is used to
skip the number 7.
Example: Continue
ARRAYS
An array is a collection of data types of the same type; for example, an array could contain all
strings. Arrays can be looped over to display their items, sorted, and many other operations.
Use a set of square brackets “[]” after the datatype to declare an array. Arrays are usually
named with a plural word. In the example below, an array of strings is named “foods”.
string[] foods;
An array must be instantiated to use it. Instantiate means to “create an instance of”; this is
done using the “new” keyword. In the example below, the number 5 specifies how many items (often
referred to as elements) the array can hold.
string[] foods;
foods = new string[5];
There are several ways to assign values to an array. It is important to point out that arrays start
at value 0 and not 1. A few examples of how to assign values are listed below:
// Example 1
string[] foods = new string[5];
// Example 2
string[] drinks = new string[5] { "Pepsi", "Sprite", "", "", "" };
// The additional "" are holding the place for the remaining 3 items
WriteLine($"I want a {drinks[1]}."); // Output: I want a Sprite.
// Example 3
string[] cars = new string[] { "Ford", "Toyota" };
WriteLine($"I want a {cars[0]}."); // Output: I want a Ford.
P a g e | 30
// Example 4
string[] animals = { "Cat", "Dog" };
WriteLine($"I want a {animals[1]}."); // Output: I want a Dog.
class ExampleArrayForEachLoop
{
static void Main(string[] args)
{
string[] foods = { "Pizza", "Burger" };
ReadLine();
}
}
Full Example: Sort and array length. The length property in this example is used to determine how many
loops should occur.
using System;
using static System.Console;
class ExampleArraySort
{
static void Main(string[] args)
{
int[] someNumbers = { 21, 22, 50, 3, 6 };
The above example uses “Array.Sort()”. Sort is a method within the Array class that organizes
the contents of an array numerically and alphabetically. The Array class has other methods as well, such
as “Array.Reverse()”.
P a g e | 31
In the previous section, the arrays have all been one-dimensional arrays. It is possible to make
two-dimensional arrays, three-dimensional arrays, and so on. These Multidimensional arrays can be
useful; the example below shows a two-dimensional array for a game map. Two values are needed for a
2-D array (row and column). Indicate a 2-D array by entering a comma between the square brackets.
class Example2DArray
{
static void Main()
{
// 2-D Array for a game map
// Place a comma between the brackets [,] to use a 2-D Array
string[,] myGameMap = new string[,]
{
{"Scary Room1", "Safe Room2", "Safe Room3"}, // Row 0
{"Dangerous Room4", "Safe Room5", "Safe Room6"}, // Row 1
{"Safe Room7", "Scary Room8", "Safe Room9"} // Row 2
};
// The first number is the row, the second number is the column
// Keep in mind that array values start at 0
WriteLine(myGameMap[0, 0]); // Output: Scary Room1
WriteLine(myGameMap[1, 0]); // Output: Dangerous Room4
WriteLine(myGameMap[2, 2]); // Output: Safe Room9
}
}
P a g e | 32
While a 2-D array can be thought of as making a square with width and height (row and
column), a 3-D array can be thought of as making a cube to include depth (row, column, and depth).
When creating a 3-D array, add an additional comma between the square brackets to indicate the 3-D
array is being created.
class Example3DArray
{
static void Main()
{
// Add 2 commas between the square brackets to indicate a 3-D array
string[,,] arrayExample3D = new string[,,]
{
{{"This is ", "a 3-D "}, // Row 0, Column 0, Depth is 0 or 1
{"array ", "example."} // Row 0, Column 1, Depth is 0 or 1
},
{{"They can ", "become "}, // Row 1, Column 0, Depth is 0 or 1
{"complex ", "very quickly."} // Row 1, Column 1, Depth is 0 or 1
},
};
// [Row, Column, Depth]
Write(arrayExample3D[0, 0, 0]); // Output: This is
Write(arrayExample3D[0, 0, 1]); // Output: a 3-D
Write(arrayExample3D[0, 1, 0]); // Output: array
Write(arrayExample3D[0, 1, 1]); // Output: example.
WriteLine(); // Add a space
Write(arrayExample3D[1, 0, 0]); // Output: They can
Write(arrayExample3D[1, 0, 1]); // Output: become
Write(arrayExample3D[1, 1, 0]); // Output: complex
Write(arrayExample3D[1, 1, 1]); // Output: very quickly
// Output:
// This is a 3-D array example
// The can become complex very quickly
}
}
P a g e | 33
ARRAYS: JAGGED
A jagged array is when arrays of various sizes are put inside an array.
string[][] jaggedEx = new string[3][] { new string[2], new string[5], new string[8] };
METHODS
A method is a group of reusable code that carries out a task. To use a method, define the
method and call the method. When defining a method, declare the elements of its structure.
• Access Modifier: The access modifier determines the visibility of the method (which classes
have access to them). Examples include public, internal, private, and protected. Access
modifiers are optional.
• Static Modifier: Static methods are shared by all instances of the class, meaning the method can
be accessed directly from the class name (static and classes are covered in the Classes section).
Static modifiers are optional.
• Return Type: A method can return a value type (Int, String, Bool, etc.) or return void if the
method does not return anything when called. A return type must be specified.
• Name: Method names are required.
• Parameters: Arguments (values) are passed to parameters in methods. Parameters are optional.
Methods can also have parameters. Parameters pass values from arguments into methods. The
parameter refers to the name and the arguments are the values sent to the method. The example below
notes where each is located.
Parameters can also be optional by giving them a default value. In the example below, following
the parameter “name”, the default value “Guest” is assigned to the parameter, making it optional.
class OptionalParemeterExample
{
public static void WelcomeMessage(string name = "Guest")
{
WriteLine($"Hello, {name}");
}
ReadLine();
}
}
P a g e | 36
Full Example: Passing an object to a method (objects and classes covered in the Classes section)
class MethodObjectExample
{
public class Player
{
public string Name { get; set; }
public int Health { get; set; } = 100;
}
Combat.FightBattle(player1);
WriteLine($"{player1.Name} has {player1.Health} health"); // Output: ABC has 80 health
}
}
P a g e | 37
Arguments can be passed by reference, not by value. Pass references using the ref keyword;
changes made in a method for a reference will affect the original value outside the method.
class ExampleMethodPassReference
{
static void PlayerDamaged(ref int playerHealth)
{
playerHealth = playerHealth - 20;
}
ReadLine();
}
}
P a g e | 38
METHODS: OVERLOAD
Overloading a method occurs when methods have the same name but different signatures. The
compiler is smart enough to know which method to use based on what is being called.
METHODS: RECURSION
The concept of Recursion is when something calls itself. When a method calls itself, it is
referred to as a recursive method.
class ExampleRecursive
{
public static string VerifyWord()
{
WriteLine("Choose: Up, Down");
string direction = ReadLine();
class ExampleRecursive
{
// A factorial is the multiplication of every number below it.
// For example: The factorial of 4 equals (1 * 2 * 3 * 4 = 24)
public static double Factorial(int number)
{
if (number == 0)
return 1;
ReadLine();
}
}
When a method invokes another method and the resulting method eventually invokes the
original method, this is called indirect recursion.
using System;
using static System.Console;
class IndirectRecursionExample
{
ReadLine();
}
}
P a g e | 42
Using named arguments helps clarify the parameters used when calling a method. Without named
arguments a long list of numbers can quickly become confusing.
AMethodName(Person: "Matt");
Named argument
class ExampleNamedArguments
{
static void Main(string[] args)
{
// Without named arguments it is not clear what the numbers represent.
CharacterDescription("Bob", 42, 72, 61);
ReadLine();
}
public static void CharacterDescription(string name, int age, int height, int level)
{
WriteLine($"{name}: level {level}, {age} years old, and height of {height} inches.");
}
}
P a g e | 43
CLASSES
A class is a group of related methods and variables with common attributes. A class can be
thought of as a “blueprint” or “template” and objects are instantiated (created) from it. A single class
can instantiate an infinite number of objects.
class Card
{
// Note: The upcoming section “Get-Set Properties” discusses how to properly handle
// fields in a class. Until then, public fields will be used.
Card1.name = "King";
Card1.value = 13;
class ExampleClass
{
static void Main(string[] args)
{
// Example 1: Instantiate an object
Card Card1 = new Card ();
In the example above, the Card class contains the method “DisplayCard()”. In the previous
examples, all methods have started with “public static”. This method leaves out the static keyword.
Static methods are accessed through the class name directly. In this example, the method is accessed
through instances of the class instantiated called “Card1” and “Card2”.
// DeckOfCards List holds objects of type Card (Class created in previous examples)
List<Card> DeckOfCards = new List<Card>()
{
// Create objects of each card needed for a deck of cards
new Card {name = "Ace", value = 1, suit = "Heart"},
new Card {name = "Two", value = 2, suit = "Heart"},
new Card {name = "Three", value = 3, suit = "Heart"}
// Continue adding cards...
};
CLASSES: CONSTRUCTOR
A constructor is essentially a method in a class that executes when the object is instantiated.
The constructor will have the same name as the class and can be overloaded. A constructor cannot have
a return type.
Example: Constructor
class ConstructorExample
{
static void Main(string[] args)
{
Person person1 = new Person(); // Call constructor with no arguments
WriteLine(person1.Name); // Output: Empty
Person person3 = new Person("Adam", "Seebeck"); // Call constructor with two arguments
WriteLine(person3.Name); // Output: Adam Seebeck
ReadLine();
}
}
P a g e | 47
CLASSES: STATIC
The keyword static in static classes denotes that the class is singular; you cannot create an
instance (object) of the class. In a static class all methods and fields must be static.
class ExampleStaticClass
{
static void Main(string[] args)
{
OnlyOneHouse.Height = 10;
OnlyOneHouse.Size = OnlyOneHouse.Height * OnlyOneHouse.Width;
GET-SET PROPERTIES
Previously in this book, fields have been declared public. However, fields should in general be
private and only allow access when needed. This is where Get-Set properties accessors Get or Set are
used to access or change data. Get-Set properties combine aspects of both fields and methods.
The “MyProperty” get section can return the private field value of “myProperty” and the set
section can assign a value to the private field “myProperty”. It is common to name the private field the
same name as the property except with a lower-case letter or with an underscore and then a lowercase
letter, for example “_myProperty”.
class ExampleGetSetPropeties
{
static void Main()
{
Vehicle brand = new Vehicle();
brand.Car = "Toyota"; // Use set in Car property to set private field car to “Toyota”
WriteLine(brand.Car); // Use get in Car property to get value of private field car
// Output: Toyota
ReadLine();
}
}
P a g e | 49
It is possible to add additional logic to a property. In the example below, logic within the get
verifies a number between 1-12 is entered, otherwise 0 is returned.
return theMonth;
}
set
{
theMonth = value;
}
}
}
class ExampleGetSetPropeties
{
static void Main()
{
Month TheMonth = new Month();
TheMonth.TheMonthVerify = 5;
WriteLine(TheMonth.TheMonthVerify); // Output: 5
TheMonth.TheMonthVerify = 13;
WriteLine(TheMonth.TheMonthVerify); // Output: 0
ReadLine();
}
}
P a g e | 50
class ExampleGetSetPropeties
{
static void Main()
{
Vehicle brand = new Vehicle();
brand.Car = "Toyota"; // Use set in Car property to set private field car to “Toyota”
WriteLine(brand.Car); // Use get in Car property to get value of private field car
// Output: Toyota
ReadLine();
}
}
Using get-set properties gives the ability to define how accessible the property is. It may be
tempting to give all properties getters and setters, however in a lot of cases only one is necessary.
Always give as little access as possible.
Another option is to change the accessibility of the set property. In the example below, set has
the private modifier before it, meaning it can only be changed within the class.
class ExamplePrivateSet
{
static void Main(string[] args)
{
Vehicle car1 = new Vehicle(54);
WriteLine(car1.CarID); // Output: 54
ReadLine();
}
}
P a g e | 52
When instantiating an object that contains properties you can assign values to them one at a
time or can assign multiple at the same time using object initializer syntax.
class ExampleObjectInitializerSyntax
{
static void Main(string[] args)
{
// Example: Object Initializer Syntax
Vehicle car1 = new Vehicle { Brand = "Toyota", Color = "Red" };
// Assign 1 at a time
Vehicle car2 = new Vehicle();
car2.Brand = "Ford";
car2.Color = "White";
ReadLine();
}
}
P a g e | 53
CLASSES: INHERITANCE
Classes can inherit from other classes, meaning they acquire all of another class’s features. With
class inheritance, the derived class acquires all the features from a base class. C# only supports single
inheritance, meaning it can only inherit one class at a time.
Example: Inheritance
class ExampleClassInheritance
{
static void Main(string[] args)
{
CarSedanDerivedClass Car1 = new CarSedanDerivedClass();
Car1.Brand = "Tesla";
ReadLine();
}
}
P a g e | 54
C# only supports single inheritance; however, inheritance is transient, meaning it can form a
class hierarchy.
class ExampleClassInheritanceTransient
{
public class BaseClassExample
{
public int TheBaseProperty { get; } = 5;
}
WriteLine(obj1.TheBaseProperty); // Output: 5
}
}
Sealed classes prevent a class from being inherited. In some scenarios using a sealed class will
also provide a performance boost.
METHOD: VIRTUAL
To redefine a method in a derived class, use a virtual method. Virtual methods are redefined
using the “virtual” keyword in the base class and the “override” keyword in the derived class.
class Animal
{
public virtual void Sound()
{
WriteLine("Generic Noise");
}
}
class ExampleVirtualMethod
{
static void Main(string[] args)
{
Animal anyAnimal = new Animal();
ExampleShow1.Sound(); // Output: Generic Noise
ReadLine();
}
}
P a g e | 56
CLASSES: ABSTRACT
Abstract classes are commonly used for inheritance and cannot be instantiated. Abstract
methods within an abstract class must be contained in the child class or the program will not compile.
Virtual methods are not required in child classes.
class ExampleAbstractClass
{
static void Main(string[] args)
{
SpanishLanguage languageSelected = new SpanishLanguage();
languageSelected.Greeting(); // Output: Hola
languageSelected.Goodbye(); // Output: Goodbye!
}
}
P a g e | 57
CLASSES: PARTIAL
Partial classes can be split across two or more source files. Each file source will contain a section
of the class and all parts will be combined when the compiled. The “partial” keyword is used for classes,
structs, and interfaces, and means that other parts may be defined within the same namespace.
class ExampleClassInheritance
{
static void Main(string[] args)
{
Employee Employee1 = new Employee();
Employee1.Name = "John";
Employee1.WelcomeMessage(Employee1.Name);
// Output: John, welcome to our employee information center
ReadLine();
}
}
While you can split up larger classes into partial classes, in most cases creating new classes with
more specific sections is advised. Partial classes are generally used for situations that involve “owner”
boundaries, such as with a GUI (Graphical User Interface). The GUI has a framework associated with it
with it and using partial classes will ensure that the framework does not overwrite any of your code.
P a g e | 58
• Public: Can be accessed by any other code in the same assembly or another assembly that
references it.
• Protected: Can be accessed only by the code in the same class or in a class that is derived from
that class.
• Private: Can be accessed only by code in the same class.
ReadLine();
}
}
P a g e | 59
When a variable is declared, a portion of memory is allocated in RAM. This portion of memory
contains the name, data type, and value of the variable. Depending on the data type, the portion of
memory allocated will be either stack or heap memory.
The stack is used to keep track of local variables and the program’s state. The heap is used to
store data that can be accessed anytime and from anywhere in your program.
Data types are either value types or reference types. The most common reference types are:
strings, arrays, and objects which are on the heap; everything else is a value type on the stack.
Data Types
Stack Heap
Value Types Reference Types
Simple
Numeric Bool
Reference types can be assigned a null reference, meaning a reference can point to nothing at
all. This is accomplished using the “null” keyword. Assigning null to value types is not allowed.
P a g e | 60
class StackAndHeapExamples
{
static void Main(string[] args)
{
int a = 7; // Value type, Stack
int b = a; // Value type, Stack
bool c = true; // Value type, Stack
When data moves from value type to a reference type, it is called boxing, and going from
reference type to value type is called unboxing.
Example: Boxing
Stack Heap
Example: Unboxing
Stack Heap
STRUCTS
A struct is similar to a class and is useful for lightweight objects, such as point, rectangle, and
color. Lightweight objects can also be created with classes, however it is often more memory efficient to
use a struct. Unlike classes, structs are value types and not reference types like classes.
Example: Struct
class ExampleStruct
{
static void Main(string[] args)
{
TheLocation theLocation1 = new TheLocation();
WriteLine($"Location1: x = {theLocation1.X}, y = {theLocation1.Y}");
// Output: Location1: x = 0, y = 0
ReadLine();
}
}
P a g e | 63
A struct can be declared without using the keyword new. This is unique to structs and this would
not work if it was a class. All value types (Int, Char, Bool, etc.) are structs, which is how you can declare
them without using the keyword “new”.
class ExampleStructWithoutNew
{
static void Main(string[] args)
{
// Previous way
// TheLocation theLocation2 = new TheLocation(23, 6);
TheLocation theLocation3;
theLocation3.X = 23;
theLocation3.Y = 6;
ReadLine();
}
}
P a g e | 64
A namespace helps organize a large group of related code. To use a namespace, call it with the
using directive or include it before the class name.
Example: Namespace
namespace NamespaceExampleConsole
{
// empty Namespace
}
The example below uses the “System” namespace. Within the System namespace there is a
class called Console, and within the Console class there is the method WriteLine.
using System;
class ExampleNamespace
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// If “using System;” was not used, the fully qualified name must be used
System.Console.WriteLine("Hello World!");
}
}
The static members of a static class can be accessed without having to specify the type name,
meaning “Console.WriteLine()” can be simplified to just “WriteLine()”. This is done with the “using
static” directive.
class ExampleNamespace
{
static void Main(string[] args)
{
WriteLine("Console.WriteLine() is simplified to just WriteLine()");
ReadLine(); // Console.Readline() is simplified to just ReadLine();
}
}
P a g e | 65
namespace Automobiles
{
static class Cars
{
public static string theCar = "ToyotaCar"; // Car info
}
class Trucks
{
// Empty class to demonstrate namespaces can contain multiple classes
}
}
class ANamespaceExample
{
static void Main(string[] args)
{
WriteLine(Automobiles.Cars.theCar);
ReadLine();
}
}
In the example above, within WriteLine “Automobiles.Cars.theCar” the full name had to be
used. However, if the static class directive is used for “Automobiles.Cars”, this can now be shortened to
just “theCar” as demonstrated in the example below.
namespace Automobiles
{
static class Cars
{
public static string theCar = "Toyota Car"; // Car info
}
}
class ANamespaceExample
{
static void Main(string[] args)
{
WriteLine(theCar);
ReadLine();
}
}
P a g e | 66
GENERICS
Generics define type-safe classes without committing to any specific data types. They are
essentially a placeholder until a specified data type is declared. There are a lot of prebuilt classes using
generics. This section covers how to create generic classes. The next section, “List”, demonstrates how
to use a prebuilt generic class.
The example below demonstrates the use of the generic type parameter, which uses the angle
brackets “<>”. Generic type names are usually a single capital letter, “T” being the most common, or a
simple name starting with “T” such as “TKey” or “TValue”.
class AGenericClass<T>
{
// Empty generic class
}
Assume in the next example it is being instantiated with a string in the following way:
class AGenericClass<T>
{
private T aVariable;
In the example above, the generic type parameter “T” uses string as the data type as that is how
the class was instantiated. If the class was instantiated with the data type int then all the corresponding
parameters would have used int instead.
P a g e | 67
class AGenericClass<T>
{
private T aVariable;
class ExampleGenericClass
{
static void Main(string[] args)
{
AGenericClass<int> intExample = new AGenericClass<int>(5);
// Output: The value is 5 and is a System.Int32
ReadLine();
}
}
P a g e | 68
LIST
Lists and arrays are similar in that they both hold items. In general, lists are used more often,
because they can easily search, sort, add, and remove items because of built-in functionalities. Lists can
constantly be changed to include more or fewer items, unlike an array that has a set number of items.
To use a list, add the following line of code below “using System”:
using System.Collections.Generic;
The code above gives access to the Generic namespace which includes the generic List class.
Because the example is using generics, specify the data type of the List class, such as string, int, or
double.
foods.Insert(5, "Cake");
// Pushes back all items after index 5, items in front of 5 stay the same.
foods.Remove("Pizza");
P a g e | 69
foods.RemoveAt(5);
WriteLine(foods[0]);
WriteLine(foods.Count);
foods.Sort();
foods.Clear();
using System.Collections.Generic;
using static System.Console;
class ExampleList
{
static void Main()
{
// Instantiate and assign items to a list
List<string> foods = new List<string>()
{
"Pizza",
"Burger",
"Hot Dog"
};
LINKEDLIST
A LinkedList is generally slower than a regular list, however it can be beneficial when
adding/removing items in the middle of a list. As with lists, a LinkedList also uses generics.
To use a LinkedList, add the following line of code below “using System”:
using System.Collections.Generic;
Example: LinkedList
using System.Collections.Generic;
using static System.Console;
class ExampleLinkedList
{
static void Main(string[] args)
{
LinkedList<string> ingredientsOrder = new LinkedList<string>();
ingredientsOrder.AddLast("Add an egg");
ingredientsOrder.AddLast("Add butter");
ingredientsOrder.AddFirst("Get a bowl");
DICTIONARY
A dictionary consists of a key and a value. In an array, the key is created automatically (0, 1, …);
with dictionaries the key name can be set to something meaningful. As with lists, dictionaries also use
generics.
To use a dictionary, add the following line of code below “using System”:
using System.Collections.Generic;
Dictionaries have two generic data types parameters. Because they are generic, any data type
can be entered. In the example below, the first generic data type is a string and is used as the key. The
second generic data type is an int and is used for the value.
myInventory.Remove("Pens");
P a g e | 73
myInventory["Computers"] = 6;
WriteLine($"Key: {myInventory["Computers"]}");
To loop through a dictionary, use a ForEach loop and the keyword “KeyValuePair<TKey,
TValue>”. This is needed because dictionary elements are retrieved as KeyValuePair objects.
WriteLine(myInventory.Count);
myInventory.Clear();
P a g e | 74
using System.Collections.Generic;
using static System.Console;
class ExampleDictionary
{
static void Main()
{
Dictionary<string, int> myInventory = new Dictionary<string, int>
{
{ "Pens", 7 },
{ "Computers", 2 }
};
WriteLine(myInventory["Pens"]); // Output: 7
ReadLine();
}
}
P a g e | 75
ENUMERATION
An enum (enumeration) is a new value type that consists of a collection of named constants.
Using enums creates a modular design that enhances clarity and reduces the probability of invalid
constants. Enums are generally defined directly in the namespace (outside of any class).
if (PlayerDirection == Direction.North)
WriteLine("The player heads north");
else if (PlayerDirection == Direction.East)
WriteLine("The player heads east into the wood.");
using System;
using static System.Console;
namespace EnumExample
{
enum Direction { North, East, South, West };
class EnumExample
{
static void Main(string[] args)
{
Direction PlayerDirection = Direction.North;
if (PlayerDirection == Direction.North)
WriteLine("The player heads north");
else if (PlayerDirection == Direction.East)
WriteLine("The player heads east into the wood.");
ReadLine();
}
}
}
By default, enum values start at 0. However, numbers can be assigned to enum values.
class EnumExample
{
static void Main(string[] args)
{
Months value = Months.February;
int monthNumber = (int)value;
WriteLine(monthNumber); // Output: 2
ReadLine();
}
}
}
P a g e | 77
TERNARY OPERATOR
The ternary operator tests a condition by comparing two values and returns a third value based
on the results. A ternary operator can often simplify a traditional “If-Else” statement.
Ternary Format:
The example below demonstrates how the ternary operator can be used to get the same result
with less coding.
class TernaryInAMethodExample
{
RANDOM NUMBER
It is often necessary to create a random number in software applications. Below are several
techniques on how to create a random number using the Random class.
// The Next() method within the r object returns a number from 0 to Int32.MaxValue
int myRandomNumber = r.Next();
// The Next method will return a non-negative random integer that is less than the
// specified maximum value supplied.
int myRandomNumber = r.Next(11); // Output: Will vary from 0 to 10
using System;
class RandomNumberExample
{
private static Random r = new Random(); // Instantiate object r from the Random class
public static int RandomNumMethod(int num1)
{
return r.Next(1, num1);
}
THIS (REFERENCE)
The “this” keyword refers to the current instance of the class; it helps to qualify members
hidden by the same name. The “this” keyword can also be used to pass the entire instance of the
current class to a parameter as an object.
Example: this
class ThisExample
{
static void Main(string[] args)
{
NameClass example1 = new NameClass();
example1.DisplayName("David");
ReadLine();
}
}
P a g e | 81
METHODS: EXTENSION
Extension methods define a method that appears to belong to a class to which it does not in
fact belong. Extension methods must be defined in static classes and be static methods.
The built in string class contains methods such as: “ToLower()” and “Length()”. In the example
below, an extension method called “ToStarBox()” is created and can be used like the previous string
methods mentioned. It will appear to be in the string class, when in fact it is not.
Extension method parameters must start with the keyword “this” followed the type that is being
created, in our example the string data type.
WriteLine(starLine);
WriteLine($"*{text}*");
WriteLine(starLine);
}
}
WriteLine(starLine);
WriteLine($"*{text}*");
WriteLine(starLine);
}
}
class ExtensionMethodExample
{
static void Main(string[] args)
{
string aWord = "Hello";
aWord.ToStarBox();
// Output: *******
// *Hello*
// *******
ReadLine();
}
}
P a g e | 83
TUPLES
A Tuple can be thought of much like an array, however a Tuple collection can contain different
data types. Tuple classes are generic containers and can hold between 1 to 8 items.
Tuple<int, string, int[]> myTuple = Tuple.Create(101, "Hello", new int[] { 41, 52 });
To access the information within a Tuple, use the name of the Tuple and then “.Item1”, “.Item2”
and so on.
STRINGBUILDER
When the value of a string is changed, a new string object in memory is created. This is because
a string is immutable (meaning cannot be changed once created). This uses additional system resources.
StringBuilder is a mutable string-like object whose value will change based on the sequence of
characters (a new string object in memory is not created when appended).
using System.Text;
using System.Text;
using static System.Console;
class ExampleStringBuilder
{
static void Main(string[] args)
{
StringBuilder theBuilder = new StringBuilder("Hello World!");
theBuilder.Replace("World", "Jason");
WriteLine(theBuilder); // Output: Hello Jason! Today is going to be great!
ReadLine();
}
}
P a g e | 86
TRY-CATCH
Try-Catch is used in scenarios that might throw an exception. Exception handling is the name
used for errors in object-oriented programs, meaning if there is an exception within the try section of
code, the program will go directly to the catch block of code. The catch block can specify the type of
exception being looked for. In the example below, this will handle all exceptions.
Example: Try-Catch
try
{
// Code that could potentially cause an exception
}
catch (Exception ex)
{
// Handle the exception
}
Finally can optionally be added to the end of a Try-Catch. The code in the finally statement will
always run. A common use for finally is if a file is open, finally can guarantee that the file will be closed
even if an error occurs.
try
{
// Code that could potentially cause an exception
}
catch (OverflowException ex)
{
// Handle the exception if it is an OverflowExcpetion
}
catch (Exception ex)
{
// Handle all other exceptions
}
finally
{
// This code will always execute if there is or isn't an exception.
}
P a g e | 87
Full Example: Try-Catch handles an exception if incorrect data is entered, such as: “asdf”
using System;
using static System.Console;
class TryCatchExample
{
static void Main(string[] args)
{
try
{
int theDay;
int theMonth;
int theYear;
WriteLine(displayDate);
}
catch (Exception ex)
{
WriteLine($"Exception: {ex.ToString()}");
}
finally
{
// Finally can optionally be added to a Try-Catch.
}
ReadLine();
}
}
P a g e | 88
UNIT TESTING
Unit Tests verify that the logic of code is working as expected. Unit testing breaks down a
program into small, testable, individual units.
The objective of this program is to build a calculator that adds two numbers. Unit Testing will be
included to validate that the program performs as designed.
using System;
namespace UTCalculator
{
class Program
{
static void Main(string[] args)
{
}
}
}
P a g e | 89
The code above will run, because there are no syntax errors in it. However, there is a logic error
(10 + 4 should equal 14 not 6). A Unit Test will be added to catch this type of error.
The template for a xUnit Test has now been created. Next a reference needs to be added for the
Unit Test to be connected to UTCalculator.
In the Solution Explorer expand CalculatorTest, right click on Dependencies, and click Add
Reference. The Reference Manager will open. On the left menu, click on Projects -> Solution and add a
checkmark next to UTCalculator. Click OK.
P a g e | 90
[Fact]
public void TestAddMethodResultShouldBeNine()
{
int result = CalculatorFeatures.AddTwoNumbers(5, 4);
Assert.Equal(9, result);
}
Within the test “Assert.Equal(9, result)” is used to compare the expected result with the answer
that the method response returns. When the test is run if 9 matches the result, it will pass the test,
otherwise it will fail.
using Xunit;
using UTCalculator; //ADD this to access UTCalculator class
namespace CalculatorTest
{
public class UnitTest1
{
[Fact]
public void TestAddMethodResultShouldBeNine()
{
int result = CalculatorFeatures.AddTwoNumbers(5, 4);
Assert.Equal(9, result);
}
}
}
P a g e | 91
On the top menu bar select Test -> Run -> All Test. The test will fail. If the Test Explorer did not
come up, select Test -> Windows -> Test Explorer.
Click on the failed test for more information. A message will say “Equal Failed, expected 9 and
Actual was 1”. This indicates a logical error in the “AddTwoNumbers” method.
It is now clear that the numbers are being subtracted: “int result = num1 - num2;”
Change the minus sign to a plus sign: “int result = num1 + num2;”
With xUnit tests, they are either marked with the attribute [Fact] or [Theory]. Fact indicates
a test, and Theory gives the ability to pass multiple data tests to the same unit test using InlineData. In
the previous Unit Test walkthrough, the Assert class was utilized to compare results. The Assert class
contains various methods that return a bool result. In the following examples “Assert.Equal” will be
used.
Below is the class and method with which the xUnit Test will correspond. This is the same code
previously used in the Unit Testing walkthrough.
[Fact]
public void TestAddMethodResultShouldBeNine()
{
int result = CalculatorFeatures.AddTwoNumbers(5, 4);
Assert.Equal(9, result);
}
[Theory]
[InlineData(5, 4, 9)]
[InlineData(6, 1, 7)]
[InlineData(2, 3, 5)]
public void AddTwoNumbersAndGetResult(int firstNum, int secondNum, int expectedResult)
{
int result = CalculatorFeatures.AddTwoNumbers(firstNum, secondNum);
Assert.Equal(expectedResult, result);
}
P a g e | 93
DIRECTIVES
Preprocessor directives are useful in improving readability, reducing complexity, and helping
with maintenance of a program.
• #region is used to indicate a certain block of code. This will help keep your code organized as
blocks and can be expanded or collapsed in Visual Studio.
#region DisplayPrompts
WriteLine("Hello");
WriteLine("World");
#endregion
#define DevMode
using static System.Console;
class ExampleDirectives
{
static void Main(string[] args)
{
string myDatabase = "";
#if (DevMode)
{
WriteLine("In development Mode, using local database");
myDatabase = "C:\\LocalDatabase....";
}
#else
{
WriteLine("In development Mode, using local database");
myDatabase = "C:\\LocalDatabase...."
}
#endif
}
}
Other: #warning, #error, #line, #region, #endregion, #pragma, #pragma warning, #pragma checksum
P a g e | 94
Variables are temporary in that once the program is turned off, the data is lost. Writing/Reading
to a text file is a permanent way to handle data that needs to be saved when the program is turned off.
The StreamWriter class writes to text files. This class in contained within “System.IO”
namespace. To use this class, include the following at the top of the page.
using System.IO;
A “using” statement is also used in front of StreamWriter. The using statement removes the
SteamWriter from memory when it is no longer needed.
Full Example: Write to a file; the file will be saved in the folder of your current project
using System;
using System.IO;
class TextFileExample
{
static void Main(string[] args)
{
string fileName = "test.txt";
string textToAdd = "Example text to save";
The ability to read from this file is also needed, and for that use StreamReader, which is set up
with a very familiar syntax to StreamWriter.
WriteLine(wholeTextfile);
}
P a g e | 95
INTERFACES
An interface behaves as a contract between itself and any class where it is used. A class that
implements an interface must now implement all its members. Interfaces can only contain declarations
and all the members are implicitly abstract and public. Interfaces are usually named with a capital “I”,
although it is not required.
Example: Interface
interface IPets
{
void Greeting();
string Name { get; set; }
}
There are useful built-in interfaces, such as: IEnumerable, IList, IDictionary, and IComparable.
Adding the interface IEnumerable to a class means it will iterate. The class must also now contain
IEnumerator. In the example below, a class for a deck of cards is created and iterated over.
using System.Collections;
using System.Collections.Generic;
using static System.Console;
Suit = suit;
}
}
public DeckOfCards()
{
deckList.Add(new Card("Two", 2, "Spade"));
deckList.Add(new Card("Three", 3, "Spade"));
deckList.Add(new Card("Four", 4, "Spade"));
}
class ExampleIEnumerable
{
static void Main(string[] args)
{
DeckOfCards theDeck = new DeckOfCards();
ReadLine();
}
}
C# does not allow multiple inheritance in classes; however, multiple interfaces are allowed. If
interfaces are added to a class that is also inheriting from a base class, the interfaces must follow the
base class.
YIELD
Yield return and yield break are used when implementing an iterator (IEnumerable). The yield
return statement returns the next element in the sequence, whereas yield break ends the iteration.
using System.Collections.Generic;
using static System.Console;
class ExampleYieldReturnAndBreak
{
public static List<int> MyNumbers = new List<int> { 9, 4, 20, 3, 7, 12 };
ReadLine();
}
}
P a g e | 98
DELEGATES
Delegates are a variable that can store a method and can then be passed around as needed.
Previously the variables discussed held data such as strings or objects, however methods can be treated
the same way. In the example below, a delegate named “MathExample” can store methods that accept
two integer values and return a string value.
class DelegateExample
{
public delegate string MathExample(int num1, int num2);
ReadLine();
}
}
P a g e | 99
class DelegateExample
{
public delegate string MathExample(int num1, int num2);
ReadLine();
}
}
P a g e | 100
EVENTS
Events allow classes to notify other classes when something occurs. GUI (Graphical User
Interfaces) applications often use Events to indicate when something occurs. One GUI example is when a
button is pressed and other classes are listening to handle that event.
There are multiple components to an event. Each of the examples below cover one of these
components. Following all the examples is a full example that has all the components put together.
An event needs to be defined inside a class. Using the “event” keyword enables others to attach
to this event. Specify the delegate type for the type of methods that can be attached to the event. In the
example below, a predefined delegate called “EventHandler” is used. The EventHandler delegate returns
void and has two parameters (object, EventArgs). Object is the sender (what sent the event) and
EventArgs object (contains basic information about the event).
The code below verifies that an event handler is attached to an event. Methods that raise events
typically start with the word “On”.
In the example below, when “TheMessage” value is set, then it will call the method
“OnWelcomeChanged” to raise the event.
The method below handles what to do when the change is detected. This method needs to be
setup based on the delegate used, in this case the “EventHandler” delegate.
Below is how to attach an event handler to an event; the “+=” operator is used to accomplish
this. You can attach multiple event handlers to an event if needed.
welcomeMessage.WelcomeChanged += welcomeMessage.HandleWelcomeChanged;
P a g e | 102
using System;
using static System.Console;
class Greetings
{
private string theMessage;
public string TheMessage
{
get { return theMessage; }
set
{
theMessage = $"Hello, {value}";
OnWelcomeChanged(); // Call OnWelcomeChanged when the value is changed
}
}
public void OnWelcomeChanged() // Methods that raise events usually start with the "On"
{
// The code below raises the event if the event is not null (verifies an event handler
// is attached to the event)
WelcomeChanged?.Invoke(this, EventArgs.Empty);
}
class EventExample
{
static void Main(string[] args)
{
Greetings welcomeMessage = new Greetings();
welcomeMessage.TheMessage = "Adam";
WriteLine(welcomeMessage.TheMessage);
ReadLine();
}
}
P a g e | 103
LAMBDAS
A lambda is essentially a method without a declaration; it uses a clear and short syntax. Write a
shorthand method directly in the place it is intended to be used. This is especially useful on small
methods that are used only once. The lambda operator is “=>” and can be read as “goes to”. The lambda
operator separates the expression into two parts (left side parameter, right side lambda body).
Example: Lambda
using System.Collections.Generic;
using static System.Console;
class LambdaExample
{
static void Main(string[] args)
{
List<int> list = new List<int>() { 5, 3, 4, 5, 6, 7 };
The lambda expression syntax can also be used on various expression-bodied members that are
a single expression. In the example below, a single expression method is simplified using the lambda
expression syntax.
class ExpressionBodiedExample
{
// “Regular” Method
public static int SquareNumber1(int number)
{
return number * number;
}
ReadLine();
}
}
P a g e | 105
THREADS
Threads add the functionality of running multiple sections of code simultaneously. Threading
allows the code to run on multiple processors. This provides a performance boost.
using System.Threading;
The “Thread” class constructor used below contains a delegate that accepts methods that return
void; only methods that return void can be used.
In the example below, the “Start” method is used and it can accept zero or one parameter.
aThread.Start(); // No parameters
bThread.Start(50); // 1 parameter
using System.Threading;
using static System.Console;
class ThreadExample
{
public static void CountTo50()
{
for (int i = 1; i <= 50; i++)
WriteLine(i);
}
ReadLine();
}
}
P a g e | 106
The previous example uses the “Start” method to create a new thread that will begin executing
the method “CountTo50”. At this point the two threads are now running simultaneously, the original
thread and now the new “aThread” as well.
In the example below, the “Join” method instructs the original thread to wait until “aThread”
has completed. The original thread will be “frozen” until it finishes, essentially joining the two threads.
Example: Join
aThread.Join();
using System.Threading;
using static System.Console;
class ThreadExample
{
public static void CountTo50()
{
for (int i = 1; i <= 50; i++)
WriteLine(i);
}
When the example above is run, the results will not output in sequence 1-200 each time. Both
threads are running at the same time and will each output their numbers as their thread is being run.
For example, the results might be 1-70 and then the other thread could output 1-50, then the first
thread could output 71-120 and so on.
P a g e | 107
The example below demonstrates using a parameter in the “Start” method. It is important to
note that the method parameter must be an object. The object can later be cast. In the example below,
it is cast from an object to an int.
using System.Threading;
using static System.Console;
class ThreadExample
{
public static void CountTo(object count)
{
for (int i = 1; i <= (int)count; i++)
WriteLine(i);
}
ReadLine();
}
}
P a g e | 108
ASYNCHRONOUS
A method using the async modifier enables the use of the await operator, which now must be
included at least once within the method. When the await operator is reached, the original caller
method will continue and the async method will continue to process until it is completed.
Async methods must have a return type of void, Task, Task<T>, or any other type that has a
GetAwaiter method. The naming convention for async methods is to append them with an “Async”
suffix.
class ExampleAsync
{
static void Main(string[] args)
{
FirstMethodAsync();
WriteLine("System is not Frozen");
ReadLine();
}
In the example above, the method “FirstMethodAsync()” contains a delay that would generally
“freeze” a program for 3 seconds. Because this is an async method, when await is reached the original
caller of the method will continue and the async method will keep processing.
P a g e | 109
Async methods can return with void as demonstrated previously, however they generally return
with Task or Task<TResult>, which encapsulate information. Task is used if there is no return statement,
and Task<TResult> is used if there is a return statement.
using System.Threading.Tasks;
When the task return type is used instead of void, the method call can now use the await.
await SecondMethodAsync();
class ExampleAsync
{
static void Main(string[] args)
{
FirstMethodAsync();
WriteLine("System is not Frozen");
ReadLine();
}
class ExampleAsync
{
static void Main(string[] args)
{
FirstMethodAsync();
WriteLine("System is not Frozen");
ReadLine();
}
It is very common to see async programming on graphical applications. For example, if a button
is pressed and content from the internet needs to be downloaded, the whole program should not be
frozen while it downloads. The program should download the content in the background while the user
is still able to use the application.
P a g e | 111
A query expression statement extracts specific information from a collection of data. The query
syntax in C# is called “Language Integrated Query”, commonly referred to by its shortened name,
“LINQ”.
LINQ queries are used on collections of data, such as Lists or Arrays, which are container types
for the “IEnumerable<T>” interface. Any container type of “IEnumerable<T>” can use a LINQ query.
To use a LINQ query, add the following line of code below “using System”:
using System.Linq;
In the examples below, the various clauses that make up a query are covered. Clauses are a
smaller syntax than a full expression; LINQ queries require multiple clauses to be a complete expression.
Queries always start with a “from” clause. The from clause defines a source of information for
the query and a range variable. A range variable is a local variable that is available throughout the query,
like the variable in a “ForEach” loop.
from o in students
The select clause specifies the part or whole object to be used for the results of the query
expression. The example below demonstrates selecting the entire object.
select o;
The results from a LINQ query are always output as “IEnumerable<T>” and can be converted to
a list or array if needed using “ToList()” or “ToArray()” .
Queries can be created using only “from” and “select” clauses; the “where” clause specifies
which items to use.
The example below uses the “let” clause which creates derived range variables. A derived range
variable uses the range variable created in the “from” clause. “Let” clauses define a variable to use in
multiple places in the query or to simplify a complex operation. In the example below, “select” also
selects a string instead of an object, and IEnumerable is modified to reflect this change.
A “join” clause combines two collections together based on a condition. In the example below,
the collections are joined together when the “StudentID” values are equal in both collections. The left
side of the equals must reference an earlier ranger variable and the right side references a new range
variable; this is called an inner join.
The “OrderBy” clause sorts all items in a collection. The default is ascending, however they can
also be specified for descending.
The “group” clause bundles elements into groups based on the information specified. In the
example below, the books collection will have a group for each “BookName”. Previously the “select”
clause has been needed to finish a full expression, however the “group” clause is the other way to finish
a full expression.
using System.Collections.Generic;
using System.Linq;
using static System.Console;
class StudentInfo
{
public int StudentID { get; set; }
public string Name { get; set; }
public int Grade1 { get; set; }
public int Grade2 { get; set; }
}
class BookInfo
{
public int StudentID { get; set; } = 0;
public string BookName { get; set; }
}
class QueryExample
{
static void Main(string[] args)
{
// Lists, students, and books will be used as our collections for this example
List<StudentInfo> students = new List<StudentInfo>
{
new StudentInfo {StudentID = 1, Name = "Jonathan", Grade1 = 95, Grade2 = 90},
new StudentInfo {StudentID = 2, Name = "Maria", Grade1 = 92, Grade2 = 85},
new StudentInfo {StudentID = 3, Name = "Marcos", Grade1 = 81, Grade2 = 91}
};
// Where specifies which items to use, in this example all Grade1 greater than 90
IEnumerable<StudentInfo> students2 = from o in students
where o.Grade1 > 90
select o;
ReadLine();
}
}
P a g e | 116
Method call syntax provides the same results as the query syntax covered in the previous
section. All the clauses previously covered are also available through these simple method invocations
that use lambda expressions.
DATABASE
Databases store a collection of information in an organized way that can easily be added,
deleted, and updated. In the following example, a local SQL database is created to demonstrate CRUD
(Create, Read, Update, Delete) of a database.
1. Create a Console Application in Visual Studio: File -> New -> Project -> Visual C# -> Console App
(.NET Core). On the bottom of the New Project page change the Name to “StudentDB” and
change the Location to where you want to save the file.
class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Grade { get; set; }
}
5. Create an additional class in the People folder called “StudentDBContext”. This class will manage
the connection to the database.
6. The class StudentDBContext needs to inherit from the Entity Framework class DBContext.
a. Update the class to “class StudentDBContext : DbContext”.
b. To use DBContext, add “using Microsoft.EntityFrameworkCore;” to the top of the page.
7. To handle the information from the Student class model previously created, a DbSet is used.
8. The connection string is added to connect to the database. The class should now look like the
example below.
// Connect to database
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\MSSqLLocalDB;Database=StudentDatabase;Trus
ted_Connection=True;");
}
}
P a g e | 119
9. Use migration to create the database. The purpose of migration is to keep the database in sync
with the models.
a. Click on Tools -> Nuget Package Manager -> Package Manager Console.
b. In the Package Manager Console input “Add-Migration InitialMigration”. This scaffolds
the initial set of tables for the model. The database hasn’t been created yet, but this is
the schema it will use.
c. After pressing enter, notice that a new folder called “Migrations” was added to the
project. This folder contains the schema and other information the database will need.
d. In the Package Manager Console input “Update-Database”. The database isn’t created
yet; this will create the database.
10. Verify the database by using SQL Server Object Explorer. If it is not currently on the left side of
Visual Studio, then click on View -> SQL Server Object Explore. The database will be located at
SQL Server -> (localdb).. -> Databases -> StudentDatabase.
11. View the table created by expanding the StudentDatabase -> Tables and then right click on
dbo.Students -> View Data.
12. It is now possible to input data directly into the database table. Enter in a Name and Grade.
Make sure the Grade is a number. Notice a new row is created when data starts to be input and
multiple records can be entered. However, data is not usually input directly into the database.
13. Below is an example of adding items to a database by code. Adding this code snippet to the
Main method will add the two students every time the application is loaded.
In the example below, a “ForEach” loop is used to loop through an entire database table and
display the results.
The example below will query the database table using the where keyword. When the “where”
keyword is used only results that match the search parameters will be returned.
Using the contains keyword can provide flexibility to a search. For example, if a user searches
“Ces”, then the results will contain names that contain “Ces” such as “Cesar”.
Example: Contains
Using the OrderBy keyword sorts the results numerically and alphabetically.
Example: OrderBy
Other popular keywords are Average, Sum, Max, and Min. An example on how to get the Max
value is below.
To remove items from a table, use RemoveRange or Remove. After removing the items,
SaveChanges must be called to update the database.
Use the select keyword to specify which columns are needed. This improves the speed of the
results returned.
Using the join keyword combines information from two separate tables based on a common
attribute between the two. For example, table 1 contains a student with ID “5”, Name “Cesar”, and
Grade “98”. Table 2 contains StudentID “5” and age “25”. If ID matches StudentID, put the information
together (Name, Grade, and Age)