Learn C# Programming

4 (1 reviews total)
By Marius Bancila , Raffaele Rialdi , Ankit Sharma
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Chapter 1: Starting with the Building Blocks of C#

About this book

The C# programming language is often developers’ primary choice for creating a wide range of applications for desktop, cloud, and mobile. In nearly two decades of its existence, C# has evolved from a general-purpose, object-oriented language to a multi-paradigm language with impressive features.

This book will take you through C# from the ground up in a step-by-step manner. You'll start with the building blocks of C#, which include basic data types, variables, strings, arrays, operators, control statements, and loops. Once comfortable with the basics, you'll then progress to learning object-oriented programming concepts such as classes and structures, objects, interfaces, and abstraction. Generics, functional programming, dynamic, and asynchronous programming are covered in detail. This book also takes you through regular expressions, reflection, memory management, pattern matching, exceptions, and many other advanced topics. As you advance, you'll explore the .NET Core 3 framework and learn how to use the dotnet command-line interface (CLI), consume NuGet packages, develop for Linux, and migrate apps built with .NET Framework. Finally, you'll understand how to run unit tests with the Microsoft unit testing frameworks available in Visual Studio.

By the end of this book, you’ll be well-versed with the essentials of the C# language and be ready to start creating apps with it.

Publication date:
April 2020


Chapter 1: Starting with the Building Blocks of C#

C# is one of the most widely used general-purpose programming languages. It is a multi-paradigm language that combines object-oriented, imperative, declarative, functional, generic, and dynamic programming. C# is one of the programming languages designed for the Common Language Infrastructure (CLI) platform, which is an open specification developed by Microsoft and standardized by the International Organization for Standardization (ISO) and European Computer Manufacturers Association (ECMA) that describes executable code and a runtime environment to be used on different computer platforms without being rewritten for specific architectures.

Over the years, C# has evolved with powerful features released version by version. The most recent version (at the time of writing) is C# 8, which has introduced several features to empower developers to be more productive. These include nullable reference types, ranges and indices, asynchronous streams, default implementations of interface members, recursive patterns, switch expressions, and many others. You will learn about these features in detail in Chapter 15, New Features of C# 8.

In this chapter, we will introduce you to the language, the .NET Framework, and the basic concepts around them. We have structured the contents of this chapter as follows:

  • Learning the history of C#
  • Understanding the CLI
  • Knowing the .NET family of frameworks
  • Assemblies in .NET
  • Understanding the basic structure of a C# program

At the end of this chapter, you will learn how to write a Hello World! program in C#.


The history of C#

C# development started at Microsoft in the late 1990s by a team led by Anders Hejlsberg. Initially, it was called Cool, but when the .NET project was first publicly announced in the summer of 2002, the language was renamed C#. The use of the sharp suffix was intended to denote that the language is an increment of C++, which, along with Java, Delphi, and Smalltalk, acted as an inspiration for the CLI and the C# language design.

The first version of C#, called 1.0, was made available in 2002 bundled with .NET Framework 1.0 and Visual Studio .NET 2002. Since then, major and minor increments of the language have been released together with new versions of .NET Framework and Visual Studio. The following table lists all of the versions and some of the key features for each of these releases:

The latest version of the language at the time of writing, 8.0, is being released with .NET Core 3.0. Although most features will also work in projects targeting .NET Framework, some of them will not because they require changes in the runtime, which is something Microsoft will no longer do as .NET Framework is being deprecated in favor of .NET Core.

Now that you have an overview of the evolution of the C# language over time, let's start looking at the platforms that the language is targeting.


Understanding the CLI

The CLI is a specification that describes how a runtime environment can be used on different computer platforms without being rewritten for specific architectures. It is developed by Microsoft and standardized by ECMA and ISO. The following diagram shows the high-level functionality of the CLI:

Figure 1.1 – Diagram of the high-level functionality of the CLI

Figure 1.1 – Diagram of the high-level functionality of the CLI

The CLI enables programs written in a variety of programming languages (that are CLS-compliant) to be executed on any operating system and with a single runtime. The CLI specifies a common language, called the Common Language Specification (CLS), a common set of data types that any language must support, called the Common Type System, and other things such as how exceptions are handled and how the state is managed. The various aspects specified by the CLI are described in more detail in the following sections.

Information box

Because of the limited scope of this chapter, a deep dive into the specification is not possible. If you want more information about the CLI, you can visit the ISO site at https://www.iso.org/standard/58046.html.

There are several implementations of the CLI and among these, the most important ones are .NET Framework, .NET Core, and Mono/Xamarin.

Common Type System (CTS)

The CTS is a component of the CLI that describes how type definitions and values are represented and memory is intended to facilitate the sharing of data between programming languages. The following are some of the characteristics and functions of the CTS:

  • It enables cross-platform integration, type safety, and high-performance code execution.
  • It provides an object-oriented model that supports the complete implementation of many programming languages.
  • It provides rules for languages to ensure that objects and data types of objects written in different programming languages can interact with each other.
  • It defines rules for type visibility and access to members.
  • It defines rules for type inheritance, virtual methods, and object lifetime.

The CTS supports two categories of types:

  • Value types: These contain their data directly and have copy semantics, which means when an object of such a type is copied its data is copied.
  • Reference types: These contain references to the memory address where the data is stored. When an object of a reference type is copied, the reference is copied and not the data it points to.

Although it is an implementation detail, value types are usually stored on the stack and reference types on the heap. Conversion between value types and a reference type is possible and known as boxing, while the other way around is called unboxing. These concepts will be explained in further detail in the next chapter.

Common Language Specification (CLS)

The CLS comprises a set of rules that any language that targets the CLI needs to adhere to, to be able to interoperate with other CLS-compliant languages. CLS rules fall into the broader rules of the CTS and therefore it can be said that the CLS is a subset of CTS. All of the rules of CTS apply to the CLS unless the CLS rules are stricter. Language constructs that make it impossible to easily verify the type safety of the code were excluded from the CLS so that all languages that work with the CLS can produce verifiable code.

The relationship between the CTS and CLS as well as the programming languages targeting the CLI is conceptually shown in the following diagram:

Figure 1.2 – A diagram showing the conceptual relationship between the CTS and CLS and the programming languages that target the CLI

Figure 1.2 – A diagram showing the conceptual relationship between the CTS and CLS and the programming languages that target the CLI

Components built using only the rules of the CLS are called CLS-compliant. An example of such components is the framework libraries that need to work across all of the languages supported on .NET.

Common Intermediate Language (CIL)

CIL is a platform-neutral intermediate language (formerly called Microsoft Intermediate Language or MSIL) that represents the intermediate language binary instruction set defined by the CLI. It is a stack-based object-oriented assembly language that represents the code in byte-code format.

Once the source code of an application is compiled, the compiler translates it into the CIL bytecode and produces a CLI assembly. When the CLI assembly is executed, the bytecode is passed through the Just-In-Time compiler to generate native code, which is then executed by the computer's processor. The CPU and the platform-independent nature of the CIL make it possible that the code is executed on any environment supporting the CLI.

To help us to understand the CIL, let's look at an example. The following listing shows a very simple C# program that prints a Hello, World! message to the console:

using System;
namespace chapter_01
   class Program
      static void Main(string[] args)
         Console.WriteLine("Hello World!");

It is possible to view the content of the assembly produced by the compiler using various utility tools, such as ildasm.exe, which comes with .NET Framework, or ILSpy, which is an open source .NET assembly browser and decompiler (available at http://www.ilspy.net/). The ildasm.exe file shows a visual representation of the program and its components, such as classes and members:

Figure 1.3 – A screenshot of the ildasm tool showing the content of an assembly

Figure 1.3 – A screenshot of the ildasm tool showing the content of an assembly

You can also see the content of the manifest (which includes assembly metadata) as well as the CIL code for each method if you double-click on it. The following screenshot shows the disassembled code of the Main method:

Figure 1.4 – A screenshot of the ildasm tool showing the IL code of the Main method

Figure 1.4 – A screenshot of the ildasm tool showing the IL code of the Main method

A human-readable dump of the CIL code is also available. This starts with the manifest and continues with the class member's declarations. A partial listing of the CIL code for the preceding program is shown here:

// Metadata version: v4.0.30319
.assembly extern System.Runtime
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
  .ver 4:2:1:0
.assembly extern System.Console
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
  .ver 4:1:1:0
.assembly chapter_01
.module chapter_01.dll
// MVID: {1CFF5587-0C75-4C14-9BE5-1605F27AE750}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x00F30000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit chapter_01.Program
       extends [System.Runtime]System.Object
  .method private hidebysig static void  Main(string[] args) cil managed
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World!"
    IL_0006:  call       void [System.Console]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  } // end of method Program::Main
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
    IL_0006:  nop
    IL_0007:  ret
  } // end of method Program::.ctor
} // end of class chapter_01.Program

An explanation of the code here is beyond the scope of this chapter, but you can probably identify at a glance parts of it such as classes, methods, and instructions executed in each method.

Virtual Execution System (VES)

VES is a part of the CLI that represents a runtime system that provides the environment for executing the managed code. It has several built-in services to support the execution of code and handling of exceptions, among other things.

The Common Language Runtime is .NET Framework's implementation of the Virtual Execution System. Other implementations of the CLI provide their own VES implementations.


The .NET family of frameworks

.NET is a general-purpose development platform developed by Microsoft for writing a variety of types of applications for desktop, cloud, and mobile. .NET Framework was the first implementation of the CLI but, over time, a series of other frameworks have been created, such as .NET Micro Framework, .NET Native, and Silverlight. While .NET Framework works on Windows, other current implementations, such as .NET Core and Mono/Xamarin, are cross-platform and run on other operating systems, such as Linux, macOS, iOS, or Android.

The following screenshot shows the main characteristics of the current top .NET frameworks. .NET Framework is intended for developing .NET applications for Windows and is distributed with the operating system. .NET Core, which is cross-platform and open source, is optimized for modern application requirements and developer workflows and is distributed with the application. Xamarin, which uses a Mono-based runtime, is also cross-platform and open source. It is intended for developing mobile applications for iOS, macOS, Android, and Windows, and is distributed with the application:

Figure 1.5 – A diagram with the main characteristic of the most important .NET frameworks

Figure 1.5 – A diagram with the main characteristic of the most important .NET frameworks

All of these implementations are based on a common infrastructure that includes languages, compilers, and runtime components and supports a variety of application models, some of which are shown in the following screenshot:

Figure 1.6 – A high-level diagram of the .NET frameworks infrastructure and the application models they support

Figure 1.6 – A high-level diagram of the .NET frameworks infrastructure and the application models they support

Here, you can see that each framework resides on top of the common infrastructure and provides a set of base libraries as well as different application models.

.NET Framework

.NET Framework was the first implementation of the CLI. It is the primary development platform for Windows Server and client developers. It contains a large class library that supports many types of applications. The framework is distributed as a part of the operating system and as a result, new versions are serviced through Windows Update, although standalone installers are also available. Initially, .NET Framework was proprietary software developed by Microsoft. In recent years, parts of .NET Framework have been open-sourced.

The following table shows the history of .NET Framework, as well as the major features available in each release:

In the future, Microsoft intends to unify all .NET frameworks into a single one. At the time of writing this book, this is planned to be named .NET 5.

.NET Framework includes the Common Language Runtime (CLR), which is the execution engine of the framework that provides services such as memory management, type safety, garbage collection, exception handling, thread management, and others. It also includes an implementation of the CLI foundational standard libraries. The following is a list of the components of the standard libraries (although not all of them):

  • Base Class Library (BCL): It provides types to represent the CLI built-in types, simple file access, custom attributes, string handling, formatting, collections, streams, and others.
  • Runtime Infrastructure Library: It provides services to dynamically load types from a stream and other services that allow the compiler to target the CLI.
  • Reflection Library: It provides services that make it possible to examine the structure of types at runtime, instantiate objects, and invoke methods.
  • Network Library: It provides networking services.
  • Extended Numerics Library: It provides support for floating-point and extended-precision data types.
  • Parallel Library: It provides parallelism in simple forms.

Apart from these libraries, the .NET Framework Class Library (FCL) includes many other libraries, such as WPF, WinForms, WCF, LINQ, and others. Most of these are in the System.* or Microsoft.* namespaces.

A key aspect of developing in C# for the .NET platform is how memory is managed. In general, developers do not have to worry about the lifetime of objects and the disposal of memory. Memory management is automatically done by the CLR through the Garbage Collector (GC). The GC handles the allocation of objects on the heap and the disposal of memory when heap objects are no longer used.

The garbage collection is a non-deterministic process because it happens on a per-need basis and not at some deterministic moments. A detailed description of the way the garbage collection works is provided in Chapter 9, Resource Management.

.NET Core

.NET Core is a new implementation of the CLI that is cross-platform, open source, and modular. It is intended for developing a variety of applications, such as web apps, micro-services, libraries, or console apps that run on Windows, Linux, and macOS. The .NET Core framework is packaged using NuGet; as a result, it is either compiled directly into an application or put into a folder inside the application. Therefore, .NET Core applications distribute the framework components directly, although a cache system for a centralized deployment, called runtime package store, is also available starting with version 2.0.

The implementation of the VES for .NET Core is called CoreCLR. Similarly, the implementation of the CLI foundational standard libraries is called CoreFX.

ASP.NET Core is a part of .NET Core but also runs on the .NET Framework CLR. However, an ASP.NET Core app is cross-platform only when targeting .NET Core.

With the release of version 3.0 in September 2019, developers can create web apps, micro-services, desktop applications, machine learning, and AI applications, IoT applications, libraries, and console applications using .NET Core.

You will learn more about .NET Core in Chapter 16, C# in Action with .NET Core 3.


Xamarin is a CLI implementation based on Mono, which is a cross-platform, open source .NET framework. In general, Mono APIs followed the progress of .NET Framework and not .NET Core. The framework is intended for writing mobile applications that can run on iOS, Android, macOS, and Windows devices.

Applications developed with Xamarin are native, which provides similar performance to those developed with Objective-C or Swift for iOS and Java or Kotlin for Android. Xamarin also provides facilities to directly invoke Objective-C, Java, C, and C++ libraries.

Xamarin applications are written in C# and use the .NET Base Class Library. They can share most of the code, with only a small portion needed to be platform-specific.

Detailed information about Xamarin is beyond the scope of this book. If you want to learn more about this implementation, you should use additional resources.


Assemblies in .NET

An assembly is a basic unit for deployment, versioning, and security. Assemblies come in two forms, either as an executable file (.exe) or a dynamic-linked library (.dll). An assembly is a collection of types, resources, and meta-information that forms a logical unit of functionality. Assemblies are loaded into memory only if needed. For .NET Framework applications, assemblies could either be located in the application private folder or shared in the Global Assembly Cache, provided they are strongly-named. For .NET Core applications, this latter solution is not available.

Each assembly contains a manifest that contains the following information:

  • The identity of the assembly (such as name and version)
  • A file table describing the files that make up the assembly, such as other assemblies or resources (such as images)
  • A list of assembly references that contains the external dependencies that the application needs

The identity of an assembly is composed of several parts:

  • The name of the file where the name should be compliant with the Windows Portable Executable file format
  • A version of the form of major.minor.build.revision, such as
  • The culture that should be locale-agnostic except in the case of satellite assemblies (which are locale-aware assemblies)
  • The public key token, which is a 64-bit hash of the private key used to sign the assembly; signed assemblies have strong names that are meant to provide a unique name

You will learn more about assemblies in Chapter 11, Reflection and Dynamic Programming.

Global Assembly Cache (GAC)

As mentioned in the preceding section, .NET Framework assemblies could either be stored locally, in the application folder, or in GAC. This is a machine-wide code cache that enables the sharing of assemblies between applications. Since the release of .NET Framework 4, the default location for the GAC is %windir%\Microsoft.NET\assembly; however, previously, the location was %windir%\assembly. GAC also enables storing multiple versions of the same assembly, which is not actually possible in a private folder, since you cannot store multiple files with the same name in the same folder.

To deploy an assembly to the GAC, you could use the Windows SDK utility tool called gacutil.exe or an installer that is able to work with the GAC. However, an assembly must have a strong name to be deployed to the GAC. A strong-name assembly is an assembly cryptographically signed with a private key that corresponds to a public key distributed with the assembly. You can sign an assembly using the Strong Name tool (sn.exe).


For more details about how to sign an assembly, please refer to the following document, which describes how to sign an assembly with a strong name: https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-sign-an-assembly-with-a-strong-name.

When you add an assembly to GAC, integrity checks are performed on all of the files contained by the assembly. This is done to ensure that the assembly has not been tampered with. The cryptographic signing ensures that any change to any of the files in the assembly invalidates the signature and only someone that has access to the private key can resign the assembly.

Runtime package store

The GAC is not used for .NET Core assemblies. These are assemblies that can run on any platform and not just Windows. Prior to .NET Core 2.0, the only option for deployment was the application folder. Since version 2.0, however, it is possible to package and deploy applications against a known set of packages that exist in the target environment. This enables faster deployment and lower disk space requirements. Typically, this store is available at /usr/local/share/dotnet/store on macOS and Linux and C:/Program Files/dotnet/store on Windows.

The packages available in the runtime package store are listed in a target manifest file that is used while publishing an application. This file has a format that is compatible with the project file format (.csproj).

Detailing the targeting process is beyond the scope of this chapter, but you can learn more about the runtime package store by visiting the following link: https://docs.microsoft.com/en-us/dotnet/core/deploying/runtime-store.


Understanding the basic structure of a C# program

So far, we have learned about the basics of C# and the .NET runtime. In this section, we will write a simple C# program so that we can have a short introduction to some of the key elements of a simple program.

Before writing a program, you must create a project. For this purpose, you should use Visual Studio 2019; alternatively, you could use any other version for most of the content of this book. The source code accompanying this book was written in Visual Studio 2019 using .NET Core projects. When creating a new project, select Console App (.NET Core) and call the project chapter_01:

Figure 1.7 – Select the Console App (.NET Core) template when creating 
a new project in Visual Studio

Figure 1.7 – Select the Console App (.NET Core) template when creating a new project in Visual Studio

A project with the following content will be automatically created for you:

Figure 1.8 – Screenshot of Visual Studio and the code generated for the selected template

Figure 1.8 – Screenshot of Visual Studio and the code generated for the selected template

This code represents the minimum a C# program must contain: a single file with a single class having a single method called Main. You can compile and run the project and the message Hello World! will be displayed to the console. However, to better understand it, let's look at the actual C# program.

The first line of the program (using System;) declares the namespaces that we want to use in this program. A namespace contains types and the one used here is the core namespace of the base class library.

On the following line, we define our own namespace, called chapter_01, which contains our code. A namespace is introduced with the namespace keyword. In this namespace, we define a single class called Program. A class is introduced with the class keyword. Furthermore, this class contains a single method called Main, with a single argument that is an array of strings called args. The code within namespaces, types (whether it's a class, struct, interface, or enum), and methods is always provided within curly braces {}. This method is the entry point of the program, which means it's where the execution of a program starts. A C# program must have one and only one Main method.

The Main method contains a single line of code. It uses the System.Console.WriteLine static method to print a text to the console. A static method is a method that belongs to a type and not an instance of the type, which means you do not call it through an object. The Main method is itself a static method, but furthermore, it is a special method. Every C# program must have a single static method called Main, which is considered the entry point of the program and the first to be called when the execution of the program begins.

Throughout the next chapters, we will learn about namespaces, types, methods, and other key features of C#.



In this chapter, we looked in short at the history of C#. We then explored the basic concepts behind the CLI and its constituents, such as CTS, CLS, CIL, and VES. Then, we looked at the .NET family of frameworks and briefly discussed .NET Framework, .NET Core, and Xamarin. We also talked about assemblies, the GAC (for .NET Framework) and the runtime package store (for .NET Core). Finally, we wrote our first C# program and looked at its structure.

This overview of the frameworks and the runtime will help you to understand the context of writing and executing a C# program and will provide a good background when we talk about more advanced features such as reflection, assembly loading, or look at the .NET Core framework.

In the next chapter, we will explore the basic data types and operators in C# and learn how to work with them.


Test what you learned

  1. When was C# first released and what is the current version of the language?
  2. What is the Common Language Infrastructure? What are its main components?
  3. What is the Common Intermediate Language and how is it related to the Just-In-Time compiler?
  4. What tools can you use to disassembly and explore the assemblies produced by the compiler?
  5. What is the Common Language Runtime?
  6. What is the Base Class Library?
  7. What are currently the major .NET frameworks? Which one will no longer be developed?
  8. What is an assembly? What constitutes the identity of an assembly?
  9. What is the Global Assembly Cache? What about the runtime package store?
  10. What is the minimum a C# program must contain to be executed?

About the Authors

  • Marius Bancila

    Marius Bancila is a software engineer with almost two decades of experience in developing solutions for the industrial and financial sectors. He is the author of The Modern C++ Challenge and co-author of Learn C# Programming. He works as a software architect and is focused on Microsoft technologies, mainly developing desktop applications with C++ and C#. He is passionate about sharing his technical expertise with others and, for that reason, he has been recognized as a Microsoft MVP for C++ and later developer technologies since 2006. Marius lives in Romania and is active in various online communities.

    Browse publications by this author
  • Raffaele Rialdi

    Raffaele Rialdi is a senior software architect working as a consultant, speaker, and trainer. Since 2003, he is a Microsoft MVP in the Developer Security category. His passion for the community leads him to be a member of the board of UGIdotNET, president of DotNetLiguria, and co-founder of the Italian C++ user group. Currently, he is working as an architect and developer on the backend of an enterprise project with a specific focus on code generation, and working on cross-platform mobile and IoT development in both C# and C++. You can find him on Twitter with the handle @raffaeler.

    Browse publications by this author
  • Ankit Sharma

    Ankit Sharma is a software engineer currently working as Senior Member Technical with ADP in Hyderabad, India. He has over six years of extensive experience with Microsoft technologies, including C#, ASP.NET, and SQL Server, and UI technologies such as jQuery, Angular, and Blazor. Ankit is a technical author and speaker and loves to contribute to the open source community. He writes articles for multiple platforms, including c-sharpcorner, Dzone, Medium, and freeCodeCamp. For his dedicated contribution to the developer's community, he has been recognized as c-sharpcorner MVP, Dzone MVB, and a top contributor in the technology category on Medium. He is also the author of the first ever book on Blazor – Blazor Quick Start Guide. You can tweet him at @ankitsharma_007.

    Browse publications by this author

Latest Reviews

(1 reviews total)
I can review all my knowledge

Recommended For You

The JavaScript Workshop

Cut through the noise and get real results with a step-by-step approach to beginner JavaScript development

By Joseph Labrecque and 7 more
ASP.NET Core 3 and React

Build modern, scalable, and cloud-ready single-page applications using ASP.NET Core, React, TypeScript, and Azure

By Carl Rippon
The Python Workshop

Cut through the noise and get real results with a step-by-step approach to learning Python 3.X programming

By Andrew Bird and 4 more
Expert C++

Design and architect real-world scalable C++ applications by exploring advanced techniques in low-level programming, object-oriented programming (OOP), the Standard Template Library (STL), metaprogramming, and concurrency

By Vardan Grigoryan and 1 more