But that was not the title of the first two editions?!? That’s correct. After thoughtful consideration, the book has a new title! Why? All editions were never only about design patterns, which is even more true for the 3rd edition, which expands even more than before into architectural styles and application organization, offering diverse strategies for structuring ASP.NET Core applications. Of course, I wanted to keep the essence of the first two editions, so here’s the subtitle that brings that continuity: An Atypical Design Patterns Guide for .NET 8, C# 12, and Beyond.
Have you noticed the and Beyond suffix? Well, that’s because the book is good not only for .NET 8 and C# 12, but you’ll also be able to leverage its content for future versions, and we wanted to clarify this.
Let’s start with the updated Table of Content:
Section 1: Principles and Methodologies
1. Introduction
2. Automated Testing
3. Architectural Principles
4. REST APIs
Section 2: Designing with ASP.NET Core
5. Minimal API
6. Model-View-Controller
7. Strategy, Abstract Factory, and Singleton Design Patterns
8. Dependency Injection
9. Application Configuration and the Options Pattern
10. Logging patterns
Section 3: Components Patterns
11. Structural Patterns
12. Behavioral Patterns
13. Operation Result Pattern
Section 4: Application Patterns
14. Layering and Clean Architecture
15. Object Mappers
16. Mediator and CQS Patterns
17. Vertical Slice Architecture
18. Request-EndPoint-Response (REPR)
19. Introduction to Microservices Architecture
20. Modular Monolith
Have you noticed? We removed the UI chapters and replaced them with more REST APIs and backend content! Yes! This third edition is a testament to our commitment to relevancy and depth, which is now exclusively focused on developers striving for robust REST API and backend design knowledge.
Architecting ASP.NET Core Applications is your gateway to mastering REST API and backend designs. It gives you the know-how for building robust and maintainable apps grounded in Gang of Four (GoF) design patterns and well-known architectural principles like SOLID, DRY, and YAGNI. The book focuses on the technical architecture mindset. It is written as a journey where we improve code over time, rework and refactor examples, and more to ensure you understand the logic behind the techniques we are covering. At the end of the book, I want you to understand the choices that we made so you can apply a similar way of thinking to your real-world problems, which are not covered in any books because each challenge is unique in the real world!
This book is a deep dive into the architectural essence of building enduring ASP.NET Core applications. The third edition is here to quench your thirst for knowledge with an expanded section on Minimal APIs, more automated testing content, more architectural building blocks, more ways to organize your applications, and a closing chapter about building a modular monolith. We explore many application-building techniques, from layering to microservices.
The sections are reimagined for a smoother learning journey, and the content has been revised to improve the clarity of each chapter. Chapters now prioritize REST API design and patterns, shedding extraneous UI code to concentrate on what truly matters in backend development.
Chapter 2 has been overhauled to cover testing approaches like black-box, white-box, and grey-box testing. The foundational architectural principles are rearranged, and the chapter is improved to establish the groundwork for modern application design even better than before. Two new chapters now focus on REST APIs and Minimal APIs, while a third chapter about building Web APIs using MVC was updated.
I improved and increased the number of real-world-like examples where numerous code projects have been updated or rewritten completely. The Dependency Injection chapter benefited from significant updates as well. I split the options and logging chapter in two, and improved the content.
Many other changes were applied, like improving the heading of chapters for easier navigation, and all chapters benefitted from content tweaks, diagram updates, code sample revamps, and more. On top of that, I added new content around open-source tools like Mapperly, MassTransit, and Refit.
Additionally, Chapter 18 is a new chapter dedicated to the Request-EndPoint-Response (REPR) pattern using Minimal APIs. I also listened to your feedback and added some code to the Microservices chapter. The new microservices project extends the REPR code sample to microservices architecture and introduces API layering with a Backend For Frontend (BFF) example. Finally, Chapter 20 is also new, discusses modular monolith architecture, and builds on top of Chapters 18 and 19’s new e-commerce examples. That last project, rebuilt in three flavors, is a larger implementation that combines more building blocks like a real app would while keeping it small enough to fit in a book.
Once again, this release is crafted for intermediate ASP.NET Core developers eager to refine their knowledge of design patterns and application development. Software architects keen on revitalizing their theoretical and hands-on expertise will also find this third edition an invaluable ally. With comprehensive coverage of updated architectural patterns, RESTful design, SOLID principles, and a touch of microservices, this edition stands as a pillar of modern backend application design.
For the best experience, I recommend you read the book cover to cover first to ensure you understand the decisions behind the refactoring and improvements we make throughout. Of course, afterward, you can use it as a reference and browse it however you please.
Join me on an exceptional learning path that will revolutionize your ASP.NET Core application architecture perspective. The third edition awaits you, promising a transformative encounter with REST API and backend design unlike anything you’ve experienced before.
Here is a reference to certain terms and acronyms from the article. Hopefully, these definitions will help you out.
REST API: REST (Representational State Transfer) API is a design style that uses HTTP requests to access and use data, allowing applications to communicate and exchange data in a standardized format, enhancing interoperability and simplicity in web services.
RESTful: RESTful refers to web services that adhere to REST principles, enabling seamless and efficient interaction between clients and servers through standardized HTTP operations.
Gang of Four (GoF) design patterns: These foundational patterns, identified by four authors, offer solutions to common design challenges, improving code reusability and maintainability.
GoF Design Patterns (Strategy, Abstract Factory, Singleton): Strategy enables selecting algorithms at runtime; Abstract Factory offers an interface for creating families of related objects; Singleton ensures a class has only one instance and provides a global point of access to it.
SOLID, DRY, and YAGNI principles: SOLID represents five principles of object-oriented design that increase software maintainability; DRY (“Don’t Repeat Yourself”) emphasizes avoiding code duplication; YAGNI (“You Aren’t Gonna Need It”) advises against adding unnecessary functionality.
Minimal API: ASP.NET Core Minimal APIs enable fast and efficient REST endpoint creation with minimal code, dependencies, and configuration, focusing on simplicity and performance while streamlining route and action declaration without needing traditional scaffolding or controllers.
Model-View-Controller (MVC): MVC is a design pattern that separates an application into three main components—Model, View, and Controller—to isolate business logic, user interface, and user input.
Dependency Injection: This technique allows the creation of dependent objects outside of a class and provides those objects to the class, improving modularity and testability and breaking tight coupling.
REPR (Request-EndPoint-Response) Pattern: This pattern promotes the simple routing and handling of HTTP requests by directly associating requests with their handling functions and responses, promoting clean and readable code.
Microservices Architecture: This architecture style structures an application as a collection of small, autonomous services, improving modularity and scalability.
Modular Monolith: A modular monolith organizes code into modules within a single application, combining the simplicity of a monolith with modular flexibility, easing maintenance and deployment.
]]>This article is part of a learn programming series where you need no prior knowledge of programming. If you want to learn how to program and want to learn it using .NET/C#, this is the right place. I suggest reading the whole series in order, starting with Creating your first .NET/C# program, but that’s not mandatory.
Before beginning, it is essential to know what we are aiming at.
A variable is an identifier that we can use programmatically to access a value stored in memory.
Let’s break that down, an identifier is a name, a way to identify your variable. We then can use that identifier to read or write data into memory. That memory is the random-access memory (RAM) of the computer executing the code. But you don’t even have to worry about that because .NET manages most of it for you. First, all you need to know is how to declare a variable and how to use it.
There are many different types of variables and ways to optimize memory usage. Still, the concept always remains similar to what we are covering now.
var
keywordC# offers many ways of creating variables.
The most straightforward one is to use the var
keyword.
The syntax is as follow:
var identifier = initial_value;
Next, let’s dissect this line of code.
var
keywordvar
is a keyword to create a variable.
It implicitly represents the type of that variable.
That type is inferred from the value of initial_value
, the right-end part of the declaration statement.
C# is a strongly-typed language, so it requires each variable to be of a specific type. The type of a variable cannot change after being declared, but its value can. We will explore types more in-depth in future articles, but for now, think of the type as the type of data that we want to use, for example, a string (text) or a number.
An alternative way to declare a variable is to use C# 1.0 syntax and use the type of the variable directly instead of var
.
Here are a few examples:
var identifier = "Hello C# Ninja!"; // This is the way we are exploring
string identifier = "Hello C# Ninja!"; // This is the equivalent of the previous line
string identifier; // We can do this because C# don't have to infer the type; it is specified explicitly.
var identifier; // We cannot do this because C# don't know what type to use.
I prefer the var
keyword as I find it simplifies the declaration of variables.
It also makes all identifiers horizontally aligned on the screen.
For example:
// C# 1.0
string variable1 = ...;
int variable2 = ...;
List<MyCustomType> variable3 = ...;
// Using the `var` keyword
var variable1 = ...;
var variable2 = ...;
var variable3 = ...;
More info: some languages are weakly-typed. That means you can change the type of data referenced by a variable implicitly, at any time. These types of changes can lead to unintended or unforeseen consequences. I recommend learning a strongly-typed language like C# to understand these concepts first. Afterward, moving to a weakly-typed language is easier. The learning curve of moving from weakly- to strongly-typed language is way more challenging. Furthermore, typing errors are caught at compile-time when using a strongly-typed language instead of at runtime, which should lead to less-buggy products.
Next, let’s explore the identifier part of our initial statement.
The identifier
represents the name of the variable and must be as specific as possible.
A variable’s name must describe what that variable is holding, so anyone reading your code knows without the need to investigate.
An identifier must start with either a letter or an _
.
It can then be composed of different Unicode characters.
The identifier is case sensitive, meaning that thisidentifier
is different from thisIdentifier
(capital i
).
I strongly suggest using only letters and numbers to keep names simple and universal.
Not everyone can easily make unusual characters like è
or ï
using their keyboard’s layout.
I will explain casing styles and code convention in one or more other articles. For now, your goal is to write working code. Not knowing every detail of every subject is ok. If you wait for that, you will never start.
Next, we explore another operator. As a refresher, we learned about the member access operators in the first article of the series.
Now that we named our variable, it is time to assign it a value.
That value also defines the type of the variable when using the var
keyword.
The =
character is the assignment operator.
The assignment operator assigns the value of the right-hand operand to the left side.
For example, to assign the string value "How are you?"
to a variable named howAreYou
we could write the following code:
var howAreYou = "How are you?";
The =
takes the right-hand value ("How are you?"
) and assigns it to the variable named howAreYou
.
We can now use and work with that variable using its identifier (howAreYou
).
We could, for example, write its value to the console.
Next, let’s briefly recap on the initial_value
of the initial statement.
We already talked about this while explaining the other parts of the statement, but I’ll make a quick recap.
The initial value could be pretty much anything: a string, an integer, or an instance of an object.
The initial value is what defines the type of a variable declared using the var
keyword.
Here are a few examples:
var greetings = "Hello, John Doe!"; // type: string
var age = 25; // type: int
var length = 12.87; // type: float
var now = DateTime.Now; // type: DateTime
If you don’t know what string
, int
, float
, and DateTime
means, it’s ok.
They are types available in C# or .NET. As mentioned before, we are going to cover types in subsequent installments of this series.
For now, the important part is to understand the syntax.
By looking at the following image, we can see all the building blocks (below):
var
keyword inferring the variable type from its value (4).identifier
of the variable (its name).var
(1) is used.Side note: Coding is very similar to playing LEGO®. Like LEGO®, we assemble blocks—of text—together to build a computer program.
Enough syntax and theory, let’s try this out next.
Let’s reuse the program we built in the first article of the series, which looked like this:
Console.WriteLine("Hello .NET Ninja!");
Practice: You can also create a new program, which will make you practice the use of the .NET CLI, introduced in the Creating your first .NET/C# program article.
To prepare the program for more advanced use-cases, we want to extract the text Hello .NET Ninja!
in a variable.
To do so, based on what we just explored, we can write the following:
var greetings = "Hello .NET Ninja!";
Console.WriteLine(greetings);
Hint: One crucial detail in the preceding code is that we don’t wrap the
greetings
identifier with"
when passing it as an argument of theWriteLine
method. We write it directly, like this:Console.WriteLine(greetings);
, since we don’t want to pass the string"greetings"
to the method, but the value referenced by thegreetings
variable. In C#,"
is the string delimiter character. We will cover strings in more detail in a subsequent article.
After executing the code above, we can see that we get the same result as before but using a variable instead of inputting text directly into the Console.WriteLine
method.
What happened is the following:
"Hello .NET Ninja!"
to the greetings
variable.
greetings
implicitly became string
.greetings
variable to the console.
greetings
variable, the message written in the console would change.Console.WriteLine("Hello .NET Ninja!");
.
This is the equivalent (a representation) of what is happening in the background at runtime.That may seem useless for now, but keep in mind that a variable’s value can be modified, assigned from user inputs, computed, and more. We are only beginning and will explore and use variables in future articles, most likely in every single one of them. Variables are foundational to programming.
Next, we look at how to make that variable’s value vary.
Now that we covered how to declare a variable and dissected the building blocks, let’s look at how to change the variable’s value.
We will reuse the assignment operator (=
) to assign a new value to the same variable.
In the following micro-program, we assign the value 17
to the variable age
, then we update its value to 18
:
var age = 17;
Console.Write("Age: ");
Console.WriteLine(age);
age = 18;
Console.Write("Congratz, you are now ");
Console.Write(age);
Console.WriteLine("!");
In the age = 18;
statement of the preceding code, we only removed the var
keyword if we compare it with the initial one (var age = 17;
).
That’s because the var
keyword’s objective is only to declare a new variable.
Quick recap.
To update the value of a variable, we must assemble the following building blocks (syntax): identifier = new_value;
.
Note: declaring a variable with the same identifier will cause an error. The name of a variable must be unique. But don’t worry, there are ways to organize our code to limit naming conflicts, but that’s for another day.
Executing the program will write the following in the console:
Age: 17
Congratz, you are now 18!
More info: the
Console.Write
method does the same thing asConsole.WriteLine
, without the “enter” at the end.
That’s it; to change the value of a variable, you only have to assign it a new value using the assignment operator (=
).
However, the type of that new value must be the same.
Next, it is your turn to try it out.
Before moving on, to practice the use of C#, I’d like you to create a program that writes the following output to the console.
------------------
What is your name?
------------------
Use a variable to handle the duplicate text.
var spacer = "------------------";
Once you are done, you can compare with the following solution.
Program.cs
var spacer = "------------------";
Console.WriteLine(spacer);
Console.WriteLine("What is your name?");
Console.WriteLine(spacer);
Don’t worry if your solution is different than mine. As long as you completed it, it means you understood the lesson or at least practiced. Practicing is the key to success!
In this article, we learned how to declare a variable and set its value.
We also dived into more syntax, explored the var
keyword and the assignment operator.
The variable concept is essential. Most importantly, you learned that a variable stores reusable data, accessible through its identifier (name).
This is a lot of theory and small details to take in, but the more you advance and the more you practice, the easier it will become.
It is now time to move to the next article: Introduction to C# constants that’s coming soon. Stay tuned by following me:
Now that you are done with this article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
In this article, we are creating a small console application using the .NET CLI to get started with .NET 6+ and C#.
|
Introduction to C# variables
You are here
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
Expected on Summer 2022
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
Expected on Summer 2022
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
Expected on Summer 2022
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
Expected on Summer 2022
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
Expected on Summer 2022
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
Expected on Summer 2022
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
Expected on Summer 2022
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
Expected on Summer 2022
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
Expected on Summer 2022
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
Expected on Summer 2022
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
More to come Somewhere in 2022 |
The first step of coding is to create a program. The program could be a simple console or a more complex application (web, mobile, game, etc.). To get started, we create a console application, which is the simplest type of program that we can make. The good news is that most of the topics covered in this series are reusable across all types of programs.
Furthermore, .NET and C# allow you to create a wide variety of programs and target most markets, from web to mobile to smart TVs. I believe this is a good choice of technology to start with.
Beforehand, let’s look at the prerequisites.
If you have not already installed the .NET Software Development Kit (SDK), you can download it from https://dotnet.microsoft.com/download. Make sure you install the .NET 6 (or later) SDK.
Hint: Make sure you install the .NET SDK, not the runtime. The runtime is used and optimized to run .NET apps. The SDK contains the runtime and all the necessary tools to develop new programs.
Another good idea would be to install an Integrated Development Environment (IDE) or a code editor. To get started, I suggest a simple, free, yet powerful code editor named Visual Studio Code (VS Code).
Hint: For more complex programs, especially if you are developing on Windows, I suggest Visual Studio (VS). VS is a full-fledged IDE, more complex than VS Code, but extremely powerful to create and maintain large .NET applications. VS also offers a free Community Edition, so you can learn and get started with it.
Next, let’s get started with our console.
With .NET, the easiest way of creating a new cross-platform project is through the .NET Command-line interface (CLI). The CLI is part of the .NET SDK. It is a program that we can use to execute and automate tasks, like creating new projects.
Hint: cross-platform means targeting multiple platforms like Windows, Linux, and Android.
But first things first, we need to create a directory that will hold our program files.
It is important to be organized.
Let’s name the directory IntroToDotNet
.
From a terminal (bash, PowerShell, or cmd), let’s start by typing dotnet new console
, which generates a new console application.
Hint: make sure you are in the right directory. I suggest a structure similar to
[drive]:\Repos\[name of your project]
. For example:D:\Repos\IntroToDotNet
. More info: repos is a shorthand for repositories which is a reference to git. git is something very important to learn in the future, but for now, let’s get back to coding.
The following terminal commands allow you to create a directory and an empty console application inside of it.
If you already created the directory, you can skip that part and only execute the dotnet new console
command.
# Create a directory
mkdir IntroToDotNet
cd IntroToDotNet
# Create the .NET project
dotnet new console
Hint: You can type
ls
in the terminal to list the files contained in the current directory.
The result of the console template is the following two files.
IntroToDotNet.csproj
[name of the directory].csproj
(IntroToDotNet.csproj
) is an XML file defining project properties.
We can use this file to configure more advanced scenarios.
We won’t get into more details but know that you need one csproj
file per project.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
It is important to note that the IntroToDotNet.csproj
file’s content will remain the same for the whole article.
More info: the name of this file does not have to match the name of the directory it is in; that’s just the default behavior of
dotnet new
.
Program.cs
Program.cs
is the entry point of our program.
This is where we write code.
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
We are exploring the meaning of that code later.
As mentioned before, the file that interests us the most is the Program.cs
file.
To keep our focus on the task at hand—learning C#—we will leverage top-level statements to discard that code.
The .NET 6 templates leverage top-level statements, allowing us to write code directly without boilerplate code.
The <ImplicitUsings>enable</ImplicitUsings>
directive of the csproj
file allows us to skip even more boilerplate code.
Let’s replace the content of the Project.cs
file with the following line:
Program.cs
Console.WriteLine("Hello .NET Ninja!");
More info: the top-level statements feature was introduced in C# 9 (.NET 5) and
ImplicitUsings
was introduced with .NET 6.
Now that we wrote some code, it is time to tell your computer to execute it.
To do so, from the directory that contains the IntroToDotNet.csproj
file, type the following:
dotnet run
Afterward, you should see Hello .NET Ninja!
written in the console.
While reading this article, you can dotnet run
the program at anytime to see the output.
Next, let’s explore that code.
There are several building blocks in that one line of code. Don’t worry if you don’t remember or grasp every detail or term just yet. Coding is like playing with LEGO®. You just need to understand how to connect the blocks, and you are good to go. Of course, there is a lot of learning to do, but that’s part of the fun.
The first thing that I’d like to point out is the last character, the ;
.
That character represents the end of a statement (the end of a line of code if you wish).
A statement is an instruction that tells the program to do something, an action, a command.
That’s the type of code that we write the most.
In C#, it is mandatory to add the ;
after a statement, or the code will not compile.
More info: in C#, we write text (code) that gets compiled into an intermediate language (IL). That IL code is then executed by the .NET runtime. The compilation is transforming the text (our code) to that IL language, getting closer to what computers understand. This is a mandatory step, but it will be done almost seamlessly by the SDK as we just explored with
dotnet run
.
Let’s now dissect more of that line of code, starting with the identifiers.
Console
is a static class that exposes a few methods.
A class is a sort of plan defining how to create an object.
An object can be pretty much anything that we need and is a mandatory concept in an object-oriented programming (OOP) language like C#.
We will revisit objects in subsequent articles.
A static class exposes its content globally without creating an instance of it (an object).
A method is a function that we can use to do something (reusable code).
In this case, we used the WriteLine
method, which writes a line into the console.
For example, every time we need to write a line to the console, we can leverage the WriteLine
method.
The line that is written to the console is a string.
A string is a bunch of characters put together to form some text.
In C#, a string is delimited by quotes, like this: "Hello .NET Ninja!"
.
Ok, there are more to strings, but not for now.
The next building blocks are the member access operators.
The first one that we encountered is the .
character.
The dot allows us to access the exposed members of a class (amongst other thing; but that’s fine for today).
In our case, we used the .
operator to access the WriteLine
method of the Console
class, like this: Console.WriteLine
.
The last bit is the parenthesis.
In C#, we use the (
and )
characters to invoke a method.
Invoking a method means executing its code.
Between the parenthesis, we can pass arguments.
An argument is an input value that will most likely change the result of the execution.
In our case, we passed the string "Hello .NET Ninja!"
to the WriteLine
method, writing that text to the console.
If we wanted to write something else, we could have passed that instead, like this:
Console.WriteLine("Something else!");
Most of these subjects deserve to be explored more in-depth but are out of this article’s scope.
That’s it; we wrote our first C#/.NET 6 program. We also wrote some text to the console, and learn how to access static class members.
We explored the “hidden” details behind one line of code and got a glimpse of many new names, like directives and statements. If you are not already familiar with object-oriented programming, don’t worry about all of those names just yet. Classes, namespaces, and methods are tools to organize our code that you will learn along the way. Many of those things (like directives and statements) are used implicitly, and unless you plan on writing about it, you don’t need to remember them just yet. Learning to program can be done in multiple iterative phases where you add little by little over what you know until you can achieve your goal. Then, you start this process over and add more layers of knowledge on top of what you know to reach your subsequent goals.
Please play with the code a little to get familiar with what we just covered. I know it is not much, but one must walk before one can run. Coding is the best way to learn, so get your hands dirty and experiment.
In the next article of the series, we will explore how to create variables.
It is now time to move to the next article: Introduction to C# variables.
Now that you are done with this first article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
You are here
In this article, we are creating a small console application using the .NET CLI to get started with .NET 6+ and C#.
|
Introduction to C# variables
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
Expected on Summer 2022
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
Expected on Summer 2022
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
Expected on Summer 2022
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
Expected on Summer 2022
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
Expected on Summer 2022
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
Expected on Summer 2022
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
Expected on Summer 2022
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
Expected on Summer 2022
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
Expected on Summer 2022
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
Expected on Summer 2022
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
More to come Somewhere in 2022 |
The following topics will be covered:
This article is an excerpt from my book, An Atypical ASP.NET Core 6 Design Patterns Guide ().
Besides being a buzzword, microservices represent an application that is divided into multiple smaller applications. Each application, or microservice, interacts with the others to create a scalable system. Usually, microservices are deployed to the cloud as containerized or serverless applications.
Before getting into too many details, here are a few principles to keep in mind when building microservices:
Furthermore, everything we have studied so far—that is, the other principles of designing software—applies to microservices but on another scale. For example, you don’t want tight coupling between microservices (solved by microservices independence), but the coupling is inevitable (as with any code). There are numerous ways to solve this problem, such as the Publish-Subscribe pattern.
There are no hard rules about how to design microservices, how to divide them, how big they should be, and what to put where. That being said, I’ll lay down a few foundations to help you get started and orient your journey into microservices.
A microservice should have a single business responsibility. Always design the system with the domain in mind, which should help you divide the application into multiple pieces. If you know Domain-Driven Design (DDD), a microservice will most likely represent a Bounded Context, which in turn is what I call a cohesive unit of business. Basically, a cohesive unit of business (or bounded context) is a self-contained part of the domain that has limited interactions with other parts of the domain.
Even if a microservice has micro in its name, it is more important to group logical operations under it than to aim at a micro-size. Don’t get me wrong here; if your unit is tiny, that’s even better. However, suppose you split a unit of business into multiple smaller parts instead of keeping it together (breaking cohesion).
In that case, you are likely to introduce useless chattiness within your system (coupling between microservices). This could lead to performance degradation and to a system that is harder to debug, test, maintain, monitor, and deploy.
Moreover, it is easier to split a big microservice into smaller pieces than assemble multiple microservices back together.
Try to apply the Single Responsibility Principle (SRP) to your microservices: a microservice should have only one reason to change unless you have a good reason to do otherwise.
Each microservice is the source of truth of its cohesive unit of business. A microservice should share its data through an API (a web API/HTTP, for example) or another mechanism (integration events, for example). It should own that data and not share it with other microservices directly at the database level.
For instance, two different microservices should never access the same relational database table. If a second microservice needs some of the same data, it can create its own cache, duplicate the data, or query the owner of that data but not access the database directly; never.
This data-ownership concept is probably the most critical part of the microservices architecture and leads to microservices independence. Failing at this will most likely lead to a tremendous number of problems. For example, if multiple microservices can read or write data in the same database table, each time something changes in that table, all of them must be updated to reflect the changes. If different teams manage the microservices, that means cross-team coordination. If that happens, each microservice is not independent anymore, which opens the floor to our next topic.
At this point, we have microservices that are cohesive units of business and own their data. That defines independence.
This independence offers the systems the ability to scale while having minimal to no impact on the other microservices. Each microservice can also scale independently, without the need for the whole system to be scaled. Additionally, when the business requirements grow, each part of that domain can evolve independently.
Furthermore, you could update one microservice without impacting the others or even have a microservice go offline without the whole system stopping.
Of course, microservices have to interact with one another, but the way they do should define how well your system runs. A little like Vertical Slice architecture, you are not limited to using one set of architectural patterns; you can independently make specific decisions for each microservice. For example, you could choose a different way for how two microservices communicate with each other versus two others. You could even use different programming languages for each microservice.
Now that we’ve defined the basics, let’s jump into the different ways microservices can communicate using event-driven architecture.
Event-driven architecture (EDA) is a paradigm that revolves around consuming streams of events, or data in motion, instead of consuming static states.
What I define by a static state is the data stored in a relational database table or other types of data stores, like a NoSQL documents store. That data is dormant in a central location and waiting for actors to consume and mutate it. It is stale between every mutation and the data (a record, for example) represents a finite state.
On the other hand, data in motion is the opposite: you consume the ordered events and determine the change in state that each event brings.
What is an event? People often interchange the words event, message, and command. Let’s try to clarify this:
A message usually has a payload (or body), headers (metadata), and a way to identify it (this can be through the body or headers).
We can use events to divide a complex system into smaller pieces or have multiple systems talk to each other without creating tight couplings. Those systems could be subsystems or external applications, such as microservices.
Like Data Transfer Objects (DTO) of web APIs, events become the data contracts that tie the multiple systems together (coupling). It is essential to think about that carefully when designing events. Of course, we cannot foresee the future, so we can only do so much to get it perfect the first time. There are ways to version events, but this is out of the scope of this article.
EDA is a fantastic way of breaking tight coupling between microservices but requires rewiring your brain to learn this newer paradigm. Tooling is less mature, and expertise is scarcer than more linear ways of thinking (like using point-to-point communication and relational databases), but this is slowly changing and well worth learning (in my opinion).
We can categorize events into the following overlapping buckets:
As we’ll explore next, all types of events play a similar role with different intents and scopes.
A domain event is a term based on DDD representing an event in the domain. This event could then trigger other pieces of logic to be executed subsequently. It allows a complex process to be divided into multiple smaller processes. Domain events work well with domain-centric designs, like Clean Architecture, as we can use them to split complex domain objects into multiple smaller pieces. Domain events are usually application events. We can use MediatR to publish domain events inside an application.
To summarize, domain events integrate pieces of domain logic together while keeping the domain logic segregated, leading to loosely coupled components that hold one domain responsibility each (single responsibility principle).
Integration events are like domain events but are used to propagate messages to external systems, to integrate multiple systems together while keeping them independent. For example, a microservice could send the new user registered
event message that other microservices react to, like saving the user id
to enable additional capabilities or sending a greeting email to that new user.
We use a message broker or message queue to publish such events. We’ll cover those next, after covering application and enterprise events.
To summarize, integration events integrate multiple systems together while keeping them independent.
An application event is an event that is internal to an application; it is just a matter of scope. If the event is internal to a single process, that event is also a domain event (most likely). If the event crosses microservices boundaries that your team owns (the same application), it is also an integration event. The event itself won’t be different; it is the reason why it exists and its scope that describes it as an application event or not.
To summarize, application events are internal to an application.
An enterprise event describes an event that crosses internal enterprise boundaries. These are tightly coupled with your organizational structure. For example, a microservice sends an event that other teams, part of other divisions or departments, consume.
The governance model around those events should be different from application events that only your team consumes. Someone must think about who can consume that data, under what circumstances, the impact of changing the event schema (data contract), schema ownership, naming conventions, data-structure conventions, and more, or risk building an unstable data highway.
To summarize, enterprise events are integration events that cross organizational boundaries.
We defined events, messages, and commands in this quick overview of event-driven architecture. An event is a snapshot of the past, a message is data, and a command is an event that suggests other systems to take action. Since all messages are from the past, calling them events is accurate. We then organized events into a few overlapping buckets to help identify the intents. We can send events for different objectives, but whether it is about designing independent components or reaching out to different parts of the business, an event remains a payload that respects a certain format (schema). That schema is the data contract (coupling) between the consumers of those events. That data contract is probably the most important piece of it all; break the contract, break the system.
Now, let’s see how event-driven architecture can help us follow the SOLID principles at cloud-scale:
EDA does not only come with advantages; it also has a few drawbacks:
You will explore any intricacies of microservices in more detail in An Atypical ASP.NET Core 6 Design Patterns Guide (), along with analysis of each of the following patterns:
The microservices architecture is different to building monoliths. Instead of one big application, we split it into multiple smaller ones that we call microservices. Microservices must be independent of one another; otherwise, we will face the same problems associated with tightly coupled classes, but at the cloud scale.
Microservices are great when you need scaling, want to go serverless, or split responsibilities between multiple teams, but keep the operational costs in mind. Starting with a monolith and migrating it to microservices when scaling is another solution. You can also plan your future migration toward microservices, which leads to the best of both worlds while keeping operational complexity low.
I don’t want you to discard the microservices architecture, but I just want to make sure that you weigh up the pros and cons of such a system before blindly jumping in. Your team’s skill level and ability to learn new technologies may also impact the cost of jumping into the microservices boat.
]]>We also cover automated testing and use tests as consumers of our code in multiple code samples. Automated testing is key to modern development approaches like continuous integration and DevOps. The strong focus on dependency injection is also still there, making sure readers learn techniques that will help them build ASP.NET Core 6+ applications.
Last but not least, the book still covers numerous design patterns, from multiple of the famous Gang of Four (GoF) patterns to application-level patterns like layering, microservices, and vertical slice architecture.
You can find the content of the first edition in the Book: An Atypical ASP.NET Core 5 Design Patterns Guide: What’s inside? article.
In the second edition, based on readers’ feedback, I addressed the pain points that readers had with the first edition. I also made many small changes to create a more polished product. Of course, I added many new C# 10 and .NET 6 features and revamped many code samples. Next is a list containing some of those changes.
I updated the code samples to align with the direction .NET is taking, making it easier for you to understand the minimal hosting model. The code style updates are a significant investment I made in the second edition; I hope you like it!
Now, most code samples use top-level statements and the minimal hosting model. They also respect the nullable reference types features introduced in C# 8.0 and enabled by default in .NET 6 project templates, allowing you to learn them at the same time.
The list of changes we just covered represents the major highlights of this second edition. I made so many little improvements that it is impossible to list them all here.
If you did not read the first edition, I’m sure you’ll love the second one. If you read the first edition, I’m sure you’ll get something out of the second one too.
Nevertheless, please share any feedback you may have with me so I can continuously improve your reading experience.
You can find the content of the first edition in the Book: An Atypical ASP.NET Core 5 Design Patterns Guide: What’s inside? article.
]]>This article is part of a learn programming series where you need no prior knowledge of programming. If you want to learn how to program and want to learn it using .NET/C#, this is the right place. I suggest reading the whole series in order, starting with Creating your first .NET/C# program, but that’s not mandatory.
This article is part of a sub-series, starting with Introduction to Boolean algebra and logical operators. It is not mandatory to read all articles in order, but I strongly recommend it, especially if you are a beginner. If you are already reading the whole series in order, please discard this word of advice.
Let’s explore Boolean algebra laws in alphabetical order. Some are very simple, while some may seem more complex, but all are very useful tools.
In the examples, the variables (A
, B
, C
) are all booleans, like this:
bool A, B, C;
The absorption law goes as follow:
A && (A || B)
always equals A
A || A && B
always equals A
The annulment law goes as follow:
A && false
is always false
A || true
is always true
The associative law says that no matter the order or priority of the OR comparisons, the result will always be the same. The following conditions yield the same result:
A || (B || C)
(A || B) || C
(A || C) || B
A || B || C
The complement law goes as follow:
A && !A
is always false
A || !A
is always true
The commutative law goes as follow:
A && B
is the same as B && A
A || B
is the same as B || A
The consensus law goes as follow:
(A || B) && (!A || C) && (B || C)
is equivalent to (A || B) && (!A || C)
A && B || A && C || B && C
is equivalent to A && B || A && C
These two might be harder to grasp, so here’s my take at a quick explanation:
(A || B) && (!A || C) && (B || C)
, A
and !A
are almost cancelling themselves, leaving the outcome of the comparison to B
and C
. Based on that fact, no matter the values, the last comparison && (B || C)
becomes useless (already evaluated).A && B || A && C || B && C
, we test B
and C
, making the last comparison void || B && C
.Word of advice: it is not mandatory to remember the consensus law, so feel free to skip this one if you think it’s too complicated for today. You can always come back to it later.
This one is very interesting and can be handy from time to time. In plain English, De Morgan’s laws are:
negation
of a disjunction
is the conjunction
of the negations
.negation
of a conjunction
is the disjunction
of the negations
.Logical
conjunction
means AND (&&
), and the logicaldisjunction
means OR (||
). Unless you are into mathematics, you don’t have to remember that.
Now to the part that interests us, in C#, De Morgan’s laws are:
!(A || B)
is equivalent to !A && !B
!(A && B)
is equivalent to !A || !B
The distributive law goes as follow:
A && B || A && C
is equivalent to A && (B || C)
(A || B) && (A || C)
is equivalent to A || B && C
The double negation law says that two negations negate themselves. In C# this looks like:
!!A
is equivalent to A
The identity law goes as follow:
A && true
always equals A
A || false
always equals A
The idempotent law goes as follow:
A && A
always equals A
A || A
always equals A
The redundancy law goes as follow:
(A || B) && (A || !B)
always equals A
A && B || A && !B
always equals A
(A || !B) && B
is equivalent to A && B
A && !B || B
is equivalent to A || B
In this article, we explored a bunch of Boolean algebra laws from a C# programmer perspective. You can find information on them, including mathematic proofs, online or in books if you are into maths. Personally, I prefer the plain C# version, so that’s why I translated them to this here. Even if some of those laws might seem a bit too complicated to remember, don’t be discouraged; even the simplest ones are helpful: start there.
Programming is like playing LEGO® blocks: we can combine all of those laws, which are logic patterns.
For example, !(!A || !B)
looks complicated, but after applying De Morgan’s law, it becomes equivalent to !(!(A && B))
.
By removing the useless parenthesis we end up having !!(A && B)
, which exposes a double negation.
Applying the double negation law leads to A && B
.
That simplified version of the original condition looks way simpler, doesn’t it?
Learning the basics is helpful in the long run. If you have a good memory, feel free to memorize all of this as a starting point. If you don’t, don’t worry, learn them one by one. The idea is to simplify your code, so it reaches a more maintainable state. With a bit of time, you will most likely know and apply many (if not all) of them without thinking about it. Just start with the simplest ones, bookmark the article, and learn the others later. Understanding rules, laws, and concepts should get you further than just remembering them; but that takes more time.
Fun fact: I’ve run many interviews in 2021, and one of my favorite technical questions is based on complicated conditions that can be simplified using some of those laws. And no, I’m not looking for candidates that know the name of the laws, just if they can resolve a complex if-statement and how.
Please leave your questions or comments below or drop me a Tweet.
It is now time to move to the next article: This is the end of this series that’s coming soon. Stay tuned by following me:
Now that you are done with this article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
In this article, we are creating a small console application using the .NET CLI to get started with .NET 5+ and C#.
|
Introduction to C# variables
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
You are here
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
This is the end of this series
This article series was migrated to a newest version of .NET. Have a look at the .NET 6 series for more!
|
switch
statement.
The switch
keyword is very standard in programming languages.
We use it to compare a variable with many values.
Please note that we are not covering switch expressions in this article.
This article is part of a learn programming series where you need no prior knowledge of programming. If you want to learn how to program and want to learn it using .NET/C#, this is the right place. I suggest reading the whole series in order, starting with Creating your first .NET/C# program, but that’s not mandatory.
This article is part of a sub-series, starting with Introduction to Boolean algebra and logical operators. It is not mandatory to read all articles in order, but I strongly recommend it, especially if you are a beginner. If you are already reading the whole series in order, please discard this word of advice.
In the previous article, Using if-else selection statements to write conditional code blocks, we covered the basics behind contextual code.
This article explores the switch
statement by converting a complex if
block to a switch
.
We go through the syntax afterward.
Initial code (if):
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "hello" || input == "world" || input == "hello world")
{
Console.WriteLine("Hello World!");
}
else if (input == "goodbye")
{
Console.WriteLine("Au revoir!"); // Goodbye in French
}
else if (input == "name?")
{
Console.WriteLine("What is your name?");
var name = Console.ReadLine();
Console.WriteLine($"Your name is {name}");
}
else
{
Console.WriteLine("Invalid input");
}
Console.WriteLine("End of the program.");
Converted code (switch):
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
switch (input)
{
case "hello":
case "world":
case "hello world":
Console.WriteLine("Hello World!");
break;
case "goodbye":
Console.WriteLine("Au revoir!"); // Goodbye in French
break;
case "name?":
Console.WriteLine("What is your name?");
var name = Console.ReadLine();
Console.WriteLine($"Your name is {name}");
break;
default:
Console.WriteLine("Invalid input");
break;
}
Console.WriteLine("End of the program.");
Let’s now analyze the previous code, starting with the new keywords:
switch
case
break
default
The switch
keyword starts a code block that must be followed by a variable in parenthesis, like this:
switch (variable)
{
// Code block
}
The case
keyword, followed by a value and :
, is an equality comparison against the original variable
passed to the switch
.
You can see case "goodbye":
as the equivalent of if (variable == "goodbye")
.
A case
block can be empty or end with a break
or return
.
We will not cover the return
keyword in this article because it is related to other concepts that we have not explored yet.
When the case
is empty, it continues to the next case
.
For example, case "hello":
falls back to case "world":
that falls back to case "hello world":
that gets executed.
Interesting fact: C# does not support falling from one non-empty
case
to another as some other languages do.
The break
keyword is a jump statement that allows controlling the flow of the program by exiting the current block, a.k.a. jumping out of the switch
block.
Finally, in a switch
block, the default
keyword is the equivalent of the else
; it is hit when no other case
was hit.
Before the exercise, let’s peek at the program flow created by a switch statement.
In a nutshell, a switch
statement allows comparing if a variable is equal to a value from a list of cases.
Here is a visual representation of the program flow created by a switch
block:
Next, it’s your turn to try it out.
Convert the following code to use a switch
statement.
var input = Console.ReadLine();
if(input == "A")
{
Console.WriteLine("1");
}
else if (input == "B")
{
Console.WriteLine("2");
}
else if (input == "C")
{
Console.WriteLine("3");
}
else if (input == "D")
{
Console.WriteLine("4");
}
else if (input == "E")
{
Console.WriteLine("5");
}
else
{
Console.WriteLine("Invalid input");
}
Once you are done, you can compare with My Solution below.
Program.cs
var input = Console.ReadLine();
switch (input)
{
case "A":
Console.WriteLine("1");
break;
case "B":
Console.WriteLine("2");
break;
case "C":
Console.WriteLine("3");
break;
case "D":
Console.WriteLine("4");
break;
case "E":
Console.WriteLine("5");
break;
default:
Console.WriteLine("Invalid input");
break;
}
Good job! You completed another small chapter of your programming journey.
This article explored the switch
statement, which allows comparing if a variable is equal to a value from a list of cases.
The switch
statement is another way to create conditional code and control our programs’ flow.
The switch
statement is handy for variables with a finite number of values like enum
s.
More recent versions of C# also introduce switch expressions and pattern matching that open many other possibilities. However, many of those possibilities require knowledge of object-oriented programming that we are not exploring in this article series.
Please leave your questions or comments below or drop me a Tweet.
It is now time to move to the next article: Boolean algebra laws.
Now that you are done with this article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
In this article, we are creating a small console application using the .NET CLI to get started with .NET 5+ and C#.
|
Introduction to C# variables
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
You are here
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
This is the end of this series
This article series was migrated to a newest version of .NET. Have a look at the .NET 6 series for more!
|
if
statement? These are the subject that we cover here.
As part of the beginner journey, we focus on the if-else selection statements LEGO® block, laying down the foundation for more advanced use-cases.
In this article, we are exploring conditional execution flows. What is a code path? How will we do that? These are the subject that we cover here. As part of the beginner journey, we focus on the if-else selection statements LEGO® block, laying down the foundation for more advanced use-cases.
This article is part of a learn programming series where you need no prior knowledge of programming. If you want to learn how to program and want to learn it using .NET/C#, this is the right place. I suggest reading the whole series in order, starting with Creating your first .NET/C# program, but that’s not mandatory.
This article is part of a sub-series, starting with Introduction to Boolean algebra and logical operators. It is not mandatory to read all articles in order, but I strongly recommend it, especially if you are a beginner. If you are already reading the whole series in order, please discard this word of advice.
In the article How to read user inputs from a console, we talked about the flow of a program, moving from the first instruction to the next. Let’s call that linear execution flow. Here, you will learn how to run only part of the code based on different values, leading to a more complex execution flow model.
By writing conditional code blocks, we can create a program that actually has more complex logic than what we programmed so far. Our program will be able to do different things based on different runtime values, like user inputs.
Let’s now explore how to program that using C#.
Before we dig into the conditional blocks, we will visit two new operators to help us compare if two values are equal or not.
Those works with all primitive types like string
and int
, for example.
The equality operator ==
allows comparing if two values are equal.
The syntax is left_operands == right_operand
and returns a Boolean value.
Tip: you can read this as left_operand is equal to right_operand.
Here is an example:
var left = "A";
var right = "B";
var result = left == right; // false
// ...
In the preceding code, since A is not equal to B, the result of the left == right
comparison is false
.
In the case of "A" == "A"
, the result would have been true
.
We have one last operator to look into before exploring the if-else selection statements, the inequality operator.
The inequality operator !=
is the opposite of the equality operator and allows comparing if two values are different (not equal).
The syntax is left_operands != right_operand
and returns a Boolean value.
Tip: you can read this as left_operand is not equal to right_operand or left_operand is different than right_operand.
Here is an example:
var left = "A";
var right = "B";
var result = left != right; // true
// ...
In the preceding code, since A is not equal to B, the result of the left != right
comparison is true
.
More info: the inequality operator is syntactic sugar, equivalent to
!(left == right)
. This simplifies writing C# code a lot.
Other comparison operators exist, but let’s keep our scope narrow here and jump into the main subject instead.
The if-else selection statements are blocks of C# code that are executed conditionally based on a Boolean expression—a condition that evaluates to true
or false
.
In this section, we are exploring the following concepts:
if
statementelse
statementelse if
statementIn C#, a statements block is delimited by {
and }
, like this:
// Code before the statement block
{ // Statement block start delimiter
//
// Code inside the block
//
} // Statement block end delimiter
// Code after the statement block
A block creates a sub-context that can access its parent context. However, the parent context can’t access that sub-context. Here is an example that illustrates this concept:
using System;
var a = 1;
{
var b = 2;
Console.WriteLine($"Inside block; a = {a}");
Console.WriteLine($"Inside block; b = {b}");
}
Console.WriteLine($"Outside block; a = {a}");
Console.WriteLine($"Outside block; b = {b}"); // Error: The name 'b' does not exist in the current context
In the preceding code, the program can access the a
variable from inside the statements block but cannot access the b
variable from outside of it.
Because of that, if we execute the code, .NET will report an error telling us The name 'b' does not exist in the current context
.
We use statements blocks extensively throughout the article, so don’t worry about it if you are unsure why you would write one.
Let’s explore why I indented the code inside the block before writing our first conditional code block using the if
statement.
Have you noticed the indentation added inside the statements block? By convention, we add that indentation for readability. That makes the different contexts (or nested-levels) line up vertically, like this:
In C#, we usually use 4 spaces to indent the code. People may also use 2 spaces (not frequent in the .NET/C# world). Some people also prefer to use tabs instead of spaces. By default, Visual Studio and Visual Studio Code will translate a tab to N spaces automatically (default: 4), so you don’t have to type 4 spaces every time. One tab will do.
“Fun” fact: A tabs versus spaces war also exists where people prefer tabs over spaces or vice versa and argue that their way is the best over the other. I personally use spaces, tried tabs, tried many techniques during the years, and realized that it does not matter much in the end. If you are working in a team or an enterprise, there may well be existing guidelines around this.
Next, we explore the if
statement.
if
statementThe if
statement does literally what its English definition is: if the condition is true, then enter the statements block; otherwise, don’t.
This is where we begin to put that Boolean algebra to good use.
The syntax goes like this:
if (condition)
{
// Do something when condition is true
}
Here is an example:
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "GO")
{
Console.WriteLine("The user entered GO!");
}
Console.WriteLine("End of the program.");
In the preceding code, input == "GO"
represents the condition that evaluates to a boolean result.
When running the program, if the user enters GO
(uppercase), the program will print The user entered GO!
. Otherwise, it will skip that code block.
Here is a running example:
As we can see from that recording, if we enter something other than GO
, the program skips the statements block.
Here is the visual representation of this program flow when the user enters GO
:
Here is the visual representation of this program flow when the user enters NOT GO
:
Note: in the preceding diagrams, the parts that are not executed are grayed out.
But what happens if we want something different to happen if the input is not GO
while keeping this logic?
else
statementThe else
statement must follow an if
statement block (or an else if
block; see below).
We can’t write an else
block alone.
The else
statements block is a fallback statements block that is executed when the if
condition is evaluated to false
.
The syntax goes like this:
if (condition)
{
// Do something when condition is true
}
else
{
// Do something when condition is false
}
In the following example, we put that to good use and display Console.WriteLine("The user did not enter GO!");
when the input is different than "GO"
.
We could write this with two if
statements or an if
followed by an else
statement.
Let’s start by the first option:
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "GO")
{
Console.WriteLine("The user entered GO!");
}
if (input != "GO")
{
Console.WriteLine("The user did not enter GO!");
}
Console.WriteLine("End of the program.");
In this case, the preceding code would do the trick.
However, we can remove that second comparison input != "GO"
by leveraging the else
statement instead.
This will a) remove that comparison (slightly improve performance) and b) make our program more maintainable by removing the duplicated logic.
The alternative looks like the following:
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "GO")
{
Console.WriteLine("The user entered GO!");
}
else // Only this line changed
{
Console.WriteLine("The user did not enter GO!");
}
Console.WriteLine("End of the program.");
Running any of those two programs results in the following execution flow:
And there we go; we can now use the if
and the if-else
statements to control the program’s execution flow.
With them, we can execute only certain statement blocks based on runtime values, like values entered by the user.
Here is the visual representation of this program flow when the user enters GO
:
Here is the visual representation of this program flow when the user enters NOT GO
:
Note: in the preceding diagrams, the parts that are not executed are grayed out.
Ok, but what happens when we want to write a different message if the user enters SHOW ME
?
else if
statementThe else if
statement is a follow-up if
statement if you wish.
As we saw in the preceding section, we can have two if
statements back to back, but they are independent of each other.
On the other hand, the else if
statement allows to add another conditional block after the if
, but the condition is only be evaluated when the previous condition was evaluated to false
.
We can chain as many else if
statements as we need.
An else
statement can optionally go last; after all else if
blocks.
Here are a few examples of the syntax:
// if + else if
if (condition1)
{
// ...
}
else if (condition2)
{
// ...
}
// if + else if + else if
if (condition1)
{
// ...
}
else if (condition2)
{
// ...
}
else if (condition3)
{
// ...
}
// if + else if + else
if (condition1)
{
// ...
}
else if (condition2)
{
// ...
}
else
{
// ...
}
// if + else if + else if + else
if (condition1)
{
// ...
}
else if (condition2)
{
// ...
}
else if (condition3)
{
// ...
}
else
{
// ...
}
As mentioned, we can add as many else if
blocks as needed.
Let’s now apply that to our problem: we want to write a different message if the user enters SHOW ME
.
We could do this only with if
statements, but as you can see below, it can get complicated:
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "GO")
{
Console.WriteLine("The user entered GO!");
}
if (input == "SHOW ME")
{
Console.WriteLine("The user entered SHOW ME!");
}
if (input != "GO" && input != "SHOW ME") // <--
{
Console.WriteLine("The user did not enter GO nor SHOW ME!");
}
Console.WriteLine("End of the program.");
By looking at the preceding code, we can see that the more logic we add, the more complex the else
-like block becomes (the if
line marked by a <--
comment).
Here we have two conditions (input == "GO"
and input == "SHOW ME"
) so we must make sure that both are false
before executing the default block (input != "GO" && input != "SHOW ME"
).
Tips: I strongly advise against writing that type of code as it can get out of hand very quickly. In this case, seeing it firsthand will allow you to identify such code. Always aim at simplicity and readability.
Let’s simplify that code using if—else if—else
blocks instead:
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "GO")
{
Console.WriteLine("The user entered GO!");
}
else if (input == "SHOW ME")
{
Console.WriteLine("The user entered SHOW ME!");
}
else
{
Console.WriteLine("The user did not enter GO nor SHOW ME!");
}
Console.WriteLine("End of the program.");
We can notice two sets of changes:
if
by an else if
.if
by an else
.Here is a diff of those two listings, where the red lines are replaced by the green lines:
using System;
Console.WriteLine("Enter something: ");
var input = Console.ReadLine();
if (input == "GO")
{
Console.WriteLine("The user entered GO!");
}
- if (input == "SHOW ME")
+ else if (input == "SHOW ME")
{
Console.WriteLine("The user entered SHOW ME!");
}
- if (input != "GO" && input != "SHOW ME")
+ else
{
Console.WriteLine("The user did not enter GO nor SHOW ME!");
}
Console.WriteLine("End of the program.");
As the preceding code block highlights, using if
, else if
, then else
allowed us to get rid of the complex condition that negates the other two conditions.
This code is still simple, but if you think about adding more and more conditions, the last if
would become very hard to maintain, error-prone, and hard to read.
Moreover, all conditions would be duplicated. Once for its own if
block and negated for that last if
.
Using an else
statement just makes our life easier, so why not, right?
Anyway, running any of those two programs results in the following execution flow:
Now, let’s explore how the code is evaluated. Those two small sets of differences change many things in the execution flow, as demonstrated in the following image:
In the preceding image, we can notice that each condition is evaluated (on the left). In contrast, the conditions are evaluated only when the previous one was false (on the right).
This is the big difference between using if—else if—else
or not.
To make it easier to understand, let’s compare the steps depicting the scenario of a user typing GO
(input == "GO"
):
The steps of the left listing goes like this:
if
is evaluated to true
.if
is evaluated as false
.if
is evaluated as false
.The steps of the right listing goes like this:
if
is evaluated to true
.In the right execution flow, only the first if
is evaluated.
The program skips the evaluation of the else if
statement and jumps over both the else if
and else
blocks.
Here is the visual representation of this program flow when the user enters GO
:
Here is the visual representation of this program flow when the user enters SHOW ME
:
Here is the visual representation of this program flow when the user enters NOT GO
:
Note: in the preceding diagrams, the parts that are not executed are grayed out.
As you may begin to realise, if
, else if
, and else
blocks are different ways to control the flow of execution of your programs.
Now that we explored that, it is time for you to practice.
You must write a program that:
Hey! that's me!
to the console.Hello, FIRST_NAME, LAST_NAME
where FIRST_NAME
and LAST_NAME
are the names the user entered.Here are a few optional hints in case you feel stuck:
If you don’t remember how to ask a user for his input, have a look at How to read user inputs from a console.
There are multiple ways of implementing this solution.
However, an if
followed by an else
block should be a good start.
The AND operator (&&
) and the equality operator (==
) will allow you to combine both conditions (first name and last name) into one.
Feel free to have a look at Introduction to Boolean algebra and logical operators if you need a reminder on logical operators.
Once you are done, you can compare with My Solution below.
Program.cs
const string MyFirstName = "Carl-Hugo";
const string MyLastName = "Marcotte";
Console.WriteLine("What is your first name? ");
var firstName = Console.ReadLine();
Console.Clear();
Console.WriteLine("What is your last name? ");
var lastName = Console.ReadLine();
Console.Clear();
if (firstName == MyFirstName && lastName == MyLastName)
{
Console.WriteLine("Hey! that's me!");
}
else
{
Console.WriteLine($"Hello, {firstName}, {lastName}");
}
In the preceding code, I:
if—else
blocks, introduced in this article.==
) and the AND logical operator (&&
), translating the English requirements to code: « If the user enters your first name and last name […] ».else
block. We explored string interpolation in Introduction to string interpolation.As you may start to notice, the more we move forward, the more LEGO® blocks we can piece together, and the more complex the application we can build. All the pieces are simple. The craft is about assembling them correctly. The hardest part of programming is probably to teach our brain to think computer, allowing it to translate human-described requirements to code.
Tip: don’t think about the code itself; understand the human version of the problem instead, then try to fix it. This should help you.
Here is an alternative way you could have implemented the condition:
// ...
if (firstName != MyFirstName || lastName != MyLastName)
{
Console.WriteLine($"Hello, {firstName}, {lastName}");
}
else
{
Console.WriteLine("Hey! that's me!");
}
// ...
In the preceding code, the logic is inverted. That condition could also have been simplified to:
// ...
if (!(firstName == MyFirstName && lastName == MyLastName))
{
Console.WriteLine($"Hello, {firstName}, {lastName}");
}
// ...
If you are not sure how I was able to play with those conditions, we will explore that in a future article about common Boolean algebra laws.
Good job! You completed another small chapter of your programming journey.
In this article, we learned how to write code that gets executed only when certain conditions are met.
We learnt about the equality (==
) and inequality (!=
) operators.
Then we explored how to write if
, else if
, and else
statements blocks to alter the linear flow of a program.
We also briefly covered code indentation as a standard way to improve the readability of your code.
Please leave your questions or comments below or drop me a Tweet.
It is now time to move to the next article: Using the switch selection statement to simplify conditional statements blocks.
Now that you are done with this article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
In this article, we are creating a small console application using the .NET CLI to get started with .NET 5+ and C#.
|
Introduction to C# variables
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
You are here
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
This is the end of this series
This article series was migrated to a newest version of .NET. Have a look at the .NET 6 series for more!
|
true
or false
.
This is a fundamental part of programming that you can’t escape, and you will use this until the end of your programmer career and maybe even beyond that point.
The article is not focusing on mathematical applications and representations but on programming. The objective is to give you the knowledge you need for the next article of the series.
This article is part of a learn programming series where you need no prior knowledge of programming. If you want to learn how to program and want to learn it using .NET/C#, this is the right place. I suggest reading the whole series in order, starting with Creating your first .NET/C# program, but that’s not mandatory.
This article is the first part of a sub-series showcasing the following articles:
In C#, bool
is the type that represents a boolean value.
A bool
can have a value of true
or false
.
A bool
is the memory representation of a bit.
A bit is a base 2 digit that is either 0
or 1
.
The value 0
means false
, and the value 1
means true
.
Want to know more? The mathematic that we learn at school is base-10; a.k.a., there are 10 numbers: 0 to 9. On the other hand, computers use base-2, or a binary numeral system, that includes only two numbers: 0 and 1. Chances are, this is not something that you need to know right away, but I recommend learning this concept one day. Knowing base-2 (binary), base-8 (octal), and base-16 (hexadecimal) can only help you (yes, there are more than just base-2 and base-10).
The following code shows the two possibilities, written in C#:
// Using var
var thisIsTrue = true;
var thisIsFalse = false;
// Using the type name
bool thisIsAlsoTrue = true;
bool thisIsAlsoFalse = false;
Now that we covered how to declare a variable of type bool
, let’s look at the basic operations of Boolean algebra.
There are three basic operations in boolean algebra:
Name | Known-as | C# |
---|---|---|
Conjunction | AND | && |
Disjunction | OR | || |
Negation | NOT | ! |
With AND
, OR
, and NOT
, we can create most logical conditions that a program requires to run.
Let’s start by exploring the NOT
logical operator.
The NOT
operator is a unary prefix operator and is different from AND
and OR
, which are binary operators.
It prefixes a boolean value and inverts it.
In C# (and many other languages), the NOT
symbol is !
.
More info: C# 8.0 introduced the
!
as a suffix operator, a.k.a. the null-forgiving operator, which is a totally different thing.
The following table lists the two possible use of the negation operator and their outcome:
Expression (C#) | English | Result |
---|---|---|
!true |
NOT true |
false |
!false |
NOT false |
true |
The following code uses the preceding grid to explore the possibilities using C#, outputting the values in the console:
var value1 = true;
var value2 = false;
var value3 = !value1; // false
var value4 = !value2; // true
Console.WriteLine($"value1: {value1}");
Console.WriteLine($"value2: {value2}");
Console.WriteLine($"value3: {value3}");
Console.WriteLine($"value4: {value4}");
When running the program, we obtain the following output:
value1: True
value2: False
value3: False
value4: True
As we can observe here, the value of the value3
and value4
variables are the opposite of their negated source.
This is the main takeaway here: the NOT operator, in !variable
, flips the original value of variable
.
One last bit: as an analogy, you could see a boolean as a light switch and the negation as the action of flipping the switch on/off. For example, when you flip the light-switch from off (
false
) to on (true
); on is the equivalent of not off (!false
) while off is the equivalent of not on (!true
).
Next, let’s jump into the AND logical operator.
In C#, the conditional logical AND
operator is represented by &&
.
Important: It is essential to double the symbol, otherwise
&
(single) is a binary operator (acting on bits), and it is different.
The &&
operator is a binary operator that acts on two operands, like result = operand1 && operand2
.
Here is an example of using the &&
operator:
var leftOperand = true;
var rightOperand = true;
var result = leftOperand && rightOperand; // true
Console.WriteLine($"Result: {result}");
The preceding code outputs Result: True
to the console.
Now that you may be wondering why true && true
returns true
, let’s have a look at the logical table of the AND
operator:
Left | Right | C# | English | Result |
---|---|---|---|---|
true |
true |
true && true |
true AND true |
true |
true |
false |
true && false |
true AND false |
false |
false |
true |
false && true |
false AND true |
false |
false |
false |
false && false |
false AND false |
false |
As you may have noticed from the preceding table, all combinations result in false
except when both operands are true
; that’s how the AND operator works.
Let’s update the preceding code to cover the true && false
scenario:
var leftOperand = true;
var rightOperand = false;
var result = leftOperand && rightOperand; // false
Console.WriteLine($"Result: {result}");
This updated code outputs Result: False
to the console, precisely like the table predicted.
Ok, we are not done yet.
Next, we look at the OR
operator, which has a similar syntax but a different logical outcome.
In C#, the conditional logical OR
operator is represented by ||
.
Important: It is essential to double the symbol, otherwise
|
(single) is a binary operator (acting on bits), and it is different.
The ||
operator, same as the &&
operator, is a binary operator that acts on two operands, like result = operand1 || operand2
.
Here is a C# example of using the ||
operator:
var leftOperand = true;
var rightOperand = true;
var result = leftOperand || rightOperand; // true
Console.WriteLine($"Result: {result}");
The preceding code outputs Result: True
to the console.
Like the AND operator, the OR operator also has a logical table that comes with it.
Let’s have a look:
Left | Right | C# | English | Result |
---|---|---|---|---|
true |
true |
true || true |
true OR true |
true |
true |
false |
true || false |
true OR false |
true |
false |
true |
false || true |
false OR true |
true |
false |
false |
false || false |
false OR false |
false |
An interesting observation is how the ||
operator returns true
whenever there is at least one operand that is equal to true
.
In other words, the ||
operator returns false
only when there is no true
(when both operands are false
).
In code, the only way to have a result of false
would be the following code:
var result = false || false; // false
Console.WriteLine($"Result: {result}");
The preceding code outputs Result: False
to the console.
Now that we covered the basic operators, it is time to look at the logical exclusive OR
operator, which is closer to the spoken OR
than the logical OR
that we just learned about.
In spoken languages, we usually use OR
as an exclusive OR.
For example, when we say, « do you prefer blue or green? » we expect a response about one of the two but not both.
That type of OR is called the exclusive OR
, also known as XOR
.
In C#, the XOR operator is ^
.
Advanced information: the
XOR
operator is a compound operator, or shortcut if you which. We can compose the equivalent of theXOR
operator using basic operators likeNOT
,AND
, andOR
. In C#, theXOR
operator can be expressed as one of the following expressions:(left || right) && (!left && !right)
or(left && !right) || (!left && right)
. In the preceding two code snippets, the parenthesis change the priority of the operations. The parenthesis play the same concept than in the following elementary mathematic equations(1 + 2) * 3 = 3 * 3 = 9
but1 + 2 * 3 = 1 + 6 = 7
.
Let’s start by exploring the XOR
logic table:
Left | Right | C# | English | Result |
---|---|---|---|---|
true |
true |
true ^ true |
true OR true |
false |
true |
false |
true ^ false |
true OR false |
true |
false |
true |
false ^ true |
false OR true |
true |
false |
false |
false ^ false |
false OR false |
false |
As you may have noticed, the XOR
logic table is the same as the OR
table, but the result is false
when both operands are true
(first row).
This is what differentiates OR and XOR: the result is true
if one operand is true
but not both.
In code, XOR looks like this:
var leftOperand = true;
var rightOperand = false;
var result = leftOperand ^ rightOperand; // true
Console.WriteLine($"Result: {result}");
When executing the preceding code, we get Result: True
as the console output because one of the operands is true but not both.
Note: based on my personal experiences, this operator is not used very often. Nevertheless, I think it is worth knowing of its existence, for those few times.
Next, that’s your turn to practice what we just covered.
The exercise will focus on the logic part and not on the code part. We will use this knowledge in the next installment, where we will learn to write conditional code based on boolean logic. For now, try to answer the following questions without consulting the logic tables.
What is the result of:
true && true
true || true
!true && true
true || !true
true ^ true
Once you are done, compare your results with the answers below:
true
true
false
true
false
Good job! You completed another small chapter of your programming journey.
In this article, we explored the three basic operations of Boolean algebra: AND
, OR
, and NOT
.
Each of them has a logical table that lists the expected output based on the inputs.
For example, AND returns true
only when both operands are true
, while OR returns false
only when both operands are false
.
Then we looked at the compound operator XOR (exclusive OR).
That operator helps simplify certain scenarios where you want a result of true
when only one of the two operands is true
but not both.
This is very important and will be used in subsequent articles to learn to use boolean algebra to write conditional logic. We will also explore more complex scenarios and some laws to help you simplify your conditional logic. This was a theoretical article that we will practice in the next one of the series.
All in all, Boolean algebra is one of the bases of programming that you can’t escape. Please leave a comment below if you have questions or comments.
It is now time to move to the next article: Using if-else selection statements to write conditional code blocks.
Now that you are done with this article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
In this article, we are creating a small console application using the .NET CLI to get started with .NET 5+ and C#.
|
Introduction to C# variables
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
You are here
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
This is the end of this series
This article series was migrated to a newest version of .NET. Have a look at the .NET 6 series for more!
|
"
.
This article is part of a learn programming series where you need no prior knowledge of programming. If you want to learn how to program and want to learn it using .NET/C#, this is the right place. I suggest reading the whole series in order, starting with Creating your first .NET/C# program, but that’s not mandatory.
This article is part of a sub-series, starting with Introduction to string concatenation. It is not mandatory to read all articles in order, but I strongly recommend it, especially if you are a beginner. If you are already reading the whole series in order, please discard this word of advice.
As mentioned in the introduction, the action of escaping characters means you write an “escape sequence” to represent specific characters.
An escape sequence starts with a \
. It is followed by a character or a series of numbers representing the Unicode character code.
Here are a few examples:
using System;
var quotes = "Wrap \"a few words\" with quotes.";
var newLine = "Add a\nnew line.";
var tab = "\tAdd a tab at the begining of the string.";
var e = "This is the letter e: \u0065";
Console.WriteLine(quotes);
Console.WriteLine(newLine);
Console.WriteLine(tab);
Console.WriteLine(e);
When we run the previous code, we obtain the following result:
If we take a closer look:
\"
to insert a "
character inside a string, which would be impossible otherwise.\n
to insert a new line (see below for more special characters).\t
to insert a horizontal tab (see below for more special characters).\u
, to insert that character into a string (UTF-16 or UTF-32).In my opinion, the most helpful special characters are:
Escape sequence | Character name | Example |
---|---|---|
\' |
Single quote | Not related to string but to char , like var q = '\''; (not covered yet). |
\" |
Double quote | See preceding example. |
\\ |
Backslash | var s = "c:\\folder\\some-file.ext"; |
\n |
New line | See preceding example. |
\r |
Carriage return | Read the OS specific line break section |
\t |
Horizontal tab | See preceding example. |
\u0000 |
Unicode escape sequence (UTF-16) | To write the character e : \u0065 |
\U00000000 |
Unicode escape sequence (UTF-32) | To write the character e : \U00000065 |
\x0[0][0][0] |
Unicode escape sequence (UTF-16) with variable length | To write the character e : \x65 |
See String Escape Sequences for some more characters.
Next, we explore line break.
In case you did not know, Windows line breaks are different from Unix-based platforms.
Windows uses a sequence of both carriage return and new line (\r\n
) while Unix uses only a new line (\n
).
Windows is pretty forgiving and will most likely understand \n
.
Nevertheless, in a cross-platform app targeting both Windows and Unix (e.g., Linux and Mac), do you really want to leave line breaks to chance?
To save the day, we can use the Environment.NewLine
property instead, which adapts to the current OS.
Here is an example (same output as the newLine
variable of the preceding example):
using System;
Console.WriteLine($"Add a{Environment.NewLine}new line."); // We leveraged interpolation here
If we have many line breaks, this can become tedious.
Hopefully, we can simplify this by leveraging the string.Format
method we cover next.
We can use the string.Format
method to format a string.
It is similar to interpolation, but instead of inserting the variables directly, we need to define numerical tokens, like {0}
, {1}
, {2}
, and so forth.
Then, we pass arguments that match those tokens in the correct order to get the final result.
Many other .NET methods allow such a construct, including Console.WriteLine
.
Here is an example of both string.Format
and Console.WriteLine
:
using System;
var newLine = string.Format("Add a{0}new line.", Environment.NewLine);
Console.WriteLine(newLine);
Console.WriteLine("Add a{0}new line.", Environment.NewLine);
When running the code, both Console.WriteLine
output the same result.
The advantage here is writing {0}
every time we need a line break.
Of course, we could use interpolation with {Environment.NewLine}
directly, but it makes the string longer.
Even worst, we could use concatenation like "Add a" + Environment.NewLine + "new line."
; I find this to be the hardest code to read, making the string even longer.
But how does that work? In the background, the framework replaces the numerical tokens with the specified arguments.
There are more to string.Format
, including the possibility to format your values, but that’s getting more and more out of the scope of our main subject.
Next, we look at escaping characters in interpolated strings.
In an interpolated string, we use the characters {
and }
to wrap the element to insert, like a variable.
What if we want to write those characters?
It is as easy as doubling them.
For {
, we write {{
.
For }
, we write }}
.
Here is an example:
var name = "Darth Vader";
var greetings = $"{{Hello {name}}}";
Console.WriteLine(greetings);
As simple as that, the preceding code will output {Hello Darth Vader}
.
Next, let’s have a look at the verbatim identifier (a.k.a. multiline strings).
The verbatim identifier (@
) allows us to write multiline strings, but it’s not all.
It also disables the escaping of characters, which can be very handy when writing file paths (amongst other things).
In other words, a backslash is just a backslash, not a special character that starts an escape sequence.
For example, it is more convenient to write var path = @"c:\folder\some-file.ext";
than var path = "c:\\folder\\some-file.ext";
(simple vs double backslash).
Since \[character]
does not work, how can we escape "
?
Like with interpolation, we only have to double it, like var v = @"Some ""quoted words"" here.";
The verbatim identifier can also be used to escape reserved keywords, like if
, for
, public
, etc.
For example, if you’d like to create a variable named public
, you can’t write var public = "some value";
because this won’t compile.
However, you could prefix your variable name with @
to make it work, like this: var @public = "some value";
.
Using this makes your code a little harder to read, but that’s a trick that can come in very handy sometimes.
More info: one place where it was handy was to create
class
attributes in an old ASP.NET MVC version becauseclass
is a reserved keyword: we had to escape it using the verbatim identifier.
Reference: if you want to know more about reserved keywords, please visit the C# Keywords page in the official documentation.
Next, it’s your turn to try it out.
In this exercise, you will write a program that writes code.
The code to write is the content of the Program.cs
file generated by the dotnet new console
template. We include all the more or less useful plumbing that we removed in the first article of the series.
Why? Well, you will have to write different tokens, escape characters, and be creative to generate that.
Writing code in code can be more challenging than it looks.
To make it a little more complicated, you must ask the user to enter its first and last name, which you will have to embed in the generated code. Here is the expected result (animated sequence):
The textual output was:
using System;
class Exercise
{
public static void Main()
{
var firstName = "Carl-Hugo";
var lastName = "Marcotte";
Console.WriteLine();
Console.WriteLine("Hello {0} {1}", firstName, lastName);
}
}
The spacing at the beginning of the lines (indentation) must be tabs, not spaces, like this:
As a side note, I usually indent my code using spaces because that’s what Visual Studio and VS Code do by default. I hit tab, and it gets converted to the configured number of spaces; very convenient. For the sake of this exercise, I wanted to add a little complexity, so I piked the tab character. Since you are most likely very new to programming, you may not know that for some people, indenting using spaces or tabs is vital; war-like important :wink:.
Now, to your keyboard!
Here are a few optional hints in case you feel stuck:
You can use $@"..."
to combine both interpolation and multiline string but beware the tab characters will be harder to fit in. You can use other techniques too and even combine them.
In a multiline string, doubling the character escapes them. Otherwise, the \
character can escape special characters or create tabs and line breaks.
Once you are done, you can compare with My Solution
below.
Program.cs
using System;
Console.Title = "IntroToDotNet";
Console.Write("What is your first name? ");
var firstName = Console.ReadLine();
Console.Clear();
Console.Write("What is your last name? ");
var lastName = Console.ReadLine();
Console.Clear();
var tab = "\t";
var solution = $@"using System;
class Exercise
{{
{tab}public static void Main()
{tab}{{
{tab}{tab}var firstName = ""{firstName}"";
{tab}{tab}var lastName = ""{lastName}"";
{tab}{tab}Console.WriteLine(""Hello {{0}} {{1}}"", firstName, lastName);
{tab}}}
}}";
Console.WriteLine(solution);
Another alternative would have been to only use interpolation, which results in this very long line:
var solution = $"using System;\n\nclass Exercise\n{{\n\tpublic static void Main()\n\t{{\n\t\tvar firstName = \"{firstName}\";\n\t\tvar lastName = \"{lastName}\";\n\t\tConsole.WriteLine(\"Hello {{0}} {{1}}\", firstName, lastName);\n\t}}\n}}";
You could have used so many combinations that I can’t list them all here. If you succeeded, that’s what is important; no matter how you did it.
Good job! You completed another small chapter of your programming journey.
In this article, we explored the string type a little more.
We discovered that the backslash character (\
) is used to escape characters or to create special characters like tabs (\t
) and line breaks (\r
and \n
).
We then learned about the Environment.NewLine
property that gives us the correct platforms-specific line break.
We also peaked at string.Format
to format strings using numerical tokens instead of interpolation.
Afterward, we explored some edge cases to escape {
and }
in interpolated strings and "
in multiline strings.
The solution was to double the characters; a.k.a. write ""
to obtain "
.
We saw that in @"..."
strings, the escape character (\
) does not work, which is an advantage that can become a disadvantage.
We finally learned to use the verbatim identifier (@
) to bypass the reserved keywords limitation and use it as an escape character for identifiers.
All in all, that’s a lot of new content that concludes our introduction to strings mini-sub-series.
It is now time to move to the next article: Introduction to Boolean algebra and logical operators.
Now that you are done with this article, please look at the series’ content.
Articles in this series |
---|
Creating your first .NET/C# program
In this article, we are creating a small console application using the .NET CLI to get started with .NET 5+ and C#.
|
Introduction to C# variables
In this article, we explore variables. What they are, how to create them, and how to use them. Variables are essential elements of a program, making it dynamic.
|
Introduction to C# constants
In this article, we explore constants. A constant is a special kind of variable.
|
Introduction to C# comments
In this article, we explore single-line and multiline comments.
|
How to read user inputs from a console
In this article, we explore how to retrieve simple user inputs from the console. This will help us make our programs more dynamic by interacting with the user.
|
Introduction to string concatenation
In this article, we dig deeper into strings and explore string concatenation.
|
Introduction to string interpolation
In this article, we explore string interpolation as another way to compose a string.
|
Escaping characters in C# strings
You are here
In this article, we explore how to escape characters like quotes and how to write special character like tabs and new lines.
|
Introduction to Boolean algebra and logical operators
This article introduces the mathematical branch of algebra that evaluates the value of a condition to true or false.
|
Using if-else selection statements to write conditional code blocks
In this article, we explore how to write conditional code using Boolean algebra.
|
Using the switch selection statement to simplify conditional statements blocks
In this article, we explore how to simplify certain conditional blocks by introducing the switch statement.
|
Boolean algebra laws
This article explores multiple Boolean algebra laws in a programmer-oriented way, leaving the mathematic notation aside.
|
This is the end of this series
This article series was migrated to a newest version of .NET. Have a look at the .NET 6 series for more!
|