About this book

Data structures allow organizing data efficiently. They are critical to various problems and their suitable implementation can provide a complete solution that acts like reusable code. In this book, you will learn how to use various data structures while developing in the C# language as well as how to implement some of the most common algorithms used with such data structures.

At the beginning, you will get to know arrays, lists, dictionaries, and sets together with real-world examples of your application. Then, you will learn how to create and use stacks and queues. In the following part of the book, the more complex data structures will be introduced, namely trees and graphs, together with some algorithms for searching the shortest path in a graph. We will also discuss how to organize the code in a manageable, consistent, and extendable way. By the end of the book,you will learn how to build components that are easy to understand, debug, and use in different applications.

Publication date:
April 2018
Publisher
Packt
Pages
292
ISBN
9781788833738

 

Chapter 1. Getting Started

Developing applications is certainly something exciting to work on, but it is also challenging, especially if you need to solve some complex problems that involve advanced data structures and algorithms. In such cases, you often need to take care of performance to ensure that the solution will work smoothly on devices with limited resources. Such a task could be really difficult and could require significant knowledge regarding not only the programming language, but also data structures and algorithms.

Did you know that replacing even one data structure with another could cause the performance results to increase hundreds of times? Does it sound impossible? Maybe, but it is true! As an example, I would like to tell you a short story about one of the projects in which I was involved. The aim was to optimize the algorithm of finding connections between blocks on a graphical diagram. Such connections should be automatically recalculated, refreshed, and redrawn as soon as any block has moved in the diagram. Of course, connections cannot go through blocks and cannot overlap other lines, and the number of crossings and direction changes should be limited. Depending on the size and the complexity of the diagram, the performance results differ. However, while conducting tests, we have received results in the range from 1 ms to almost 800 ms for the same test case. What could be the most surprising aspect is that such a huge improvement has been reached mainly by... changing data structures of two sets.

Now, you could ask yourself the obvious question: which data structures should I use in given circumstances and which algorithms could be used to solve some common problems? Unfortunately, the answer is not simple. However, within this book, you will find a lot of information about data structures and algorithms, presented in the context of the C# programming language, with many examples, code snippets, and detailed explanations. Such content could help you to answer the aforementioned questions while developing the next great solutions, which could be used by many people all over the world! Are you ready to start your adventure with data structures and algorithms? If so, let's start!

In this chapter, you will cover the following topics:

  • Programming language
  • Data types
  • Installation and configuration of the IDE
  • Creating the project
  • Input and output
  • Launching and debugging
 

Programming language


As a developer, you have certainly heard about many programming languages, such as C#, Java, C++, C, PHP, or Ruby. In all of them, you can use various data structures, as well as implement algorithms, to solve both basic and complex problems. However, each language has its own specificity, which could be visible while implementing data structures and accompanying algorithms. As already mentioned, this book will focus only on the C# programming language, which is also the main topic of this section.

The C# language, pronounced as "C Sharp", is a modern, general-purpose, strongly-typed, and object-oriented programming language that can be used while developing a wide range of applications, such as web, mobile, desktop, distributed, and embedded solutions, as well as even games. It cooperates with various additional technologies and platforms, including ASP.NET MVC, Windows Store, Xamarin, Windows Forms, XAML, and Unity. Therefore, when you learn the C# language, as well as getting to know more about data structures and algorithms in the context of this programming language, you can use such skills to create more than one particular type of software.

The current version of the language is C# 7.1. It is worth mentioning its interesting history with the following versions of the language (for example, 2.0, 3.0, and 5.0) in which new features have been added to increase language possibilities and to simplify the work of developers. When you take a look at release notes for particular versions, you will see how the language is being improved and expanded over time.

The syntax of the C# programming language is similar to other languages, such as Java or C++. For this reason, if you know such languages, you should quite easily be able to understand the code written in C#. As an example, similarly as in the languages mentioned previously, the code consists of statements that end with semicolons (;), and curly brackets ({ and }) are used to group statements, such as within the foreach loop. You could also find similar code constructions, such as the if statement, or while and for loops.

Developing various applications in the C# language is also simplified by the availability of many additional great features, such as Language Integrated Query (LINQ), which allows developers to get data from various collections, such as SQL databases or XML documents, in a consistent way. There are also some approaches to shorten the required code, such as using lambda expressions, expression-bodied members, getters and setters, or string interpolation. It is worth mentioning the automatic garbage collection that simplifies the task of releasing memory. Of course, the solutions mentioned are only the very limited subset of features available while developing in C#. You will see some others in the following parts of this book, together with examples and detailed descriptions.

 

Data types


While developing applications in the C# language, you could use various data types, which are divided into two groups, namely value types and reference types. The difference between them is very simple—a variable of a value type directly contains data, while a variable of a reference type just stores a reference to data, as shown as follows:

As you can see, a Value type stores its actual Value directly in the Stack memory, while a Reference type only stores a Reference here. The actual value is located in the Heap memory. Therefore, it is also possible to have two or more variables of a reference type that reference exactly the same value.

Of course, a difference between value and reference types is very important while programming and you should know which types belong to the groups mentioned. Otherwise, you could make mistakes in the code that could be quite difficult to find. For instance, you should remember to take care while updating the data of a reference type, because the change could also be reflected in other variables that are referencing the same object. Moreover, you should be careful while comparing two objects with the equals (=) operator, because you could compare the reference, not the data itself, in the case of two instances of a reference type.

Note

The C# language also supports pointer types, which can be declared as type* identifier or void* identifier. However, such types are beyond the scope of this book. You can read more about them at: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/pointer-types.

Value types

To give you a better understanding of data types, let's start with the analysis of the first group (that is, value types), which could be further divided into structs and enumerations.

Structs

Within structs, you have access to many built-in types, which could be used either as keywords or types from the System namespace.

One of them is the Boolean type (the bool keyword), which makes it possible to store a logical value, that is, one of two values, namely true or false.

As for storing integer values, you can use one of the following types: Byte (the byte keyword), SByte (sbyte), Int16 (short), UInt16 (ushort), Int32 (int), UInt32 (uint), Int64 (long), and UInt64 (ulong). They differ by the number of bytes for storing values and therefore by the range of available values. As an example, the short data type supports values in the range from -32,768 to 32,767 while uint supports values in the range from 0 to 4,294,967,295. Another type within the integral types is Char (char), which represents a single Unicode character such as 'a' or 'M'.

In the case of floating-point values, you can use two types, namely Single (float) and Double (double). The first uses 32 bits, while the second uses 64 bits. Thus, their precision differs significantly.

What's more, the Decimal type (the decimal keyword) is available. It uses 128 bits and is a good choice for monetary calculations.

An example declaration of a variable in the C# programming language is as follows:

int number; 

You can assign a value to a variable using the equals sign (=), shown as follows:

number = 500; 

Of course, declaration and assignment could be performed in the same line:

int number = 500; 

If you want to declare and initialize an immutable value, that is, a constant, you can use the const keyword, as shown in the following line of code:

const int DAYS_IN_WEEK = 7; 

Note

More information about the built-in data types, together with the complete list of ranges, is available at: https://msdn.microsoft.com/library/cs7y5x0x.aspx.

Enumerations

Apart from structs, the value types contain enumerations. Each has a set of named constants to specify the available set of values. For instance, you can create the enumeration for available languages or supported currencies. An example definition is as follows:

enum Language { PL, EN, DE }; 

Then, you can use the defined enumeration as a data type, as shown as follows:

Language language = Language.PL; 
switch (language) 
{ 
    case Language.PL: /* Polish version */ break; 
    case Language.DE: /* German version */ break; 
    default: /* English version */ break; 
} 

It is worth mentioning that enumerations allow you to replace some magical strings (such as "PL" or "DE") with constant values and this has a positive impact on code quality.

Note

You can also benefit from more advanced features of enumerations, such as changing the underlying type or specifying values for particular constants. You can find more information at: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum.

Reference types

The second main group of types is named reference types. Just as a quick reminder, a variable of a reference type does not directly contain data, because it just stores a reference to data. In this group, you can find three built-in types, namely string, object, and dynamic. Moreover, you can declare classes, interfaces, and delegates.

Note

More information about the reference types is available at: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types.

Strings

There is often the necessity to store some text values. You can achieve this goal using the String built-in reference type from the System namespace, which is also available using the string keyword. The string type is a sequence of Unicode characters. It can have zero chars, one or more chars, or the string variable can be set to null.

You can perform various operations on string objects, such as concatenation or accessing a particular char using the [] operator, as shown as follows:

string firstName = "Marcin", lastName = "Jamro"; 
int year = 1988; 
string note = firstName + " " + lastName.ToUpper()  
   + " was born in " + year; 
string initials = firstName[0] + "." + lastName[0] + "."; 

At the beginning, the firstName variable is declared, and the "Marcin" value is assigned to it. Similarly, "Jamro" is set as a value of the lastName variable. In the third line, you concatenate five strings (using the + operator), namely, the current value of firstName, the space, the current value of lastName converted to the upper-case string (by calling the ToUpper method), the string " was born in ", and the current value of the year variable. In the last line, the first chars from firstName and lastName variables are obtained, using the [] operator, as well as concatenated with two dots to form the initials, that is, M.J., which are stored as a value of the initials variable.

The Format static method could also be used for constructing the string, as follows:

string note = string.Format("{0} {1} was born in {2}",  
   firstName, lastName.ToUpper(), year); 

In this example, you specify the composite format string with three format items, namely the firstName (represented by {0}), upper-case lastName ({1}), and the year ({2}). The objects to format are specified as the following parameters.

It is also worth mentioning the interpolated string, which uses interpolated expressions to construct a string. To create a string using this approach, the $ character should be placed before ", as shown in the following example:

string note = $"{firstName} {lastName.ToUpper()}  
   was born in {year}"; 

Object

The Object class, declared in the System namespace, performs a very important role while developing applications in the C# language because it is the base class for all classes. It means that built-in value types and built-in reference types, as well as user-defined types, are derived from the Object class, which is also available by using the object alias.

As the object type is the base entity for all value types, it means that it is possible to convert a variable of any value type (for example, int or float) to the object type, as well as to convert back a variable of the object type to a specific value type. Such operations are named boxing (the first one) and unboxing (the other). They are shown as follows:

int age = 28; 
object ageBoxing = age; 
int ageUnboxing = (int)ageBoxing; 

Dynamic

Apart from the types already described, the dynamic one is available for developers. It allows the bypassing of type checking during compilation so that you can perform it during the run time. Such a mechanism is useful while accessing some application programming interfaces (APIs), but it will not be used in this book.

Classes

As already mentioned, C# is an object-oriented language and supports declaration of classes together with various members, including constructors, finalizers, constants, fields, properties, indexers, events, methods, and operators, as well as delegates. Moreover, classes support inheritance and implementing interfaces. Static, abstract, and virtual members are available, as well.

An example class is shown as follows:

public class Person 
{ 
    private string _location = string.Empty; 
    public string Name { get; set; } 
    public int Age { get; set; } 
 
    public Person() => Name = "---"; 
 
    public Person(string name, int age) 
    { 
        Name = name; 
        Age = age; 
    } 
 
    public void Relocate(string location) 
    { 
        if (!string.IsNullOrEmpty(location)) 
        { 
            _location = location; 
        } 
    } 
 
    public float GetDistance(string location) 
    { 
        return DistanceHelpers.GetDistance(_location, location); 
    } 
} 

The Person class contains the _location private field with the default value set as the empty string (string.Empty), two public properties (Name and Age), a default constructor that sets a value of the Name property to --- using the expression body definition, an additional constructor that takes two parameters and sets values of properties, the Relocate method that updates the value of the private field, as well as the GetDistance method that calls the GetDistance static method from the DistanceHelpers class and returns the distance between two cities in kilometers.

You can create an instance of the class using the new operator. Then, you can perform various operations on the object created, such as calling a method, as shown as follows:

Person person = new Person("Mary", 20); 
person.Relocate("Rzeszow"); 
float distance = person.GetDistance("Warsaw");  

Interfaces

In the previous section, a class was mentioned that could implement one or more interfaces. It means that such a class must implement all methods, properties, events, and indexers, that are specified in all implemented interfaces. You can easily define interfaces in the C# language using the interface keyword.

As an example, let's take a look at the following code:

public interface IDevice 
{ 
    string Model { get; set; } 
    string Number { get; set; } 
    int Year { get; set; } 
 
    void Configure(DeviceConfiguration configuration); 
    bool Start(); 
    bool Stop(); 
} 

The IDevice interface contains three properties, namely those representing a device model (Model), serial number (Number), and production year (Year). What's more, it has signatures of three methods, which are Configure, Start, and Stop. When a class implements the IDevice interface, it should contain the mentioned properties and methods.

Delegates

The delegate reference type allows specification of the required signature of a method. The delegate could then be instantiated, as well as invoked, as shown in the following code:

delegate double Mean(double a, double b, double c); 
 
static double Harmonic(double a, double b, double c) 
{ 
    return 3 / ((1 / a) + (1 / b) + (1 / c)); 
} 
 
static void Main(string[] args) 
{ 
    Mean arithmetic = (a, b, c) => (a + b + c) / 3; 
    Mean geometric = delegate (double a, double b, double c) 
    { 
        return Math.Pow(a * b * c, 1 / 3.0); 
    }; 
    Mean harmonic = Harmonic; 
    double arithmeticResult = arithmetic.Invoke(5, 6.5, 7); 
    double geometricResult = geometric.Invoke(5, 6.5, 7); 
    double harmonicResult = harmonic.Invoke(5, 6.5, 7); 
} 

In the example, the Mean delegate specifies the required signature of the method for calculating the mean value of three floating-point numbers. It is instantiated with the lambda expression (arithmetic), anonymous method (geometric), and named method (harmonic). Each delegate is invoked by calling the Invoke method.

 

Installation and configuration of the IDE


While reading the book, you will see many examples presenting data structures and algorithms, together with detailed descriptions. The most important parts of the code will be shown directly in the book. Moreover, complete source code will be available to download. Of course, you can only read the code from the book, but it is strongly recommended to write such code on your own, and then launch and debug the program to understand how various data structures and algorithms operate.

As already mentioned, the examples shown in the book will be prepared in the C# language. To keep things simple, the console-based applications will be created, but such data structures could be used in other kinds of solutions as well.

The example projects will be created in Microsoft Visual Studio 2017 Community. This Integrated Development Environment (IDE) is a comprehensive solution for developing various kinds of projects. To download, install, and configure it, you should:

  1. Open the website https://www.visualstudio.com/downloads/ and choose the Free download option from the Visual Studio Community 2017 section just below the Visual Studio Downloads header. The download process of the installer should begin automatically.
  2. Run the downloaded file and follow the instructions to start the installation. When the screen presenting possible options is shown, choose the .NET desktop development option, as shown in the following screenshot. Then, click Install. The installation could take some time, but its progress could be observed using the Acquiring and Applying progress bars.
  1. When the message Installation succeeded! is shown, click on the Launch button to start the IDE. You will be asked to sign in with the Microsoft account. Then, you should choose suitable Development Settings (such as Visual C#) in the Start with a familiar environment section. Moreover, you should choose the color theme from Blue, Blue (Extra Contrast), Dark, and Light. At the end, click on the Start Visual Studio button.
 

Creating the project


Just after launching the IDE, let's proceed by creating a new project. Such a process will be performed many times while reading the book to create the example applications according to information provided in particular chapters.

To create a new project:

  1. Click on File | New | Project in the main menu.
  2. Choose Installed | Visual C# | Windows Classic Desktop on the left in the New Project window, as shown in the following screenshot. Then, click on Console App (.NET Framework) in the middle. You should also type a name of the project (Name) and a name of the solution (Solution name), as well as select location for the files (Location) by pressing the Browse button. At the end, click on OK to automatically create the project and generate the necessary files:

Congratulations, you have just created the first project! But what is inside?

Let's take a look at the Solution Explorer window, which presents the structure of the project. It is worth mentioning that the project is included in the solution with the same name. Of course, a solution could contain more than one project, which is a common scenario while developing more complex applications.

Note

If you cannot find the Solution Explorer window, you could open it by choosing the View | Solution Explorer option from the main menu. In a similar way, you could open other windows, such as Output or Class View. If you cannot find a suitable window (for example, C# Interactive) directly within the View option, let's try to find it in the View | Other Windows node.

The automatically generated project (named GettingStarted) has the following structure:

  • The Properties node with one file (AssemblyInfo.cs) that contains general information about the assembly with the application, such as about its title, copyright, and version. The configuration is performed using attributes, for example, AssemblyTitleAttribute and AssemblyVersionAttribute.
  • The References element presents additional assemblies or projects that are used by the project. It is worth noting that you could easily add references by choosing the Add Reference option from the context menu of the References element. Moreover, you could install additional packages using the NuGet Package Manager, which could be launched by choosing Manage NuGet Packages from the References context menu.

Note

It is a good idea to take a look at packages already available before writing the complex module on your own because a suitable package could be already available for developers. In such a case, you could not only shorten the development time, but also reduce the chance of introducing mistakes.

  • The App.config file contains the Extensible Markup Language (XML)-based configuration of the application, including the number of the minimum supported version of the .NET Framework platform.
  • The Program.cs file contains the code of the main class in the C# language. You could adjust the behavior of the application by changing the following default implementation:
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
 
namespace GettingStarted 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
        } 
    } 
} 

The initial content of the Program.cs file contains the definition of the Program class within the GettingStarted namespace. The class contains the Main static method, which is called automatically when the application is launched. The five using statements are included as well, namely System, System.Collections.Generic, System.Linq, System.Text, and System.Threading.Tasks.

Before proceeding, let's take a look at the structure of the project in the file explorer, not in the Solution Explorer window. Are such structures exactly the same?

Note

You could open the directory with the project in the file explorer by choosing the Open Folder in File Explorer option from the context menu of the project node in the Solution Explorer window.

First of all, you can see the bin and obj directories, which are generated automatically. Both contain Debug and Release directories, whose names are related to the configuration set in the IDE. After building the project, a subdirectory of the bin directory (that is, Debug or Release) contains .exe, .exe.config, and .pdb files, while the subdirectory in the obj directory—for example—contains .cache and some temporary .cs files. What's more, there is no References directory, but there are .csproj and .csproj.user files with XML-based configurations of the project. Similarly, the solution-based .sln configuration file is located in the solution's directory.

Note

If you are using a version control system, such as SVN or Git, you could ignore the bin and obj directories, as well as the .csproj.user file. All of them can be generated automatically.

If you want to learn how to write some example code, as well as launch and debug the program, let's proceed to the next section.

 

Input and output


Many examples shown in the following part of the book will require interaction with the user, especially by reading input data and showing output. You can easily add such features to the application, as explained in this section.

Reading from input

The application can read data from the standard input stream using a few methods from the Console static class from the System namespace, such as ReadLine and ReadKey. Both are presented in the examples in this section.

Let's take a look at the following line of code:

string fullName = Console.ReadLine(); 

Here, you use the ReadLine method. It waits until the user presses the Enter key. Then, the entered text is stored as a value of the fullName string variable.

In a similar way, you can read data of other types, such as int, as shown as follows:

string numberString = Console.ReadLine(); 
int.TryParse(numberString, out int number); 

In this case, the same ReadLine method is called and the entered text is stored as a value of the numberString variable. Then, you just need to parse it to int and store it as a value of the int variable. How can you do that? The solution is very simple—use the TryParse static method of the Int32 struct. It is worth mentioning that such a method returns a Boolean value, indicating whether the parsing process has finished successfully. Thus, you can perform some additional actions when the provided string representation is incorrect.

A similar scenario, regarding the DateTime structure and the TryParseExact static method, is shown in the following example:

string dateTimeString = Console.ReadLine(); 
if (!DateTime.TryParseExact( 
    dateTimeString, 
    "M/d/yyyy HH:mm", 
    new CultureInfo("en-US"), 
    DateTimeStyles.None, 
    out DateTime dateTime)) 
{ 
    dateTime = DateTime.Now; 
} 

This example is more complicated than the previous one, so let's explain it in detail. First of all, the string representation of the date and time is stored as a value of the dateTimeString variable. Then, the TryParseExact static method of the DateTime struct is called, passing five parameters, namely the string representation of the date and time (dateTimeString), the expected format of the date and time (M/d/yyyy HH:mm), the supported culture (en-US), the additional styles (None), as well as the output variable (dateTime) passed by reference using the out parameter modifier.

If the parsing is not completed successfully, the current date and time (DateTime.Now) is assigned to the dateTime variable. Otherwise, the dateTime variable contains the DateTime instance consistent with the string representation provided by the user.

Note

While writing the part of code involving the CultureInfo class name, you could see the following error: CS0246 The type or namespace name 'CultureInfo' could not be found (are you missing a using directive or an assembly reference?). This means that you do not have a suitable using statement at the top of the file. You can easily add one by clicking on the bulb icon shown in the left-hand margin of the line with the error and choosing the using System.Globalization; option. The IDE will automatically add the missing using statement and the error will disappear.

Apart from reading the whole line, you can also get to know which character or function key has been pressed by the user. To do so, you can use the ReadKey method, as shown in the following part of code:

ConsoleKeyInfo key = Console.ReadKey(); 
switch (key.Key) 
{ 
    case ConsoleKey.S: /* Pressed S */ break; 
    case ConsoleKey.F1: /* Pressed F1 */ break; 
    case ConsoleKey.Escape: /* Pressed Escape */ break; 
} 

After calling the ReadKey static method and once any key has been pressed by a user, information about the pressed key is stored as the ConsoleKeyInfo instance (that is, key, in the current example). Then, you use the Key property to get an enumeration value (of ConsoleKey) representing a particular key. At the end, the switch statement is used to perform operations depending on the key that has been pressed. In the example shown, three keys are supported, namely S, F1, and Esc.

Writing to output

Now, you know how to read input data, but how can you ask questions to the user or present results on the screen? The answer, together with examples, is shown in this section.

Similarly as in the case of reading data, operations related to the standard output stream are performed using methods of the Console static class from the System namespace, namely Write and WriteLine. Let's see them in action!

To write some text, you can just call the Write method, passing the text as a parameter. An example of code is as follows:

Console.Write("Enter a name: "); 

The preceding line causes the following output to be shown:

Enter a name:

What's important here is that the written text is not followed by the line terminator. If you want to write some text and move to the next line, you can use the WriteLine method, as shown in the following code snippet:

Console.WriteLine("Hello!"); 

After executing this line of code, the following output is presented:

Hello!

Of course, you can also use Write and WriteLine methods in more complex scenarios. For example, you can pass many parameters to the WriteLine method, namely the format and additional arguments, as shown in the following part of the code:

string name = "Marcin"; 
Console.WriteLine("Hello, {0}!", name); 

In this case, the line will contain Hello, a comma, a space, a value of the name variable (that is, Marcin), as well as the exclamation mark. The output is shown as follows:

Hello, Marcin!

The next example presents a significantly more complex scenario of writing the line regarding the confirmation of a table reservation at a restaurant. The output should have the format Table [number] has been booked for [count] people on [date] at [time]. You can achieve this goal by using the WriteLine method, as shown as follows:

string tableNumber = "A100"; 
int peopleCount = 4; 
DateTime reservationDateTime = new DateTime( 
    2017, 10, 28, 11, 0, 0); 
CultureInfo cultureInfo = new CultureInfo("en-US"); 
Console.WriteLine( 
    "Table {0} has been booked for {1} people on {2} at {3}", 
    tableNumber, 
    peopleCount, 
    reservationDateTime.ToString("M/d/yyyy", cultureInfo), 
    reservationDateTime.ToString("HH:mm", cultureInfo)); 

The example starts with a declaration of four variables, namely tableNumber (A100), peopleCount (4), and reservationDateTime (10/28/2017 at 11:00 AM), as well as cultureInfo (en-US). Then, the WriteLine method is called passing five parameters, namely the format string followed by arguments that should be shown in the places marked with {0}, {1}, {2}, and {3}. It is worth mentioning the last two lines, where the string presenting date (or time) is created, based on the current value of the reservationDateTime variable.

After executing this code, the following line is shown in the output:

Table A100 has been booked for 4 people on 10/28/2017 at 11:00

Of course, in real-world scenarios, you will use read- and write-related methods in the same code. For example, you can ask a user to provide a value (using the Write method) and then read the text entered (using the ReadLine method).

This simple example, which is also useful in the next section of this chapter, is shown as follows. It allows the user to enter data relating to the table reservation, namely the table number and the number of people, as well as the reservation date. When all of the data is entered, the confirmation is presented. Of course, the user will see information about the data that should be provided:

using System; 
using System.Globalization; 
 
namespace GettingStarted 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            CultureInfo cultureInfo = new CultureInfo("en-US"); 
 
            Console.Write("The table number: "); 
            string table = Console.ReadLine(); 
 
            Console.Write("The number of people: "); 
            string countString = Console.ReadLine(); 
            int.TryParse(countString, out int count); 
 
            Console.Write("The reservation date (MM/dd/yyyy): "); 
            string dateTimeString = Console.ReadLine(); 
            if (!DateTime.TryParseExact( 
                dateTimeString, 
                "M/d/yyyy HH:mm", 
                cultureInfo, 
                DateTimeStyles.None, 
                out DateTime dateTime)) 
            { 
                dateTime = DateTime.Now; 
            } 
 
            Console.WriteLine( 
                "Table {0} has been booked for {1} people on {2}  
                 at {3}", 
                table, 
                count, 
                dateTime.ToString("M/d/yyyy", cultureInfo), 
                dateTime.ToString("HH:mm", cultureInfo)); 
        } 
    } 
} 

The preceding code snippet is based on the parts of code shown and described previously. After launching the program and entering the necessary data, the output could look as follows:

The table number: A100The number of people: 4The reservation date (MM/dd/yyyy): 10/28/2017 11:00Table A100 has been booked for 4 people on 10/28/2017 at 11:00Press any key to continue . . . 

When the code is created, it is a good idea to improve its quality. One of the interesting possibilities associated with the IDE is related to removing unused using statements, together with sorting the remaining ones. You can easily perform such an operation by choosing the Remove and Sort Usings option from the context menu in the text editor.

 

Launching and debugging


Unfortunately, the written code doesn't always work as expected. In such a case, it is a good idea to start debugging to see how the program operates, find the source of the problem, and correct it. This task is especially useful for complex algorithms, where the flow could be complicated, and therefore quite difficult to analyze just by reading the code. Fortunately, the IDE is equipped with various features for debugging that will be presented in this section.

First of all, let's launch the application to see it in action! To do so, you just need to select a proper configuration from the drop-down list (Debug, in this example) and click on the button with the green triangle and the Start caption in the main toolbar, or press F5. To stop debugging, you can choose Debug | Stop Debugging or press Shift + F5.

Note

You can also run the application without debugging. To do so, choose Debug | Start Without Debugging from the main menu or press Ctrl + F5.

As already mentioned, there are various debugging techniques, but let's start with breakpoint-based debugging, since it is one of the most common approaches offering huge opportunities. You can place a breakpoint in any line of the code. The program will stop as soon as the line is reached, before executing it. Then, you can see the values of particular variables to check whether the application works as expected.

To add a breakpoint, you can either click on the left-hand margin (next to the line on which the breakpoint should be placed) or place the cursor on the line (where the breakpoint should be added) and press the F9 key. In both cases, the red circle will be shown, as well as the code from the given line will be marked with a red background, as shown in line 17 in the following screenshot:

When a line with the breakpoint is reached while executing the program, it stops, and the line is marked with the yellow background and the margin icon changes, as shown in line 15 in the screenshot. Now, you can check the value of the variable by simply moving the cursor over its name. The current value will appear in the ToolTip.

You can also click on the pin icon located on the right-hand side of the ToolTip to pin it in the editor. Its value will then be visible without the necessity of moving the cursor over the name of the variable. Such a value will be automatically refreshed as soon as it has changed. The result is presented in the following screenshot.

The IDE could adjust its appearance and features depending on the operations performed currently. For example, while debugging, you have access to some special windows, such as Locals, Call Stack, and Diagnostic Tools. The first shows available local variables together with their types and values. The Call Stack window presents information about the following called methods. The last one (namely Diagnostic Tools) shows information about memory and CPU usage, as well as events.

Moreover, the IDE supports conditional breakpoints that stop execution of the program only if the associated Boolean expression is evaluated to true. You can add a condition to a given breakpoint by choosing the Conditions option from the context menu, which is shown after right-clicking on the breakpoint icon in the left-hand margin. Then, the Breakpoint Settings window appears, where you should check the Conditions checkbox and specify the Conditional Expression, such as the one shown in the following screenshot. In the example, execution will stop only when the value of the count variable is greater than 5, that is, count > 5:

When the execution is stopped, you can use the step-by-step debugging technique. To move execution of the program to the next line (without incorporating another breakpoint), you can click on the Step Over icon in the main toolbar or press F10. If you want to step into the method, which is called in the line where the execution has stopped, just click on the Step Into button or press F11. Of course, you can also go to the next breakpoint by clicking on the Continue button or by pressing F5.

The next interesting feature, available in the IDE, is called Immediate Window. It allows developers to execute various expressions when the program execution is stopped using the current values of the variables. You just need to enter an expression in the Immediate Window and press the Enter key. The example is shown in the following screenshot:

Here, the lower-case version of the table number is returned by executingtable.ToLower(). Then, the total number of minutes between the current date and the dateTimevariable is calculated and shown in the window.

 

Summary


This was only the first chapter of the book, but it contained quite a lot of information that will be useful while reading the remaining ones. At the beginning, you saw that using proper data structures and algorithms is not an easy task, but could have a significant impact on the performance of the developed solution. Then, the C# programming language was briefly presented with a focus on showing various data types, both value and reference ones. Classes, interfaces, and delegates were also described.

In the following part of the chapter, the process of installation and configuration of the IDE was presented. Then, you learned how to create a new project, and its structure has been described in details. Next, you saw how to read data from the standard input stream, as well as how to write data to the standard output stream. The read- and write-related operations were also mixed into one example.

At the end of the chapter, you saw how to run the example program, as well as how to debug it using breakpoints and step-by-step debugging to find the source of the problem. What's more, you learned the possibilities of the Immediate Window feature.

After this introduction, you should be ready to proceed to the next chapter and see how to use arrays and lists, as well as accompanying algorithms. Let's go!

About the Author

  • Marcin Jamro

    Marcin Jamro, PhD, is an entrepreneur and researcher, as well as a developer and architect of various kinds of applications. He is the President of the Board at TITUTO Sp. z o.o. [Ltd.] in Rzeszów, Poland.

    Marcin is interested in many aspects of computer science, including software engineering and project management. He is an author of two other books, namely Windows Phone 8 Game Development (2013) and Windows Application Development Cookbook (2017), both by Packt Publishing.

    Marcin has published several papers, participated in many conferences, organized a few of them, and participated in two internships at Microsoft in Redmond, USA. He has the MCP, MCTS, and MCPD certificates.

    Browse publications by this author

Latest Reviews

(5 reviews total)
Muy didáctico
Der Stoff wird vollständig und für mich gut verständlich erklärt, die Codebeispiele sind nachvollziehbar und können als Ausgangspunkt für eigene Anwendungen dienen. Perfekt!
It's a very good book on a subject that unfortunately there are not many resources for in C#. Loved the pacing, and content is excellent.

Recommended For You

Hands-On Design Patterns with C# and .NET Core

Apply design patterns to solve problems in software architecture and programming using C# 7.x and .NET Core 2

By Gaurav Aroraa and 1 more
Hands-On Network Programming with C# and .NET Core

A comprehensive guide to understanding network architecture, communication protocols, and network analysis to build secure applications compatible with the latest versions of C# 8 and .NET Core 3.0

By Sean Burns
Hands-On Software Architecture with C# 8 and .NET Core 3

Design scalable and high-performance enterprise applications using the latest features of C# 8 and .NET Core 3

By Francesco Abbruzzese and 1 more
Mastering Visual Studio 2019 - Second Edition

Explore the new features in Visual Studio 2019 and build apps using WPF, .NET Core, TypeScript, and Azure

By Kunal Chowdhury