Console Input and Output

This chapter already used Console.WriteLine repeatedly for writing out text to the command console. In addition to being able to write out data, a program needs to be able to accept data that a user may enter.

Getting Input from the Console

One way to retrieve text that is entered at the console is to use Console.ReadLine(). This method stops the program execution so that the user can enter characters. When the user presses the Enter key, creating a newline, the program continues. The output, also known as the return, from the Console.ReadLine() method is the string of text that was entered. Consider Listing 1.16 and the corresponding output shown in Output 1.4.

Listing 1.16: Using Console.ReadLine()
public class HeyYou
{
    public static void Main()
    {
        string firstName;
        string lastName;
 
        Console.WriteLine("Hey you!");
 
        Console.Write("Enter your first name: ");
        firstName = Console.ReadLine();
 
        Console.Write("Enter your last name: ");
        lastName = Console.ReadLine();
    }
}
Output 1.4
Hey you!
Enter your first name: Inigo
Enter your last name: Montoya

After each prompt, this program uses the Console.ReadLine() method to retrieve the text the user entered and assign it to an appropriate variable. By the time the second Console.ReadLine() assignment completes, firstName refers to the value Inigo and lastName refers to the value Montoya.

If you encounter a CS8600, “Converting null literal or possible null value to non-nullable type,” warning when assigning Console.ReadLine(), you can safely ignore it until Chapter 2. Alternatively, use string? rather than string when declaring the firstName and lastName variables to address the warning. You can also disable the nullable-related warnings entirely by disabling them, setting the Nullable element in your project file to disable (<Nullable>disable</Nullable>) within the PropertyGroup element of the .csproj file.

AdVanceD Topic
System.Console.Read()

In addition to the System.Console.ReadLine() method, there is a System.Console.Read() method. However, the data type returned by the System.Console.Read() method is an integer corresponding to the character value read, or –1 if no more characters are available. To retrieve the actual character, it is necessary to first cast the integer to a character, as shown in Listing 1.17.

Listing 1.17: Using System.Console.Read()
int readValue;
char character;
readValue = Console.Read();
character = (char) readValue;
Console.Write(character);

The Console.Read() method does not return the input until the user presses the Enter key; no processing of characters will begin, even if the user types multiple characters before pressing the Enter key.

You can use Console.ReadKey(),11 which, in contrast to Console.Read(), returns the input after a single keystroke. It allows the developer to intercept the keystroke and perform actions such as key validation or to restrict the characters to numerics.

Writing Output to the Console

Listing 1.16 prompted the user for their first and last names using the method Console.Write() rather than Console.WriteLine(). Instead of placing a newline character after displaying the text, the Console.Write() method leaves the current position on the same line. In this way, any text the user enters will be on the same line as the prompt for input. The output from Listing 1.16 demonstrates the effect of Console.Write().

The next step is to write the values retrieved using Console.ReadLine() back to the console. In the case of Listing 1.18, the program writes out the user’s full name. However, instead of using Console.WriteLine() as before, this code uses a slight variation that leverages string interpolation.12 Notice in Listing 1.18, the dollar sign preceding the string literal in the call to Console.WriteLine; it indicates that string interpolation will be used. Output 1.5 shows the corresponding output.

Listing 1.18: Formatting Using String Interpolation
public class HeyYou
{
    public static void Main()
    {
        string firstName;
        string lastName;
 
        Console.WriteLine("Hey you!");
 
        Console.Write("Enter your first name: ");
        firstName = Console.ReadLine();
 
        Console.Write("Enter your last name: ");
        lastName = Console.ReadLine();
 
        Console.WriteLine(
            $"Your full name is { firstName } { lastName }.");
    }
}
Output 1.5
Hey you!
Enter your first name: Inigo
Enter your last name: Montoya
Your full name is Inigo Montoya.

Instead of writing out “Your full name is” followed by another Write statement for firstName, a third Write statement for the space, and finally a WriteLine statement for lastName, Listing 1.18 writes out the entire output string interpolation. With string interpolation, the compiler interprets the interior of the curly brackets within the string as regions in which you can embed code (expressions) that the compiler will evaluate and convert to strings. Rather than executing lots of code snippets individually and combining the results as a string at the end, string interpolation allows you to do this in a single step. This makes the code easier to understand.

Prior to C# 6.0, C# used a different approach, that of composite formatting. With composite formatting, the code first supplies a format string to define the output format—see Listing 1.19.

Listing 1.19: Formatting Using Console.WriteLine()’s Composite Formatting
public class HeyYou
{
    public static void Main()
    {
        string firstName;
        string lastName;
 
        Console.WriteLine("Hey you!");
 
        Console.Write("Enter your first name: ");
        firstName = Console.ReadLine();
 
        Console.Write("Enter your last name: ");
        lastName = Console.ReadLine();
 
        Console.WriteLine(
            "Your full name is {0} {1}.", firstName, lastName);
    }
}

In this example, the format string is Your full name is {0} {1}. It identifies two indexed placeholders for data insertion in the string. Each placeholder corresponds to the order of the arguments that appear after the format string.

Note that the index value begins at zero. Each inserted argument (known as a format item) appears after the format string in the order corresponding to the index value. In this example, since firstName is the first argument to follow immediately after the format string, it corresponds to index value 0. Similarly, lastName corresponds to index value 1.

Note that the placeholders within the format string need not appear in order. For example, Listing 1.20 switches the order of the indexed placeholders and adds a comma, which changes the way the name is displayed (see Output 1.6).

Listing 1.20: Swapping the Indexed Placeholders and Corresponding Variables
Console.WriteLine("Your full name is {1}, {0}.", firstName, lastName);
Output 1.6
Hey you!
Enter your first name: Inigo
Enter your last name: Montoya
Your full name is Montoya, Inigo

In addition to not having the placeholders appear consecutively within the format string, it is possible to use the same placeholder multiple times within a format string. Furthermore, it is possible to omit a placeholder. It is not possible, however, to have placeholders that do not have a corresponding argument.

note
Since string interpolation is almost always easier to understand than the alternative composite string approach, throughout the remainder of the book we use string interpolation by default.
Comments

In this section, we modify the program in Listing 1.19 by adding comments. In no way does this modification change the execution of the program; rather, providing comments within the code can simply make the code more understandable in areas where it isn’t inherently clear. Listing 1.21 shows the new code, and Output 1.7 shows the corresponding output.

Listing 1.21: Commenting Your Code
public class CommentSamples
{
    public static void Main()
    {
        string firstName; // Variable for storing the first name
        string lastName;  // Variable for storing the last name
 
        Console.WriteLine("Hey you!");
 
        Console.Write /* No new line */ ("Enter your first name: ");
        firstName = Console.ReadLine();
 
        Console.Write /* No new line */ ("Enter your last name: ");
        lastName = Console.ReadLine();
 
        /* Display a greeting to the console 
          using composite formatting. */
 
        Console.WriteLine("Your full name is {1}, {0}."
            firstName, lastName);
        // This is the end
        // of the program listing
    }
}
Output 1.7
Hey you!
Enter your first name: Inigo
Enter your last name: Montoya
Your full name is Inigo Montoya.

Despite the inserted comments, compiling and executing the new program produces the same output as before.

Programmers use comments to describe and explain the code they are writing, especially where the syntax itself is difficult to understand, or perhaps a particular algorithm implementation is surprising. Since comments are pertinent only to the programmer reviewing the code, the compiler ignores comments and generates an assembly that is devoid of any trace that comments were part of the original source code.

Table 1.2 shows four different C# comment types. The program in Listing 1.21 includes three of these.

Table 1.2: C# Comment Types

Comment Type

Description

Example

Delimited comments

A forward slash followed by an asterisk, /*, identifies the beginning of a delimited comment. To end the comment, use an asterisk followed by a forward slash: */. Comments of this form may span multiple lines in the code file or appear embedded within a line of code. The asterisks that appear at the beginning of the lines but within the delimiters are simply for formatting.

/*comment*/

Single-line comments

Comments may be declared with a delimiter comprising two consecutive forward slash characters: //. The compiler treats all text from the delimiter to the end of the line as a comment. Comments of this form are considered a single line. It is possible, however, to place sequential single-line comments one after another, as is the case with the last comment in Listing 1.19.

//comment

XML delimited comments

Comments that begin with /** and end with **/ are called XML delimited comments. They have the same characteristics as regular delimited comments, except that instead of ignoring XML comments entirely, the compiler can place them into a separate text file.13

/**comment**/

XML single-line comments

XML single-line comments begin with /// and continue to the end of the line. In addition, the compiler can save single-line comments into a separate file with the XML delimited comments.

///comment

A more comprehensive discussion of the XML comments and how they are leveraged to generate API documentation appears in Chapter 10, where we further discuss the various XML tags.

There was a period in programming history when a prolific set of comments implied a disciplined and experienced programmer. This is no longer the case. Instead, code that is readable without comments is more valuable than that which requires comments to clarify what it does. If developers find it necessary to enter comments to clarify what a block of code is doing, they should favor rewriting the code more clearly over commenting it. Writing comments that simply repeat what the code clearly shows serves only to clutter the code, decrease its readability, and increase the likelihood of the comments going out of date because the code changes without the comments getting updated.

Guidelines
DO NOT use comments unless they describe something that is not obvious to someone other than the developer who wrote the code.
DO favor writing clearer code over entering comments to clarify a complicated algorithm.

Beginner Topic
Extensible Markup Language

The Extensible Markup Language (XML) is a simple and flexible hierarchical text format. XML is extensible because included within an XML document is information that describes the data, known as metadata. Here is a sample XML file:

<?xml version="1.0" encoding="utf-8" ?>

<body>

   <book title="Essential C# 11.0">

       <chapters>

           <chapter title="Introducing C#"/>

           <chapter title="Data Types"/>

           ...

       </chapters>

   </book>

</body>

The file starts with a header indicating the version and character encoding of the XML file, after which appears one main “book” element. Elements begin with a word in angle brackets, such as <body>. To end an element, place the same word in angle brackets and add a forward slash to prefix the word, as in </body>. In addition to elements, XML supports attributes. title="Essential C#" is an example of an XML attribute. Note that the metadata (book title, chapter, and so on) describing the data (“Essential C#,” “Data Types”) is included in the XML file. This can result in rather bloated files, but it offers the advantage that the data includes a description to aid in interpreting it.

Debugging

One significant feature of using an IDE is its support for debugging. To try it, follow these additional steps in either operating system version of Visual Studio or in Visual Studio Code:

1.
With the latest version of Program.cs open (Listing 1.9), put your cursor on the last Console.WriteLine line and click the Debug->Toggle Breakpoint (F9) menu item to activate a breakpoint on that line.
2.
In Visual Studio, click the Debug->Start Debugging (F5) menu to relaunch the application but this time with debugging activated.
3.
Visual Studio Code is similar except the menu is Run->Start Debugging (F5). Visual Studio Code also displays a notification to create the build and debug assets, the launch.json and tasks.json files, if you haven’t created them already. Because of the Console.ReadLine statement, you will also need to change the console line in the launch.json file to use “integratedTerminal” rather than “internalConsole.” Now, when debugging, be sure to switch to the TERMINAL output window to respond to your program’s prompts.
4.
Once debugging starts, execution will stop on the line where you set the breakpoint. You can then hover your cursor over a variable (e.g., firstName) to see its value.
5.
In Visual Studio, you can even move the execution of the program from the current line to another line within the method by dragging the yellow arrow in the left margin of the file window.
6.
To continue the program execution, use the Continue button for either IDE (or the Debug->Continue (F5) menu in Visual Studio or Run->Continue (F5) in Visual Studio Code).

For more information about debugging, see the links corresponding to each version:

________________________________________

11. In C# 2.0 and above.
12. A C# 6.0 feature.
13. XML delimited comments were explicitly added only in C# 2.0, but the syntax is compatible with C# 1.0.
{{ snackbarMessage }}