Overview
This chapter introduces you to the basics of C#. You will start by learning about the basics of the .NET Command-Line Interface (CLI) and how to use Visual Studio Code (VS Code) as a basic Integrated Development Environment (IDE). You will then learn about the various C# data types and how to declare variables for these types, before moving on to a section about arithmetic and logical operators. By the end of the chapter, you will know how to handle exceptions and errors and be able to write simple programs in C#.
C# is a programming language created in the early 2000s by a team at Microsoft led by Anders Hejlsberg, who is also among the creators of some other popular languages, such as Delphi and Turbo Pascal, both widely used in the 1990s. Over the last 20 years, C# has grown and evolved, and today it is one of the most widely used programming languages globally, according to Stack Overflow's 2020 insights.
It has its reasons for holding such an honorable place in the tech community. C# allows you to write applications for a wide segment of markets and devices. From the banking industry, with its high-security standards, to e-commerce companies, which hold enormous volumes of transactions, it is a language trusted by companies that need both performance and reliability. Besides that, C# also makes it possible to write web, desktop, mobile, and even IoT applications, allowing you to develop for almost every kind of device.
C# was initially limited to work only on Windows; however, there have been concerted efforts by the C# team over the past few years to make it cross-platform compatible. Today, it can be used with all major OS distributions, namely, Windows, Linux, and macOS. The goal is simple: to develop, build, and run C# anywhere, letting each developer and team choose their most productive or favorite environment.
Another remarkable characteristic of C# is that it is a strongly typed programming language. You will dive into this more deeply in the upcoming sections, and you will see that strong typing enables better data security while programming.
Besides that, C# has become open source over the last few years, with Microsoft as its principal maintainer. This is highly advantageous, as it allows the language to receive continuous improvements from around the globe, with a solid backing company that both promotes and invests in it. C# is also a multi-paradigm language, meaning that you can use it to write software in many programming styles, in a beautiful, concise, and proper manner.
One term you'll hear a lot in the C# world is .NET. It is the foundation of C#, a framework that the language is built on top of. It has both a Software Development Kit (SDK) that allows the language to be developed and a runtime that allows the language to run.
That said, to start developing with C#, you only need to install the .NET SDK. This installation will provide both a compiler and the runtime on the development environment. In this section, you will cover the basic steps of preparing your environment for developing and running C# locally.
Note
Please refer to the Preface of this book for step-by-step instructions on how to download the .NET 6.0 SDK and install it on your machine.
Once the installation of the .NET 6.0 SDK is completed, you will have something called the .NET CLI. This Command-Line Interface (CLI) allows you to create new projects, compile them, and run them with very simple commands that you can run directly from your terminal.
After the installation, run the following command on your favorite terminal:
dotnet --list-sdks
You should see an output like this:
6.0.100 [/usr/local/share/dotnet/sdk]
This output shows that you have the 6.0.100 version of the SDK installed on your computer. That means you are ready to start developing your applications. If you type dotnet -–help, you will notice that several commands will appear for you as options to run within the CLI. In this section, you will cover the most basic ones that you need to create and run applications: new, build, and run.
The dotnet new command allows you to create a bootstrap project to start developing. The CLI has several built-in templates, which are nothing more than basic bootstraps for various types of applications: web apps, desktop apps, and so on. You must specify two things in the dotnet new command:
The name is passed as an argument, which means you should specify it with a -n or –name flag. The command is as follows:
dotnet new TYPE -n NAME
For instance, to create a new console application named MyConsoleApp you can simply type:
dotnet new console -n MyConsoleApp
This will generate a new folder with a file named MyConsoleApp.csproj, which is the C# project file that contains all the metadata needed by the compiler to build your project, and some files needed for the application to be built and run.
Next, the dotnet build command allows you to build an application and make it ready to run. This command should be placed only in two locations:
Solution (.sln) files are files that contain the metadata of one or more project files. They are used to organize multiple project files into single builds.
Finally, the third important command is dotnet run. This command allows you to properly run an application. It can be called without any arguments from the folder that contains the .csproj file of your .NET app, or without passing the project folder with the -–project flag on the CLI. The run command also automatically builds the application prior to the run.
While working through this book, you will use Visual Studio Code (VS Code) as your code editor. It works on all platforms, and you can download the version for your OS at https://code.visualstudio.com/. Although VS Code is not a complete Integrated Development Environment (IDE), it has a lot of extensions that make it a powerful tool to develop and do proper C# coding, regardless of the OS being used.
To properly develop C# code, you will primarily need to install the Microsoft C# extension. It equips VS Code with the ability to do code completion and identify errors and is available at https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp.
Note
Before proceeding, it is recommended that you install VS Code and the Microsoft C# extension. You can find a step-by-step breakdown of the installation process in the Preface of this book.
In order to run, every C# program needs something called an entry point. In C#, the standard entry point for a program is the Main method. Regardless of your program type, whether it is a web application, desktop application, or even a simple console one, the Main method will be the entry point for your C# program. This means that each time an application runs, the runtime searches for this method within your code and executes the code blocks inside it.
This structure is created for you by the CLI, with the new command. A Program.cs file contains a class named Program, with a method named Main, which, in turn, contains a single instruction that will be executed after the program is built and running. You will learn more about methods and classes later, but for now, just know that a class is something that usually contains a set of data and that can perform actions on this data through these methods.
Another important thing to note regarding basic C# concepts is comments. Comments allow you to place free text inside C# code files, without affecting the compiler. A comment section should always start with //.
In this exercise, you will see the CLI commands you learned about in the previous section, as you build your first ever C# program. It will be a simple console app that will print Hello World to the console.
Perform the following steps to do so:
dotnet new console -n Exercise1_01
This command will create a new console application in the Exercise1_01 folder.
dotnet run --project Exercise1_01
You should see the following output:
Note
You can find the code used for this exercise at https://packt.link/HErU6.
In this exercise, you created the most basic program possible with C#, a console application that prints some text to the prompt. You also learned how to use .NET CLI, which is the mechanism built within the .NET SDK to create and manage .NET projects.
Now proceed to the next section to grasp how top-level statements are written.
You must have noticed in Exercise 1.01 that, by default, when you create a console application, you have a Program.cs file that contains the following:
You will learn about classes and methods in detail later, but for now, for the sake of simplicity, you do not need these resources to create and execute programs with C#. The latest version (.NET 6) introduced a feature that makes writing simple programs much easier and less verbose. For instance, consider the following:
using System;
namespace Exercise1_01
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
You can simply replace this snippet with two lines of code, as follows:
using System;
Console.WriteLine("Hello World!");
By using such top-level statements, you can write concise programs. You can simply put the statements to be executed at the top of the program. This is also useful for speeding up the learning curve with C#, as you need not worry about advanced concepts upfront. The only thing to look out for here is that the project can have only one file with top-level statements.
That is why in this chapter, you will find that all exercises will use this format, to make things as clear as possible.
You will now take your first steps in creating your own programs. This section will delve into the concept of variables—what they are and how to use them.
A variable is a name given to a computer memory location that holds some data that may vary. For a variable to exist, it first must be declared with a type and a name. It can also have a value assigned to it. The declaration of a variable can be achieved in a few different ways.
There are some basic considerations regarding naming conventions for variables in C#:
Variables can be declared in two ways: explicitly and implicitly. Both styles of the declaration have their pros and cons, which you will explore in the next section.
A variable can be declared explicitly by writing both its type and value. Suppose you want to create two variables, a and b, both containing integers. Doing so explicitly would look like this:
int a = 0;
int b = 0;
Before a variable is used, it must have a value assigned. Otherwise, the C# compiler will give an error while building your program. The following example illustrates that:
int a;
int b = a; // The compiler will prompt an error on this line: Use of unassigned local variable
It is also possible to declare multiple variables in the same line, like in the following snippet, where you are declaring three variables; two hold the value 100 and one holds the value 10:
int a, b = 100, c = 10;
Remember that C# is a strongly typed programming language; this means that a variable will always have a type associated with it. It does not matter whether the type is declared implicitly or explicitly. With the var keyword, the C# compiler will infer the variable type based on the value that has been assigned to it.
Consider that you want to create a variable that holds some text using this method. This can be done with the following statement:
var name = "Elon Musk";
For storing text in a variable, you should start and end the text with double quotes ("). In the preceding example, by looking at the value that was assigned to name, C# knows that the type this variable holds is a string, even though the type is not mentioned in the statement.
Explicit declarations enhance readability with the type declared, and this is one of the main advantages of this technique. On the other hand, they tend to let the code become more verbose, especially when working with some data types (that you will see further ahead), such as Collections.
Essentially, deciding on the style of declaration depends on the personal preferences of the programmer, and may be influenced by the company's guidelines in some cases. In this journey of learning, it is recommended that you pick one that makes your learning path smoother, as there are few substantial differences from a purely technical standpoint.
In the next exercise, you will do this yourself by assigning variables to inputs that come from a user's interaction with a console application, where the user will be asked to input their name. To complete this exercise, you will make use of the following built-in methods that C# provides, which you will be using frequently in your C# journey:
In this exercise, you will create an interactive console application. The app should ask you for your name, and once provided, it should display a greeting with your name in it.
To complete this exercise, perform the following steps:
dotnet new console -n Exercise1_02
This command creates a new console application in the Exercise1_02 folder.
Console.WriteLine("Hi! I'm your first Program. What is your name?");
var name = Console.ReadLine();
Console.WriteLine($"Hi {name}, it is very nice to meet you. We have a really fun journey ahead.");
dotnet run --project Exercise1_02
This outputs the following:
Hi! I'm your first Program. What is your name?
Hi! I'm your first Program. What is your name?
Mateus
Hi Mateus, it is very nice to meet you. We have a really fun journey ahead.
Note
You can find the code used for this exercise at https://packt.link/1fbVH.
You are more familiar with what variables are, how to declare them, and how to assign values to them. Now it is time to start talking about what data these variables can store and, more specifically, what types of data there are.
In this section, you will talk about the main data types within C# and their functionalities.
C# uses the string keyword to identify data that stores text as a sequence of characters. You can declare a string in several ways, as shown in the following snippet. However, when assigning some value to a string variable, you must place the content between a pair of double quotes, as you can see in the last two examples:
// Declare without initializing.
string message1;
// Initialize to null.
string message2 = null;
// Initialize as an empty string
string message3 = System.String.Empty;
// Will have the same content as the above one
string message4 = "";
// With implicit declaration
var message4 = "A random message" ;
One simple but effective technique (that you used in the preceding Exercise 1.02) is one called string interpolation. With this technique, it is very simple to mix plain text values with variable values, so that the text is combined among these two. You can combine two or more strings by following these steps:
$"Hi {name}, it is very nice to meet you. We have a really fun journey ahead.");
Another important fact to remember about strings is that they are immutable. This means that a string object cannot be changed after its creation. This happens because strings in C# are an array of characters. Arrays are data structures that gather objects of the same type and have a fixed length. You will cover arrays in detail in an upcoming section.
In the next exercise, you will explore string immutability.
In this exercise, you will use two strings to demonstrate that string references are always immutable. Perform the following steps to do so:
dotnet new console -n Exercise1_03
static void FormatString(string stringToFormat)
{
stringToFormat.Replace("World", "Mars");
}
In the preceding snippet, the Replace function is used to replace the first string (World, in this case) with the second one (Mars).
static string FormatReturningString(string stringToFormat)
{
return stringToFormat.Replace("Earth", "Mars");
}
var greetings = "Hello World!";
FormatString(greetings);
Console.WriteLine(greetings);
var anotherGreetings = "Good morning Earth!";
Console.WriteLine(FormatReturningString(anotherGreetings));
dotnet run
Hello World!
Good morning Mars!
Note
You can find the code used for this exercise at https://packt.link/ZoNiw.
With this exercise, you saw the concept of string immutability in action. When you passed a string that was a reference type (Hello World!) as a method argument, it was not modified. That is what happens when you use the FormatString method, which returns void. Due to string immutability, a new string is created but not allocated to any variable, and the original string stays the same. With the second method, it returns a new string, and this string is then printed to the console.
Even though strings are reference values, when you use the .Equals() method, the equality operator (==), and other operators (such as !=), you are actually comparing the values of the strings, as can be seen in the following example:
string first = "Hello.";
string second = first;
first = null;
Now you can compare these values and call Console.WriteLine() to output the result, like so:
Console.WriteLine(first == second);
Console.WriteLine(string.Equals(first, second));
Running the preceding code results in the following output:
False
False
You get this output because, even though strings are reference types, both the == and .Equals comparisons run against string values. Also, remember that strings are immutable. This means that when you assign second to first and set first as null, a new value is created for first and, therefore, the reference for second does not change.
C# has its numeric types subdivided into two main categories—integral and floating-point type numbers. The integral number types are as follows:
Deciding which type of integral type to use depends on the size of the values you want to store.
All these types are called signed values. This means that they can store both negative and positive numbers. There is also another range of types called unsigned types. Unsigned types are byte, ushort, uint, and ulong. The main difference between them is that signed types can store negative numbers and unsigned types can store only numbers greater than or equal to zero. You will use signed types most of the time, so do not worry about remembering this all at once.
The other category, namely, floating-point types, refers to the types used to store numbers with one or more decimal points. There are three floating-point types in C#:
var myFloat = 10f;
var myDouble = 10d;
var myDecimal = 10m;
Choosing the floating-point type depends mainly on the degree of precision required. For instance, decimal is mostly used for financial applications that need a very high degree of precision and cannot rely on rounding for accurate calculations. With GPS coordinates, double variables might be appropriate if you want to deal with sub-meter precisions that usually have 10 digits.
Another relevant point to consider when choosing numeric types is performance. The larger the memory space allocated to a variable, the less performant the operations with these variables are. Therefore, if high precision is not a requirement, float variables will be better performers than doubles, which, in turn, will be better performers than decimals.
Here you grasped what variables are and their main types. Now you will perform some basic calculations with them, such as addition, subtraction, and multiplication. This can be done using the arithmetic operators available in C#, such as +, -, /, and *. So, move on to the next exercise where you will create a basic calculator using these operators.
In this exercise, you will create a simple calculator that receives two inputs and shows the results between them, based on which arithmetic operation is selected.
The following steps will help you complete this exercise:
dotnet new console -n Exercise1_04
Console.WriteLine("Type a value for a: ");
var a = int.Parse(Console.ReadLine());
Console.WriteLine("Now type a value for b: ");
var b = int.Parse(Console.ReadLine());
The preceding snippet uses the .ReadLine method to read the input. This method, however, gives a string, and you need to evaluate a number. Therefore, the Parse method has been used here. All the numeric types have a method called Parse, which receives a string and converts it into a number.
Console.WriteLine($"The value for a is { a } and for b is { b }");
Console.WriteLine($"Sum: { a + b}");
Console.WriteLine($"Multiplication: { a * b}");
Console.WriteLine($"Subtraction: { a - b}");
Console.WriteLine($"Division: { a / b}");
Type a value for a:
10
Now type a value for b:
20
The value for a is 10 and b is 20
Sum: 30
Multiplication: 200
Subtraction: -10
Division: 0
Note
You can find the code used for this exercise at https://packt.link/ldWVv.
Thus, you have built a simple calculator app in C# using the arithmetic operators. You also learned about the concept of parsing, which is used to convert strings to numbers. In the next section, you will briefly cover the topic of classes, one of the core concepts of programming in C#.
Classes are an integral part of coding in C# and will be covered comprehensively in Chapter 2, Building Quality Object-Oriented Code. This section touches upon the basics of classes so that you can begin using them in your programs.
The reserved class keyword within C# is used when you want to define the type of an object. An object, which can also be called an instance, is nothing more than a block of memory that has been allocated to store information. Given this definition, what a class does is act as a blueprint for an object by having some properties to describe this object and specifying the actions that this object can perform through methods.
For example, consider that you have a class named Person, with two properties, Name and Age, and a method that checks whether Person is a child. Methods are where logic can be placed to perform some action. They can return a value of a certain type or have the special void keyword, which indicates that they do not return anything but just execute some action. You can also have methods calling other methods:
public class Person
{
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; set; }
public int Age { get; set; }
public void GetInfo()
{
Console.WriteLine($"Name: {Name} – IsChild? {IsChild()}");
}
public bool IsChild()
{
return Age < 12;
}
}
One question remains, though. Since classes act as blueprints (or definitions if you prefer), how do you actually allocate memory to store the information defined by a class? This is done through a process called instantiation. When you instantiate an object, you allocate some space in memory for it in a reserved area called the heap. When you assign a variable to an object, you are setting the variable to have the address of this memory space, so that each time you manipulate this variable, it points to and manipulates the data allocated at this memory space. The following is a simple example of instantiation:
var person = new Person();
Note that Person has properties that have two magic keywords—get and set. Getters define that a property value can be retrieved, and setters define that a property value can be set.
Another important concept here is the concept of a constructor. A constructor is a method with no return type, usually present at the top level of the class for better readability. It specifies what is needed for an object to be created. By default, a class will always have a parameter-less constructor. If another constructor with parameters is defined, the class will be constrained to only this one. In that case, if you still want to have a parameter-less constructor, you must specify one. This is quite useful, as classes can have multiple constructors.
That said, you can assign values to an object property that has a setter in the following ways:
var person = new Person("John", 10);
var person = new Person() { Name = "John", Age = 10 };
var person = new Person();
person.Name = "John";
person.Age = 10;
There is a lot more to classes that you will see further on. For now, the main ideas are as follows:
A date can be represented in C# using the DateTime value type. It is a struct with two static properties called MinValue, which is January 1, 0001 00:00:00, and MaxValue, which is December 31, 9999 11:59:59 P.M. As the names suggest, both these values represent the minimum and maximum dates according to the Gregorian calendar date format. The default value for DateTime objects is MinValue.
It is possible to construct a DateTime variable in various ways. Some of the most common ways are as follows:
var now = DateTime.Now;
This sets the variable to the current date and time on the calling computer, expressed as the local time.
var now = DateTime.UtcNow;
This sets the variable to the current date and time on this computer, expressed as the Coordinated Universal Time (UTC).
In this exercise, you will use the TimeSpan method/struct to calculate the difference between your local time and the UTC time. To complete this exercise, perform the following steps:
dotnet new console -n Exercise1_05
Console.WriteLine("Are the local and utc dates equal? {0}", DateTime.Now.Date == DateTime.UtcNow.Date);
Console.WriteLine(" If the dates are equal, does it mean that there's no TimeSpan interval between them? {0}",
(DateTime.Now.Date - DateTime.UtcNow.Date) == TimeSpan.Zero);
DateTime localTime = DateTime.Now;
DateTime utcTime = DateTime.UtcNow;
TimeSpan interval = (localTime - utcTime);
Console.WriteLine(" Difference between the {0} Time and {1} Time: {2}:{3} hours",
localTime.Kind.ToString(),
utcTime.Kind.ToString(),
interval.Hours,
interval.Minutes);
Console.Write(" If we jump two days to the future on {0} we'll be on {1}",
new DateTime(2020, 12, 31).ToShortDateString(),
new DateTime(2020, 12, 31).AddDays(2).ToShortDateString());
In the preceding snippet, you first checked whether the current local date and UTC dates were equal. Then you checked for the interval between them, if any, using the TimeSpan method. Next, it printed the difference between the local and UTC time and printed the date two days ahead of the current one (31/12/ 2020, in this case).
dotnet run --project Exercise1_05
You should see an output like the following:
Are the local and utc dates equal? True
If the dates are equal, does it mean there's no TimeSpan interval between them? True
Difference between the Local Time and Utc Time: 0:0 hours
If we jump two days to the future on 31/12/2020 we'll be on 02/01/2021
Note
You can find the code used for this exercise at https://packt.link/WIScZ.
Note that depending on your time zone, you will likely see different output.
It is also possible to format DateTime values to localized strings. That means formatting a DateTime instance according to a special concept within the C# language called a culture, which is a representation of your local time. For instance, dates are represented differently in different countries. Now take a look at the following examples, where dates are outputted in both the format used in France and the format used in the United States:
var frenchDate = new DateTime(2008, 3, 1, 7, 0, 0);
Console.WriteLine(frenchDate.ToString(System.Globalization.CultureInfo.
CreateSpecificCulture("fr-FR")));
// Displays 01/03/2008 07:00:00
var usDate = new DateTime(2008, 3, 1, 7, 0, 0);
Console.WriteLine(frenchDate.ToString(System.Globalization.CultureInfo.CreateSpecificCulture("en-US")));
// For en-US culture, displays 3/1/2008 7:00:00 AM
It is also possible to explicitly define the format you want the date to be output in, as in the following example, where you pass the yyyyMMddTHH:mm:ss value to say that you want the date to be output as year, then month, then day, then hour, then minutes preceded by a colon, and finally, seconds, also preceded by a colon:
var date1 = new DateTime(2008, 3, 1, 7, 0, 0);
Console.WriteLine(date1.ToString("yyyyMMddTHH:mm:ss"));
The following output gets displayed:
20080301T07:00:00
You are already familiar with these. Recall that in the preceding exercise, you did the following comparison:
var now = DateTime.Now.Date == DateTime.UtcNow.Date;
This output assigns the value true to now if the dates are equal. But as you know, they might not necessarily be the same. Therefore, if the dates are different, a false value will be assigned. These two values are the result of such Boolean expressions and are called Boolean values. That is why the now variable has the type of bool.
Boolean expressions are the base for every logical comparison in every program. Based on these comparisons, a computer can execute a certain behavior in a program. Here are some other examples of Boolean expressions and variable assignments:
var basicComparison = a > b;
bool anotherBasicComparison = b >= a;
var animal1 = "Leopard";
var animal2 = "Lion";
bool areTheseAnimalsSame = animal1 == animal2;
Clearly, the result of the previous comparison would be false and this value will be assigned to the areTheseAnimalsSame variable.
Now that you have learned what Booleans are and how they work, it is time to look at some logical operators you can use to compare Boolean variables and expressions:
bool areTheseStringsWithZeroLength = "".Length == 0 && " ".Length == 0;
Console.WriteLine(areTheseStringsWithZeroLength);// will return false
bool isOneOfTheseStringsWithZeroLength = "".Length == 0 || " ".Length == 0;
Console.WriteLine(isOneOfTheseStringsWithZeroLength); // will return true
bool isOneOfTheseStringsWithZeroLength = "".Length == 0 || " ".Length == 0;
bool areYouReallySure = !isOneOfTheseStringsWithZeroLength;
Console.WriteLine(areYouReallySure); // will return false
Up till now, you have learned about types, variables, and operators. Now it is time to go into the mechanisms that help you to use these concepts in real-world problems—that is, decision-making statements.
In C#, if-else statements are some of the most popular choices for implementing branching in code, which means telling the code to follow one path if a condition is satisfied, else follow another path. They are logical statements that evaluate a Boolean expression and continue the program's execution based on this evaluation result.
For example, you can use if-else statements to check whether the password entered satisfies certain criteria (such as having at least six characters and one digit). In the next exercise, you will do exactly that, in a simple console application.
In this exercise, you will use if-else statements to write a simple credentials check program. The application should ask the user to enter their username; unless this value is at least six characters in length, the user cannot proceed. Once this condition is met, the user should be asked for a password. The password should also have a minimum of six characters containing at least one digit. Only after both these criteria are met should the program display a success message, such as User successfully registered.
The following steps will help you complete this exercise:
dotnet new console -n Exercise1_06
Console.WriteLine("Please type a username. It must have at least 6 characters: ");
var username = Console.ReadLine();
if (username.Length < 6)
{
Console.WriteLine($"The username {username} is not valid.");
}
else
{
Console.WriteLine("Now type a
password. It must have a length of at least 6 characters and also contain a number.");
var password = Console.ReadLine();
if (password.Length < 6)
{
Console.WriteLine("The password must have at least 6 characters.");
}
else if (!password.Any(c => char.IsDigit(c)))
{
Console.WriteLine("The password must contain at least one number.");
}
else
{
Console.WriteLine("User successfully registered.");
}
}
From the preceding snippet, you can see that if the user enters fewer than six characters, an error message is displayed as The password must have at least 6 characters.. If the password doesn't contain a single digit but satisfies the preceding condition, another error message is displayed as The password must contain at least one number..
Notice the logical condition used for this, which is !password.Any(c => char.IsDigit(c)). You will learn more about the => notation in Chapter 2, Building Quality Object-Oriented Code, but for now, you just need to know that this line checks every character in the password and uses the IsDigit function to check whether the character is a digit. This is done for every character, and if no digit is found, the error message is displayed. If all the conditions are met, a success message is displayed as User successfully registered..
Please type a username. It must have at least 6 characters:
thekingjames
Now type a password. It must have at least 6 characters and a number.
James123!"#
User successfully registered
Note
You can find the code used for this exercise at https://packt.link/3Q7oK.
In this exercise, you worked with if-else branching statements to implement a simple user registration program.
Another simple-to-use, yet effective, decision-making operator is the ternary operator. It allows you to set the value of a variable based on a Boolean comparison. For example, consider the following example:
var gift = person.IsChild() ? "Toy" : "Clothes";
Here, you are using the ? symbol to check whether the Boolean condition placed before it is valid. The compiler runs the IsChild function for the person object. If the method returns true, the first value (before the : symbol) will be assigned to the gift variable. If the method returns false, the second value (after the : symbol) will be assigned to the gift variable.
The ternary operator is simple and makes assignments based on simple Boolean verifications even more concise. You will be using this quite often in your C# journey.
There are two types of variables in C#, namely, reference types and value types. Variables of value types, such as structs, contain the values themselves, as the name suggests. These values are stored in a memory space called the stack. When a variable of such a type is declared, specific memory space is allocated to store this value, as illustrated in the following figure:
Here, the value of the variable, which is 5, is stored in memory at the location 0x100 in the RAM. The built-in value types for C# are bool, byte, char, decimal, double, enum, float, int, long, sbyte, short, struct, uint, ulong, and ushort.
The scenario for reference type variables is different, though. The three main reference types you need to know about in this chapter are string, array, and class. When a new reference type variable is assigned, what is stored in memory is not the value itself, but instead a memory address where the value gets allocated. For example, consider the following diagram:
Here, instead of the value of the string variable (Hello), the address where it is allocated (0x100) is stored in memory. For brevity, you will not dive deep into this topic, but it is important to know the following points:
For instance, consider the following code, which deals with integers. Here, you declare an int variable named a and assign the value 100 to it. Later, you create another int variable named b and assign the value of a to it. Finally, you modify b, to be incremented by 100:
using System;
int a = 100;
Console.WriteLine($"Original value of a: {a}");
int b = a;
Console.WriteLine($"Original value of b: {b}");
b = b + 100;
Console.WriteLine($"Value of a after modifying b: {a}");
Console.WriteLine($"Value of b after modifying b: {b}");
The values of a and b will be displayed in the following output:
Original value of a: 100
Original value of b: 100
Value of a after modifying b: 100
Value of b after modifying b: 200
In this example, the value from a was copied into b. From this point, any other modification you do on b will reflect changes only in b and a will continue to have its original value.
Now, what if you pass reference types as method arguments? Consider the following program. Here, you have a class named Car with two properties—Name and GearType. Inside the program is a method called UpgradeGearType that receives an object of the Car type and changes its GearType to Automatic:
using System;
var car = new Car();
car.Name = "Super Brand New Car";
car.GearType = "Manual";
Console.WriteLine($"This is your current configuration for the car {car.Name}: Gea–Type - {car.GearType}");
UpgradeGearType(car);
Console.WriteLine($"You have upgraded your car {car.Name} for the GearType {car.GearType}");
void UpgradeGearType(Car car)
{
car.GearType = "Automatic";
}
class Car
{
public string Name { get; set; }
public string GearType { get; set; }
}
After you create a Car instance and call the UpgradeGearType() method, the output will be as the follows:
This is your current configuration for the car Super Brand New Car: GearType – Manual
You have upgraded your car Super Brand New Car for the GearType Automatic
Thus, you see that if you pass an object of a reference type (car in this case) as an argument to a method (UpgradeGearType in this example), every change made inside this object is reflected after and outside the method call. This is because reference types refer to a specific location in memory.
In this exercise, you will see how equality comparison is different for value types and reference types. Perform the following steps to do so:
dotnet new console -n Exercise1_07
struct GoldenRetriever
{
public string Name { get; set; }
}
class BorderCollie
{
public string Name { get; set; }
}
class Bernese
{
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj is Bernese borderCollie && obj != null)
{
return this.Name == borderCollie.Name;
}
return false;
}
}
Here, the this keyword is used to refer to the current instance of the borderCollie class.
var aGolden = new GoldenRetriever() { Name = "Aspen" };
var anotherGolden = new GoldenRetriever() { Name = "Aspen" };
var aBorder = new BorderCollie() { Name = "Aspen" };
var anotherBorder = new BorderCollie() { Name = "Aspen" };
var aBernese = new Bernese() { Name = "Aspen" };
var anotherBernese = new Bernese() { Name = "Aspen" };
var goldenComparison = aGolden.Equals(anotherGolden) ? "These Golden Retrievers have the same name." : "These Goldens have different names.";
var borderComparison = aBorder.Equals(anotherBorder) ? "These Border Collies have the same name." : "These Border Collies have different names.";
var berneseComparison = aBernese.Equals(anotherBernese) ? "These Bernese dogs have the same name." : "These Bernese dogs have different names.";
Console.WriteLine(goldenComparison);
Console.WriteLine(borderComparison);
Console.WriteLine(berneseComparison);
These Golden Retrievers have the same name.
These Border Collies have different names.
These Bernese dogs have the same name.
Note
You can find the code used for this exercise at https://packt.link/xcWN9.
As mentioned earlier, structs are value types. Therefore, when two objects of the same struct are compared with Equals, .NET internally checks all the struct properties. If those properties have equal values, then true is returned. With Golden Retrievers, for instance, if you had a FamilyName property and this property was different between the two objects, the result of the equality comparison would be false.
For classes and all other reference types, the equality comparison is quite different. By default, object reference is checked on equality comparison. If the references are different (and they will be, unless the two variables are assigned to the same object), the equality comparison will return false. This explains the result you see for Border Collies in the example that the references were different for the two instances.
However, there is a method that can be implemented in reference types called Equals. Given two objects, the Equals method can be used for comparison following the logic placed inside the method. That is exactly what happened with the Bernese dogs example.
Now that you have dealt with value and reference types, you will briefly explore the default value types. In C#, every type has a default value, as specified in the following table:
These default values can be assigned to a variable using the default keyword. To use this word in a variable declaration, you must explicitly declare the variable type before its name. For example, consider the following snippet, where you are assigning the default value to two int variables:
int a = default;
int b = default;
Both a and b will be assigned the value 0 in this case. Note that it is not possible to use var in this case. This is because, for implicitly declared variables, the compiler needs a value assigned to the variable in order to infer its type. So, the following snippet will lead to an error because no type was set, either through an explicit declaration or by variable assignment:
var a = default;
var b = default;
The switch statement is often used as an alternative to the if-else construct if a single expression is to be tested against three or more conditions, that is, when you want to select one of many code sections to be executed, such as the following:
switch (matchingExpression)
{
case firstCondition:
// code section
break;
case secondCondition:
// code section
break;
case thirdCondition:
// code section
break;
default:
// code section
break;
}
The matching expression should return a value that is of one of the following types: char, string, bool, numbers, enum, and object. This value will then be evaluated within one of the matching case clauses or within the default clause if it does not match any prior clause.
It is important to say that only one switch section in a switch statement will be executed. C# doesn't allow execution to continue from one switch section to the next. However, a switch statement does not know how to stop by itself. You can either use the break keyword if you only wish to execute something without returning or return something if that is the case.
Also, the default keyword on a switch statement is where the execution goes if none of the other options are matched. In the next exercise, you will use a switch statement to create a restaurant menu app.
In this exercise, you will create a console app that lets the user select from a menu of food items available at a restaurant. The app should display an acknowledgment receipt for the order. You will use the switch statement to implement the logic.
Follow these steps to complete this exercise:
var menuBuilder = new System.Text.StringBuilder();
menuBuilder.AppendLine("Welcome to the Burger Joint. ");
menuBuilder.AppendLine(string.Empty);
menuBuilder.AppendLine("1) Burgers and Fries - 5 USD");
menuBuilder.AppendLine("2) Cheeseburger - 7 USD");
menuBuilder.AppendLine("3) Double-cheeseburger - 9 USD");
menuBuilder.AppendLine("4) Coke - 2 USD");
menuBuilder.AppendLine(string.Empty);
menuBuilder.AppendLine("Note that every burger option comes with fries and ketchup!");
Console.WriteLine(menuBuilder.ToString());
Console.WriteLine("Please type one of the following options to order:");
var option = Console.ReadKey();
switch (option.KeyChar.ToString())
{
case "1":
{
Console.WriteLine(" Alright, some burgers on the go. Please pay the cashier.");
break;
}
case "2":
{
Console.WriteLine(" Thank you for ordering cheeseburgers. Please pay the cashier.");
break;
}
case "3":
{
Console.WriteLine(" Thank you for ordering double cheeseburgers, hope you enjoy them. Please pay the cashier!");
Any other input, however, should be considered invalid and a message gets displayed, letting you know you have selected an invalid option:
break;
}
case "4":
{
Console.WriteLine(" Thank you for ordering Coke. Please pay the cashier.");
break;
}
default:
{
Console.WriteLine(" Sorry, you chose an invalid option.");
break;
}
}
Welcome to the Burger Joint.
1) Burgers and Fries – 5 USD
2) Cheeseburger – 7 USD
3) Double-cheeseburger – 9 USD
4) Coke – 2 USD
Note that every burger option comes with fries and ketchup!
Please type one of the follow options to order:
1
Alright, some burgers on the go! Please pay on the following cashier!
Note
You can find the code used for this exercise at https://packt.link/x1Mvn.
Similarly, you should get the output for the other options as well. You have learned about branching statements in C#. There is another type of statement that you will use often while programming using C#, called iteration statements. The next section covers this topic in detail.
Iteration statements, also called loops, are types of statements that are useful in the real world, as you often need to continuously repeat some logical execution in your applications while or until some condition is met, such as operating with a number that must be incremented until a certain value. C# offers numerous ways of implementing such iterations, and in this section, you will examine each of these in detail.
The first iteration statement you will consider is the while statement. This statement allows a C# program to execute a set of instructions while a certain Boolean expression is evaluated to be true. It has one of the most basic structures. Consider the following snippet:
int i = 0;
while (i < 10)
{
Console.WriteLine(i);
i = i +1;
}
The preceding snippet shows how you can use the while statement. Note that the while keyword is followed by a pair of brackets enclosing a logical condition; in this case, the condition is that the value of i must be less than 10. The code written inside the curly braces will be executed until this condition is true.
Thus, the preceding code will print the value of i, starting with 0, up to 10. This is fairly simplistic code; in the next exercise, you will use the while statement for something a little more complex, such as checking whether a number entered by you is a prime number.
In this exercise, you will use a while loop to check whether a number you enter is prime. To do so, the while loop will check whether the counter is less than or equal to the integer result of the division of the number by 2. When this condition is satisfied, you check whether the remainder of the division of the number by the counter is 0. If not, you increment the counter and continue until the loop condition is not met. If it is met, it means the number is not false and the loop can stop.
Perform the following steps to complete this exercise:
static bool IsPrime(int number)
{
if (number ==0 || number ==1) return false;
bool isPrime = true;
int counter = 2;
while (counter <= Math.Sqrt(number))
{
if (number % counter == 0)
{
isPrime = false;
break;
}
counter++;
}
return isPrime;
}
Console.Write("Enter a number to check whether it is Prime: ");
var input = int.Parse(Console.ReadLine());
Console.WriteLine($"{input} is prime? {IsPrime(input)}.");
Enter a number to check whether it is Prime:
29
29 is prime? True
As expected, the result for 29 is true since it is a prime number.
Note
You can find the code used for this exercise at https://packt.link/5oNg5.
The preceding exercise aimed to show you the simple structure of a while loop with some more complex logic. It checks a number (named input) and prints whether it is a prime number. Here, you have seen the break keyword used again to stop program execution. Now proceed to learn about jump statements.
There are some other important keywords used within loops that are worth mentioning as well. These keywords are called jump statements and are used to transfer program executions to another part. For instance, you could rewrite the IsPrime method as follows:
static bool IsPrimeWithContinue(int number)
{
if (number == 0 || number ==1) return false;
bool isPrime = true;
int counter = 2;
while (counter <= Math.Sqrt(number))
{
if (number % counter != 0)
{
counter++;
continue;
}
isPrime = false;
break;
}
return isPrime;
}
Here, you have inverted the logical check. Instead of checking whether the remainder is zero and then breaking the program execution, you have checked that the remainder is not zero and, if so, have used the continue statement to pass the execution to the next iteration.
Now look at how you can rewrite this using another special keyword, goto:
static bool IsPrimeWithGoTo(int number)
{
if (number == 0 || number ==1) return false;
bool isPrime = true;
int counter = 2;
while (counter <= Math.Sqrt(number))
{
if (number % counter == 0)
{
isPrime = false;
goto isNotAPrime;
}
counter++;
}
isNotAPrime:
return isPrime;
}
The goto keyword can be used to jump from one part of the code to another one defined by what is called a label. In this case, the label was named isNotAPrime. Finally, take a look at one last way of writing this logic:
static bool IsPrimeWithReturn(int number)
{
if (number == 0 || number ==1) return false;
int counter = 2;
while (counter <= Math.Sqrt(number))
{
if (number % counter == 0)
{
return false;
}
counter ++;
}
return true;
}
Now, instead of using break or continue to stop the program execution, you simply use return to break the loop execution since the result that you were looking for was already found.
The do-while loop is like the previous one, but with one subtle difference: it executes the logic at least once, while a simple while statement may never be executed if the condition is not met at the first execution. It has the following structure:
int t = 0;
do
{
Console.WriteLine(t);
t++;
} while (t < 5);
In this example, you write the value of t, starting from 0, and keep incrementing it while it is smaller than 5. Before jumping into the next type of loop, learn about a new concept called arrays.
An array is a data structure used to store many objects of the same type. For instance, the following example is a variable declared as an array of integer numbers:
int[] numbers = { 1, 2, 3, 4, 5 };
The first important thing to note about arrays is that they have a fixed capacity. This means that an array will have the length defined at the time of its creation and this length cannot change. The length can be determined in various ways. In the preceding example, the length is inferred by counting the number of objects in the array. However, another way of creating an array is like this:
var numbers = new int[5];
Here, you are creating an array that has the capacity of 5 integers, but you do not specify any value for the array elements. When an array of any data type is created without adding elements to it, the default values for that value type are set for each position of the array. For example, consider the following figure:
The preceding figure shows that when you create an integer array of five elements, without assigning a value to any element, the array is automatically filled with the default value at every position. In this case, the default value is 0. Now consider the following figure:
In the preceding example, you have created an array of five objects and assigned the "Hello" string value to the element at index 1. The other positions of the array are automatically assigned the default value for objects, which is null.
Finally, it is worth noting that all arrays have indexes, which refers to the positions of the individual array elements. The first position will always have an index 0. Thus, the positions in an array of size n can be specified from index 0 to n-1. Therefore, if you call numbers[2], this means that you are trying to access the element in position 2 inside the numbers array.
A for loop executes a set of instructions while a Boolean expression matches a specified condition. Just like while loops, jump statements can be used to stop a loop execution. It has the following structure:
for (initializer; condition; iterator)
{
[statements]
}
The initializer statement is executed before the loop starts. It is used to declare and assign a local variable that will be used only inside the scope of the loop.
But in more complex scenarios, it can be used to combine other statement expressions as well. The condition specifies a Boolean condition that indicates when the loop should either continue or exit. The iterator is usually used to increment or decrement the variable created in the initializer section. Take the following example, where a for loop is used to print the elements of an integer array:
int[] array = { 1, 2, 3, 4, 5 };
for (int j = 0; j < array.Length - 1; j++)
{
Console.WriteLine(array[j]);
}
In this example, an initializer variable, j, has been created that is assigned 0 initially. The for loop will keep executing while j is smaller than the array length minus 1 (remember that indexes always start at 0). After each iteration, the value of j is incremented by 1. In this way, the for loop goes through the entire array and performs the given action, that is, printing the value of the current array element.
C# also allows the usage of nested loops, that is, a loop within a loop, as you will see in the next exercise.
In this exercise, you will execute one of the simplest sorting algorithms. Bubble sort consists of going through every pair of elements inside an array and swapping them if they are unordered. In the end, the expectation is to have an array ordered in ascending order. You will use nested for loops to implement this algorithm.
To begin with, the array to be sorted should be passed as a parameter to this method. For each element of this array, if the current element is greater than the next, their positions should be swapped. This swap occurs by storing the value of the next element in a temporary variable, assigning the value of the current element to the next element, and finally, setting the value of the current element with the temporary value stored. Once the first element is compared to all others, a comparison starts for the second element and so on, till finally, the array is sorted.
The following steps will help you complete this exercise:
dotnet new console -n Exercise1_10
static int[] BubbleSort(int[] array)
{
int temp;
// Iterate over the array
for (int j = 0; j < array.Length - 1; j++)
{
// If the last j elements are already ordered, skip them
for (int i = 0; i < array.Length - j - 1; i++)
{
if (array[i] > array[i + 1])
{
temp = array[i + 1];
array[i + 1] = array[i];
array[i] = temp;
}
}
}
return array;
}
int[] randomNumbers = { 123, 22, 53, 91, 787, 0, -23, 5 };
int[] sortedArray = BubbleSort(randomNumbers);
Console.WriteLine("Sorted:");
for (int i = 0; i < sortedArray.Length; i++)
{
Console.Write(sortedArray[i] + " ");
}
Sorted:
-23 0 5 22 53 91 123 787
Note
You can find the code used for this exercise at https://packt.link/cJs8y.
In this exercise, you used the two concepts learned in the last two sections: arrays and for loops. You manipulated arrays, accessing their values through indexes, and used for loops to move through these indexes.
There is another way to go through every element of an array or group in C#, called foreach statements. You will explore this in the following section.
A foreach statement executes a set of instructions for each element of a collection. Just like a for loop, the break, continue, goto, and return keywords can also be used with foreach statements. Consider the following example, in which you iterate over every element of an array and write it to the console as the output:
var items = new int[] { 1, 2, 3, 4, 5 };
foreach (int element in items)
{
Console.WriteLine(element);
}
The preceding snippet prints the numbers from 1 to 5 to the console. You can use foreach statements with much more than arrays; they can also be used with lists, collections, and spans, which are other data structures that will be covered in later chapters.
So far, you have been creating programs that interact mostly with CPU and memory. This section will focus on I/O operations, that is, input and output operations, on the physical disk. A great example of this type of operation is file handling.
C# has several classes that help you perform I/O operations. Some of these are as follows:
You already know that files are mostly some sets of data located somewhere in a hard drive that can be opened for reading or writing by some program. When you open a file in a C# application, your program reads the file as a sequence of bytes through a communication channel. This communication channel is called a stream. Streams can be of two types:
The Stream class is an abstract class in C# that enables common operations regarding this byte flow. For file handling on a hard disk, you will use the FileStream class, designed specifically for this purpose. The following are two important properties of this class: FileAccess and FileMode.
This is an enum that provides you with options to choose a level of access when opening a specified file:
This is an enum that specifies the operations that can be performed on a file. It should be used along with the access mode as some modes only work with some levels of access. Take a look at the options, as follows:
In this exercise, you will read text from a Comma-Separated Values (CSV) file. CSV files simply contain data represented by strings and separated either by colons or semicolons.
Perform the following steps to complete this exercise:
dotnet new console -n Exercise1_11
Model;Memory;Storage;USB Ports;Screen;Condition;Price USD
Macbook Pro Mid 2012;8GB;500GB HDD;USB 2.0x2;13" screen;Refurbished;400
Macbook Pro Mid 2014;8GB;512GB SSD;USB 3.0x3;15" screen;Refurbished;750
Macbook Pro Late 2019;16GB;512GB SSD;USB 3.0x3;15" screen;Refurbished;1250
using System;
using System.IO;
using System.Threading.Tasks;
namespace Exercise1_11
{
public class Program
{
public static async Task Main()
{
using (var fileStream = new FileStream("products.csv", FileMode.Open, FileAccess.Read))
{
using (var reader = new StreamReader(fileStream))
{
var content = await reader.ReadToEndAsync();
var lines = content.Split(Environment.NewLine);
foreach (var line in lines)
{
Console.WriteLine(line);
}
}
}
}
}
}
Note
You can find the code used for this exercise at https://packt.link/5flid.
This exercise has some pretty interesting outcomes, which you are going to learn step by step. First, you opened a file using the FileStream class. This allows you to start streaming bytes from a file with two special properties, namely, FileMode and FileAccess. It will return a stream of bytes with the file contents. However, to read this content as text, you need to use the StreamReader class. This class enables you to read these bytes as text characters.
Notice also that your Main method changed from void to async Task. Additionally, the await keyword has been used, which is used for asynchronous operations. You will learn more about these topics in upcoming chapters. For now, you only need to know that an async operation is something that does not block the program execution. This means that you can output lines as they are being read; that is, you do not have to wait for all of them to be read.
In the next section, learn about the special keyword that handles files, databases, and network connections.
Another special thing about the preceding exercise was the using keyword. It is a keyword used to clean up unmanaged resources from memory. These resources are special objects that handle some operational system resources, such as files, databases, and network connections. They are called special because they do what is called I/O operations; that is, they interact with the real resources of the machine, such as network and hard drives, not just with memory spaces.
The memory used by objects in C# is handled by something called the garbage collector. By default, C# handles the memory space in the stack and the heap. The only types of objects that do not perform this cleanup are called unmanaged objects.
Cleaning these objects from memory means that the resources will be free to be used by another process in the computer. That means a file can be handled by another one, a database connection is free to be used again by a connection pool, and so on. Those types of resources are called disposable resources. Every time you deal with a disposable resource, you can use the using keyword when creating an object. Then, the compiler knows that when the using statement closes, it can automatically free these resources.
In this exercise, you will write some text into a CSV file, again using the FileStream class.
Follow these steps to complete this exercise:
dotnet new console -n Exercise1_12
static async Task ReadFile(FileStream fileStream)
{
using (var reader = new StreamReader(fileStream))
{
var content = await reader.ReadToEndAsync();
var lines = content.Split(Environment.NewLine);
foreach (var line in lines)
{
Console.WriteLine(line);
}
}
}
using (var file = new StreamWriter("products.csv", append: true))
{
file.Write(" One more macbook without details.");
}
using (var fileStream = new FileStream("products.csv", FileMode.Open,
FileAccess.Read))
{
await ReadFile(fileStream);
}
Model;Memory;Storage;USB Ports;Screen;Condition;Price USD
Macbook Pro Mid 2012;8GB;500GB HDD;USB 2.0x2;13" screen;Refurbished;400
Macbook Pro Mid 2014;8GB;512GB SSD;USB 3.0x3;15" screen;Refurbished;750
Macbook Pro Late 2019;16GB;512GB SSD;USB 3.0x3;15" screen;Refurbished;1250
One more macbook without details.
Note that for each run, the program will append a new line, so you will see more lines being added.
Note
You can find the code used for this exercise at https://packt.link/dUk2z.
Sometimes your program will fail to execute at some point and may not provide an output. Such an instance is called an exception error. The next section details all about such an error.
Exceptions indicate that a program has failed to execute at some point for some reason and can be raised by either the code itself or the .NET runtime. Usually, an exception is a severe failure and can even terminate your program's execution. Fortunately, C# provides a special way of handling exceptions, which is try/catch blocks:
try
{
// some logic that might throw an exception
}
catch
{
// error handling
}
Inside the try clause, you call the code that might throw an exception, and inside the catch clause, you can treat the exception that was raised. For instance, consider the following example:
double Divide(int a, int b) => a/b;
This method takes two integers and returns the result of a division between them. However, what will happen if b is 0? In such a case, the runtime will throw System.DivideByZeroException, indicating that it is not possible to execute the division. How could you handle this exception in a real-world program? You will explore this in the next exercise.
In this exercise, you will create a console app that takes two inputs from you, divides the first number by the second one, and outputs the result. If you enter an invalid character, the app should throw an exception, and all of this should be handled inside the program logic.
Perform the following steps to complete this exercise:
static double Divide(int a, int b)
{
return a / b;
}
bool divisionExecuted = false;
while (!divisionExecuted)
{
try
{
Console.WriteLine("Please input a number");
var a = int.Parse(Console.ReadLine());
Console.WriteLine("Please input another number");
var b = int.Parse(Console.ReadLine());
var result = Divide(a, b);
Console.WriteLine($"Result: {result}");
divisionExecuted = true;
}
catch (System.FormatException)
{
Console.WriteLine("You did not input a number. Let's start again ... ");
continue;
}
catch (System.DivideByZeroException)
{
Console.WriteLine("Tried to divide by zero. Let's start again ... ");
continue;
}
}
Please input a number
5
Please input another number
0
Tried to divide by zero. Let's start again …
Please input a number
5
Please input another number
s
You did not input a number. Let's start again …
Please input a number
5
Please input another number
1
Result: 5
Note
You can find the code used for this exercise at https://packt.link/EVsrJ.
In this exercise, you handled two types of exceptions that are as follows:
Now that you have seen how exceptions are handled, it is important to note a rule of thumb that will help you in your C# journey, which is that you should only catch what you can or what you need to handle. There are only a few situations where exception handling is really needed, as follows:
The throw keyword can also be used to intentionally stop the program execution flow in certain cases. For example, consider that you are creating a Person object and that the Name property should not be null at the time of creation. You can enforce on this class a contract that says: if these parameters are not correctly provided, it cannot be used. Typically, you would do so by throwing System.ArgumentException or System.ArgumentNullException, as in the following snippet, which uses ArgumentNullException to do so:
class Person
{
Person(string name)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
Name = name;
}
String Name { get ; set; }
}
Here, if the value of the name argument is null or if you only enter space characters, ArgumentNullException is thrown, and the program does not execute successfully. The null/white space condition is checked with the help of the IsNullOrWhiteSpace function, which can be used for string variables.
Now it's time to practice all that you learned in the previous sections through an activity.
To complete this activity, you need to create a guessing game using the concepts you have learned about and practiced so far in this chapter. In this game, first, a random number from one to 10 must be generated, not to be output to the console. The console should then prompt the user to input a number and then guess which random number has been generated, and the user should get a maximum of five chances.
Upon every incorrect input, a warning message should be displayed, letting the user know how many chances they have left, and if all five chances are exhausted with incorrect guesses, the program terminates. However, once the user guesses correctly, a success message should be displayed, before the program terminates.
The following steps will help you complete this activity:
new Random().Next(0, 10)
This generates a random number for you, between 0 and 10. You could replace 10 with a higher number if you wanted to make the game a little more difficult, or with a smaller number to make it easier, but for this activity, you will use 10 as the maximum value.
Note
The solution to this activity can be found at https://packt.link/qclbF.
This chapter gave you an overview of the fundamentals of C# and what it looks like to write programs with it. You explored everything from the variable declaration, data types, and basic arithmetic and logical operators to file and exception handling. You also explored how C# allocates memory while dealing with value and reference types.
In the exercises and activities in this chapter, you were able to solve some real-world problems and think of solutions that can be implemented with this language and its resources. You learned how to prompt for user inputs in console apps, how to handle files within a system, and finally, how to deal with unexpected inputs through exception handling.
The next chapter will cover the essentials of Object-oriented programming, diving deeper into the concept of classes and objects. You will also learn about the importance of writing clean, concise code that is easy to maintain, and the principles you can follow for writing such code.