ms_learn_csharp/010_conventions/010_csharp.md

682 lines
24 KiB
Markdown
Raw Normal View History

2024-07-17 16:32:54 -04:00
# Create readable code with conventions, whitespace, and comments in C#
Write code that is easier to read, update and support using naming conventions,
comments and whitespace.
### Learning objectives
In this module, you will:
- Choose descriptive names for variables that describe their purpose and intent.
- Use code comments to temporarily instruct the compiler to ignore lines of code.
- Use code comments to describe higher-level requirements or purpose for a
passage of code.
- Write code that effectively uses whitespace to convey the relationship of
lines of code.
Prerequisites
- Experience declaring variables using common data types.
- Experience using `Console.WriteLine()` to output messages to a console.
- Experience using the `if` and `else if` statement.
## Introduction
The code that you write should communicate your intent to both the compiler
and other developers who may need to read your code. And since you're the
developer who will be reading your code most often, sometimes months after you
originally wrote it, it's in your best interest to write code that's clear and
easy to understand. Remember, you may write code once, but you will need to
read it many times.
Suppose you've been asked to write some code for another group of developers.
You meet with them to discuss the specification and the assignment is clear.
After the meeting they tell you that you'll be working independently during
development. Once you're done, you'll hand off your code to the other group.
The coding task isn't beyond your skill level, but you've never had to write
code that someone else will be maintaining. The team told you that as long as
you to follow the standard coding conventions for C#, there should be no
problem. You make plans to review the C# coding conventions that relate to the
code you're going to work on.
In this module, you'll learn how to choose names for your variables that
describe their purpose and intent. You'll learn how to add code comments that
document the higher-level requirements and your approach in code, as well as
to temporarily instruct the compiler to ignore lines of code. Finally, you'll
learn how whitespace can be used to help convey the relationship of individual
lines of code.
By the end of this module, you'll write code more purposefully, focusing on
the readability and quality of the code to communicate to both the compiler
and other developers.
---
## Choose variable names that follow rules and conventions
A software developer once famously said, "The hardest part of software
development is naming things." Not only does the name of a variable have to
follow certain syntax rules, it should also be used to make the code more
human-readable and understandable. That's a lot to ask of one line of code!
### Variable name rules
There are some variable naming rules that are enforced by the C# compiler.
- Variable names can contain alphanumeric characters and the underscore (`_`)
character. Special characters like the pound `#,` the dash `-,` and the dollar
sign `$` are not allowed.
- Variable names must begin with an alphabetical letter or an underscore, not
a number. Using an underscore character to start a variable name is typically
reserved for private instance fields. A link to further reading can be found
in the module summary.
- Variable names must NOT be a C# keyword. For example, these variable name
declarations won't be allowed: `float float;` or `string string;`.
- Variable names are case-sensitive, meaning that `string MyValue;` and
`string myValue;` are two different variables.
- Variable name conventions
Conventions are suggestions that are agreed upon by the software
development community. While you're free to decide not to follow these
conventions, they're so popular that it might make it difficult for other
developers to understand your code. You should practice adopting these
conventions and make them part of your own coding habits.
- Variable names should use camel case, which is a style of writing that uses
a lower-case letter at the beginning of the first word and an upper-case
letter at the beginning of each subsequent word. For example:
`string thisIsCamelCase;`.
- Variable names should be descriptive and meaningful in your application. You
should choose a name for your variable that represents the kind of data it
will hold (not the data type). For example: `bool orderComplete;`, NOT
`bool isComplete;`.
- Variable names should be one or more entire words appended together. Don't
use contractions because the name of the variable may be unclear to others who
are reading your code. For example: `decimal orderAmount;`, NOT
`decimal odrAmt;`.
- Variable names shouldn't include the data type of the variable. You might
see some advice to use a style like `string strMyValue;`. It was a popular
style years ago. However, most developers don't follow this advice anymore and
there are good reasons not to use it.
The example string firstName; follows all of these rules and conventions,
assuming I want to use this variable to store data that represents someone's
first name.
#### Variable name examples
Here's a few examples of variable declarations (using common data types):
```cs
char userOption;
int gameScore;
float particlesPerMillion;
bool processedCustomer;
```
#### Other naming conventions
The rules and conventions described above are for local variables. A local
variable is a variable that is scoped within the body of a method, or a
variable in a console application that uses top-level statements (like the
code in this module).
There are other types of constructs that you can use in your applications, and
many have their own conventions. For example, classes are often used in C#
programming, and have associated conventions. Although you won't be creating
classes in this module, it's important for you to know that the naming
conventions you just learned about fit into a larger naming framework.
---
## Exercise
### Create effective code comments
In this exercise, you'll add notes to your code and temporarily disable certain lines of code from compilation. Then you'll look at how the C# compiler understands whitespace and how to use whitespace to increase the readability of your code.
#### What is a code comment?
A code comment is an instruction to the compiler to ignore everything after
the code comment symbols in the current line.
```cs
// This is a code comment!
```
This may not seem useful at first, however it's useful in three situations:
- When you want to leave a note about the intent of a passage of code. It can
be helpful to include code comments that describe the purpose or the thought
process when you're writing a particularly challenging set of coding
instructions. Your future self will thank you.
- When you want to temporarily remove code from your application to try a
different approach, but you're not yet convinced your new idea will work. You
can comment out the code, write the new code, and once you're convinced the
new code will work the way you want it to, you can safely delete the old
(commented code).
- Adding a message like `TODO` to remind you to look at a given passage of
code later. While you should use this judiciously, it's a useful approach. You
may be working on another feature when you read a line of code that sparks a
concern. Rather than ignoring the new concern, you can mark it for
investigation later.
> Note
> Code comments should be used to say what the code cannot. Often, developers
update their code but forget to update the code comments. It's best to use
comments for higher-level ideas and not to add comments about how an
individual line of code works.
At the Terminal command prompt, to create a new console application in a
specified folder, type dotnet `new console -o ./PathTo/Project` and then press
Enter.
#### Create and use code comments
In this task, you will try creating and removing various types of code comments.
Enter the following code:
```cs
string firstName = "Bob";
int widgetsSold = 7;
Console.WriteLine($"{firstName} sold {widgetsSold} widgets.");
```
To modify your code with code comments and revisions, update your code as
follows:
```cs
string firstName = "Bob";
int widgetsPurchased = 7;
// Testing a change to the message.
// int widgetsSold = 7;
// Console.WriteLine($"{firstName} sold {widgetsSold} widgets.");
Console.WriteLine($"{firstName} purchased {widgetsPurchased} widgets.");
```
Take a minute to review your comments and code updates.
Notice that the code comments are used to document the potential change being
made, and to temporarily disable the old message as you test the new message.
Your next step will be to test your update. If you're satisfied with the new
code, you can safely delete the old code that was commented out. This is a
safer, more methodical approach to modifying working code until you're
convinced that you're ready to permanently remove it.
At the Terminal command prompt, type dotnet run and then press Enter.
You should see the following output:
```txt
Bob purchased 7 widgets.
```
Again, if you're satisfied with your updates, delete the old code that's
commented out.
Delete the code comments.
Your code should match the following:
```cs
string firstName = "Bob";
int widgetsPurchased = 7;
Console.WriteLine($"{firstName} purchased {widgetsPurchased} widgets.");
```
To apply a block comment that comments out multiple lines, update your code as
follows:
```cs
/*
string firstName = "Bob";
int widgetsPurchased = 7;
Console.WriteLine($"{firstName} purchased {widgetsPurchased} widgets.");
*/
```
Block comments are great if you need to write a long comment or remove many
lines of code. Block comments use a `/*` at the beginning of the code and a
`*/` at the end. Using a block comment is the quickest and easiest way to
disable three or more lines of code.
Replace your existing code with the following:
```cs
Random random = new Random();
string[] orderIDs = new string[5];
// Loop through each blank orderID
for (int i = 0; i < orderIDs.Length; i++){
// Get a random value that equates to ASCII letters A through E
int prefixValue = random.Next(65, 70);
// Convert the random value into a char, then a string
string prefix = Convert.ToChar(prefixValue).ToString();
// Create a random number, pad with zeroes
string suffix = random.Next(1, 1000).ToString("000");
// Combine the prefix and suffix together, then assign to current OrderID
orderIDs[i] = prefix + suffix;
}
// Print out each orderID
foreach (var orderID in orderIDs){
Console.WriteLine(orderID);
}
```
> Note
> There are many C# concepts in this code listing that may be new to you. It's
not necessary to understand what the code is doing in order to appreciate how
comments can help readers understand the purpose of the code.
Take a minute to see if you can figure out the purpose of the code.
Given the comments, you might be able to figure out what the code is doing
(assuming the comments accurately describe the current state and were updated
as the code was updated). But can you guess why this code exists? Wouldn't it
be helpful if there was some explanation at the top of the code file that
provided some context and described its purpose?
Consider how you would improve the comments.
Notice that there are two main problems with these comments:
- The code comments unnecessarily explain the obvious functionality of
individual lines of code. These are considered low-quality comments because
they merely explain how C# or methods of the .NET Class Library work. If the
reader is unfamiliar with these ideas, they can look them up using
learn.microsoft.com or IntelliSense.
- The code comments don't provide any context to the problem being solved by
the code. These are considered low-quality comments because the reader doesn't
gain any insight into the purpose of this code, especially as it relates to
the larger system.
Remove the existing comments.
Your code should match the following:
```cs
Random random = new Random();
string[] orderIDs = new string[5];
for (int i = 0; i < orderIDs.Length; i++){
int prefixValue = random.Next(65, 70);
string prefix = Convert.ToChar(prefixValue).ToString();
string suffix = random.Next(1, 1000).ToString("000");
orderIDs[i] = prefix + suffix;
}
foreach (var orderID in orderIDs){
Console.WriteLine(orderID);
}
```
Notice that the code is already less cluttered.
To add a comment that explains the higher-level purpose of your code, update
your code as follows:
```cs
/*
The following code creates five random OrderIDs
to test the fraud detection process. OrderIDs
consist of a letter from A to E, and a three
digit number. Ex. A123.
*/
Random random = new Random();
string[] orderIDs = new string[5];
for (int i = 0; i < orderIDs.Length; i++){
int prefixValue = random.Next(65, 70);
string prefix = Convert.ToChar(prefixValue).ToString();
string suffix = random.Next(1, 1000).ToString("000");
orderIDs[i] = prefix + suffix;
}
foreach (var orderID in orderIDs){
Console.WriteLine(orderID);
}
```
A comment's usefulness is subjective. In all matters related to code
readability, you should use your best judgment. Do what you think is best to
improve the clarity of your code.
### Recap
The main takeaways from this exercise:
- Use code comments to leave meaningful notes to yourself about the problem
your code solves.
- Don't use code comments that explain how C# or the .NET Class Library
works.
- Use code comments when temporarily trying alternative solutions until you're
ready to commit to the new code solution, at which point you can delete the
old code.
- Never trust comments. They may not reflect the current state of the code
after many changes and updates.
---
## Exercise
### Use whitespace to make your code easier to read
Print and web designers understand that putting too much information in a
small space overwhelms the viewer. So, they strategically use whitespace, or
negative space, to break up information to maximize the viewer's ability to
consume the primary message of their work.
Developers can use a similar strategy when writing code in an editor. By using
white space to convey meaning, developers can increase the clarity of their
code's intent.
### What is whitespace?
The term "whitespace" refers to individual spaces produced by the `space bar`,
tabs produced by the `tab` key, and new lines produced by the `enter` key.
The C# compiler ignores whitespace. To understand how whitespace is ignored,
and how to maximize clarity using white space, work through the following exercise.
#### Add code to illustrate how whitespace is ignored by the C# compiler
Enter the following code:
```cs
// Example 1:
Console
.
WriteLine
(
"Hello Example 1!"
)
;
// Example 2:
string firstWord="Hello";string lastWord="Example 2";Console.WriteLine(firstWord+" "+lastWord+"!");
```
At the Terminal command prompt, type `dotnet run` and then press Enter.
You should see the following output:
```txt
Hello Example 1!
Hello Example 2!
```
Take a minute to consider what this result tells you about how you should use
whitespace in your code.
These two code examples illustrate two vital ideas:
- Whitespace doesn't matter to the compiler. However ...
- Whitespace, when used properly, can increase your ability to read and
comprehend the code.
You likely write your code once, but need to read the code multiple times.
Therefore, you should focus on the readability of the code you write. Over time,
you'll get a feel for when and how to use whitespace, such as the space
character, tabs, and new lines.
Early guidance:
- Each complete command (a statement) belongs on a separate line.
- If a single line of code becomes long, you can break it up. However, you
should avoid arbitrarily splitting up a single statement to multiple lines
until you have a good reason to do so.
- Use a space to the left and right of the assignment operator.
Replace your existing code with the following code:
```cs
Random dice = new Random();
int roll1 = dice.Next(1, 7);
int roll2 = dice.Next(1, 7);
int roll3 = dice.Next(1, 7);
int total = roll1 + roll2 + roll3;
Console.WriteLine($"Dice roll: {roll1} + {roll2} + {roll3} = {total}");
if ((roll1 == roll2) || (roll2 == roll3) || (roll1 == roll3)) {
if ((roll1 == roll2) && (roll2 == roll3)) {
Console.WriteLine("You rolled triples! +6 bonus to total!");
total += 6;
} else {
Console.WriteLine("You rolled doubles! +2 bonus to total!");
total += 2;
}
}
```
Notice that this code doesn't include much whitespace. This code will be used
to illustrate an approach for adding whitespace to your applications.
Effective whitespace should make it easier to understand what your code is
doing.
> Note
> The code uses the `Random` class to help develop a simulated dice game, where
the total value from three rolls is used to evaluate a "winning" score. The
code awards extra points for rolling doubles or triples. You don't need to
fully understand this code in order to see the benefit of including whitespace.
Take a minute to consider how you would use whitespace to improve the
readability of this code.
There are two features of this code to take note of:
- There's no vertical whitespace in this code example. In other words, there's
no empty lines separating the lines of code. It all runs together into one
dense code listing.
- The code blocks as defined by the opening and closing curly brace symbols
`{ }` are compressed together, making their boundaries difficult to visually
discern.
Generally speaking, to improve readability, you introduce a blank line between
two, three, or four lines of code that do similar or related things.
Phrasing your code using vertical whitespace is subjective. It's possible two
developers won't agree on what is most readable, or when to add whitespace.
Use your best judgment.
To add vertical whitespace that improves readability, update your code as
follows:
```cs
Random dice = new Random();
int roll1 = dice.Next(1, 7);
int roll2 = dice.Next(1, 7);
int roll3 = dice.Next(1, 7);
int total = roll1 + roll2 + roll3;
Console.WriteLine($"Dice roll: {roll1} + {roll2} + {roll3} = {total}");
if ((roll1 == roll2) || (roll2 == roll3) || (roll1 == roll3)) {
if ((roll1 == roll2) && (roll2 == roll3)) {
Console.WriteLine("You rolled triples! +6 bonus to total!");
total += 6;
} else {
Console.WriteLine("You rolled doubles! +2 bonus to total!");
total += 2;
}
}
```
Your first line of whitespace is used to separate the declaration of the `dice`
variable from the code lines used to assign values to your roll variables.
This separation makes it easier to see how `dice` is being used in your code.
Your next line of whitespace separates the declaration of your roll variables
from the declaration of `total`. Grouping the declaration of your three roll
variables is helpful in two ways. First, it creates a group of code lines that
includes related variables. Second, the variable names are so similar and the
declaration follows the same pattern. So, grouping them together draws your
eye to the similarities and helps to expose the differences.
Finally, your third line of whitespace separates another group of related
statements from your nested `if` statements. The group of statements that
includes the declaration of `total` and the `Console.WriteLine()` method is
related by purpose rather than appearance. Your code is focused on the total
value achieved by the three dice and whether the roll included doubles or
triples. These lines are related because you need to calculate `total` and
report the results of the roll to the user.
Some developers might argue that you should add an empty line in between the
declaration of `total` and the `Console.WriteLine()`. Again, the choice of
whitespace is up to your best judgment. You should decide which is more
readable for you and use that style consistently.
All that you have left is the `if` statement. You can examine that now.
Focusing on the lines of code below the `if` keyword, modify your code as follows:
```cs
Random dice = new Random();
int roll1 = dice.Next(1, 7);
int roll2 = dice.Next(1, 7);
int roll3 = dice.Next(1, 7);
int total = roll1 + roll2 + roll3;
Console.WriteLine($"Dice roll: {roll1} + {roll2} + {roll3} = {total}");
// HORRIBLE
if ((roll1 == roll2) || (roll2 == roll3) || (roll1 == roll3))
{
if ((roll1 == roll2) && (roll2 == roll3))
{
Console.WriteLine("You rolled triples! +6 bonus to total!");
total += 6;
}
else
{
Console.WriteLine("You rolled doubles! +2 bonus to total!");
total += 2;
}
}
```
Notice that you've moved the opening and closing curly braces to their own
line to improve spacing.
The `{` and `}` symbols create code blocks. Many C# constructs require code
blocks. These symbols should be placed on a separate line so that their
boundaries are clearly visible and readable.
Furthermore, it's important to use the `tab` key to line up the code block
symbols under the keyword they belong to. For example, notice the line of code
that starts with the keyword `if`. Below that line is the `{` symbol. This
alignment makes it easy to understand that the `{` "belongs to" the `if`
statement. Furthermore, the last `}` symbol lines up with the `if` statement
as well. The combination of alignment and indentation makes it easy to
understand where the code block begins and ends.
The code lines inside of this code block are indented, indicating that they
"belong" to this code block.
You follow a similar pattern with the inner `if` statement and `else` statement,
and the code inside of those code blocks.
**Not everyone agrees with this style** guidance for including whitespace.
However, you should consider using this guidance as a starting point when
writing code. In the future, you can be purposeful when making a decision to
deviate from this guidance.
### Recap
The main takeaways from this exercise:
- Use whitespace judiciously to improve the readability of your code.
- Use line feeds to create empty lines to separate phrases of code. A phrase
includes lines of code that are similar, or work together.
- Use line feeds to separate code block symbols so that they are on their own
line of code.
- Use the tab key to line up a code block with the keyword they're associated
with.
- Indent code inside of a code block to show ownership.
---
## Exercise
### Complete a challenge activity to improve code readability
Code challenges will reinforce what you've learned and help you gain some
confidence before continuing on.
### Code readability challenge
In this challenge, you'll use the techniques you learned in this module to
improve the readability of a code sample. You are provided with a code sample
that is poorly styled and commented. Your goal is to update the code using
style guidelines for variable names, code comments, and whitespace to improve
code readability.
#### Apply style guidelines to improve readability
```cs
string str = "The quick brown fox jumps over the lazy dog.";
// convert the message into a char array
char[] charMessage = str.ToCharArray();
// Reverse the chars
Array.Reverse(charMessage);
int x = 0;
// count the o's
foreach (char i in charMessage) { if (i == 'o') { x++; } }
// convert it back to a string
string new_message = new String(charMessage);
// print it out
Console.WriteLine(new_message);
Console.WriteLine($"'o' appears {x} times.");
```
> Note
> This code sample may include .NET Class Library methods that are unfamiliar
to you. For example, you may not be familiar with the `ToCharArray()` method of
the `String` class, or the `Reverse` method of the `Array` class. You do not
need to fully understand the code sample in order to be successful in this
challenge.
> Tip
> The high-level purpose of this code is to reverse a string and count the
number of times a particular character appears.
To improve readability, update the code using style guidelines.
Use the techniques that you learned in this module to make improvements to the
code and increase its readability.
---
```cs
/*
This code reverses a message, counts the number of times
a particular character appears, then prints the results
to the console window.
*/
string orig_msg = "The quick brown fox jumps over the lazy dog.";
char[] msg = orig_msg.ToCharArray();
Array.Reverse(msg);
int ltr_count = 0;
foreach (char ltr in msg) {
if (ltr == 'o') {
ltr_count++;
}
}
string new_message = new String(msg);
Console.WriteLine(new_message);
Console.WriteLine($"'o' appears {ltr_count} times.");
```
- [read more](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names)