You can find the wrox.com
code downloads for this chapter at www.wrox.com/go/beginningvisualc#2015programming
on the Download Code tab. The code is in the Chapter 3 download and individually named according to the names throughout the chapter.
To use C# effectively, it's important to understand what you're actually doing when you create a computer program. Perhaps the most basic description of a computer program is that it is a series of operations that manipulate data. This is true even of the most complicated examples, including vast, multi-featured Windows applications (such as the Microsoft Office Suite). Although this is often completely hidden from users of applications, it is always going on behind the scenes.
To illustrate this further, consider the display unit of your computer. What you see onscreen is often so familiar that it is difficult to imagine it as anything other than a “moving picture.” In fact, what you see is only a representation of some data, which in its raw form is merely a stream of 0s and 1s stashed away somewhere in the computer's memory. Any onscreen action — moving a mouse pointer, clicking on an icon, typing text into a word processor — results in the shunting around of data in memory.
Of course, simpler situations show this just as well. When using a calculator application, you are supplying data as numbers and performing operations on the numbers in much the same way as you would with paper and pencil — but a lot quicker!
If computer programs are fundamentally performing operations on data, this implies that you need a way to store that data, and some methods to manipulate it. These two functions are provided by variables and expressions, respectively, and this chapter explores what that means, both in general and specific terms.
First, though, you'll take a look at the basic syntax involved in C# programming, because you need a context in which you can learn about and use variables and expressions in the C# language.
The look and feel of C# code is similar to that of C++ and Java. This syntax can look quite confusing at first and it's a lot less like written English than some other languages. However, as you immerse yourself in the world of C# programming, you'll find that the style used is a sensible one, and it is possible to write very readable code without much effort.
Unlike the compilers of some other languages such as Python, C# compilers ignore additional spacing in code, whether it results from spaces, carriage returns, or tab characters (collectively known as whitespace characters). This means you have a lot of freedom in the way that you format your code, although conforming to certain rules can help make your code easier to read.
C# code is made up of a series of statements, each of which is terminated with a semicolon. Because whitespace is ignored, multiple statements can appear on one line, although for readability it is usual to add carriage returns after semicolons, to avoid multiple statements on one line. It is perfectly acceptable (and quite normal), however, to use statements that span several lines of code.
C# is a block-structured language, meaning statements are part of a block of code. These blocks, which are delimited with curly brackets ({
and }
), may contain any number of statements, or none at all. Note that the curly bracket characters do not need accompanying semicolons.
For example, a simple block of C# code could take the following form:
{
<code line 1, statement 1>;
<code line 2, statement 2>
<code line 3, statement 2>;
}
Here the <code
line
x,
statement
y>
sections are not actual pieces of C# code; this text is used as a placeholder where C# statements would go. In this case, the second and third lines of code are part of the same statement, because there is no semicolon after the second line. Indenting the third line of code makes it easier to recognize that it is actually a continuation of the second line.
The following simple example uses indentation to clarify the C# itself. This is actually standard practice, and in fact Visual Studio automatically does this for you by default. In general, each block of code has its own level of indentation, meaning how far to the right it is. Blocks of code may be nested inside each other (that is, blocks may contain other blocks), in which case nested blocks will be indented further:
{
<code line 1>;
{
<code line 2>;
<code line 3>;
}
<code line 4>;
}
In addition, lines of code that are continuations of previous lines are usually indented further as well, as in the third line of code in the first code example.
Of course, this style is by no means mandatory. If you don't use it, however, you will quickly find that things can get very confusing as you move through this book!
Comments are something else you often see in C# code. A comment is not, strictly speaking, C# code at all, but it happily cohabits with it. Comments are self-explanatory: They enable you to add descriptive text to your code — in plain English (or French, German, Mongolian, and so on) — which is ignored by the compiler. When you start dealing with lengthy code sections, it's useful to add reminders about exactly what you are doing, such as “this line of code asks the user for a number” or “this code section was written by Bob.”
C# provides two ways of doing this. You can either place markers at the beginning and end of a comment or you can use a marker that means “everything on the rest of this line is a comment.” The latter method is an exception to the rule mentioned previously about C# compilers ignoring carriage returns, but it is a special case.
To indicate comments using the first method, you use /*
characters at the start of the comment and */
characters at the end. These may occur on a single line, or on different lines, in which case all lines in between are part of the comment. The only thing you can't type in the body of a comment is */
, because that is interpreted as the end marker. For example, the following are okay:
/* This is a comment */
/* And so…
… is this! */
The following, however, causes problems:
/* Comments often end with "*/" characters */
Here, the end of the comment (the characters after "*/"
) will be interpreted as C# code, and errors will occur.
The other commenting approach involves starting a comment with //
. After that, you can write whatever you like — as long as you keep to one line! The following is okay:
// This is a different sort of comment.
The following fails, however, because the second line is interpreted as C# code:
// So is this,
but this bit isn't.
This sort of commenting is useful to document statements because both can be placed on a single line:
<A statement>; // Explanation of statement
It was stated earlier that there are two ways of commenting C# code, but there is a third type of comment in C# — although strictly speaking this is an extension of the //
syntax. You can use single-line comments that start with three /
symbols instead of two, like this:
/// A special comment
Under normal circumstances, they are ignored by the compiler — just like other comments — but you can configure Visual Studio to extract the text after these comments and create a specially formatted text file when a project is compiled. You can then use it to create documentation. In order for this documentation to be created, the comments must follow the rules of XML documentation as described here https://msdn.microsoft.com/library/aa288481.aspx — a subject not covered in this book but one that is well worth learning about if you have some spare time.
A very important point about C# code is that it is case sensitive. Unlike some other languages, you must enter code using exactly the right case, because using an uppercase letter instead of a lowercase one will prevent a project from compiling. For example, consider the following line of code, taken from Chapter 2:
Console.WriteLine("The first app in Beginning C# Programming!");
This code is understood by the C# compiler, as the case of the Console.WriteLine()
command is correct. However, none of the following lines of code work:
console.WriteLine("The first app in Beginning C# Programming!");
CONSOLE.WRITELINE("The first app in Beginning C# Programming!");
Console.Writeline("The first app in Beginning C# Programming!");
Here, the case used is wrong, so the C# compiler won't know what you want. Luckily, as you will soon discover, Visual Studio is very helpful when it comes to entering code, and most of the time it knows (as much as a program can know) what you are trying to do. As you type, it suggests commands that you might like to use, and it tries to correct case problems.
Here, you'll take a closer look at the console application example from Chapter 2 (ConsoleApplication1
) and break down the structure a bit. Here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Output text to the screen.
Console.WriteLine("The first app in Beginning C# Programming!");
Console.ReadKey();
}
}
}
You can immediately see that all the syntactic elements discussed in the previous section are present here — semicolons, curly braces, and comments, along with appropriate indentation.
The most important section of code at the moment is the following:
static void Main(string[] args)
{
// Output text to the screen.
Console.WriteLine("The first app in Beginning C# Programming!");
Console.ReadKey();
}
This is the code that is executed when you run your console application. Well, to be more precise, the code block enclosed in curly braces is executed. The comment line doesn't do anything, as mentioned earlier; it's just there for clarity. The other two code lines output some text to the console window and wait for a response, respectively, although the exact mechanisms of this don't need to concern you for now.
Note how to achieve the code outlining functionality shown in the previous chapter, albeit for a Windows application, since it is such a useful feature. You can do this with the #region
and #endregion
keywords, which define the start and end of a region of code that can be expanded and collapsed. For example, you could modify the generated code for ConsoleApplication1
as follows:
#region Using directives
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#endregion
This enables you to collapse this code into a single line and expand it again later should you want to look at the details. The using
statements contained here, and the namespace
statement just underneath, are explained at the end of this chapter.
For now, don't worry about the other code in the example, because the purpose of these first few chapters is to explain basic C# syntax, so the exact method of how the application execution gets to the point where Console.WriteLine()
is called is of no concern. Later, the significance of this additional code is made clear.
As mentioned earlier, variables are concerned with the storage of data. Essentially, you can think of variables in computer memory as boxes sitting on a shelf. You can put things in boxes and take them out again, or you can just look inside a box to see if anything is there. The same goes for variables; you place data in them and can take it out or look at it, as required.
Although all data in a computer is effectively the same thing (a series of 0s and 1s), variables come in different flavors, known as types. Using the box analogy again, boxes come in different shapes and sizes, so some items fit only in certain boxes. The reasoning behind this type system is that different types of data may require different methods of manipulation, and by restricting variables to individual types you can avoid mixing them up. For example, it wouldn't make much sense to treat the series of 0s and 1s that make up a digital picture as an audio file.
To use variables, you have to declare them. This means that you have to assign them a name and a type. After you have declared variables, you can use them as storage units for the type of data that you declared them to hold.
C# syntax for declaring variables merely specifies the type and variable name:
<type> <name>;
If you try to use a variable that hasn't been declared, your code won't compile, but in this case the compiler tells you exactly what the problem is, so this isn't really a disastrous error. Trying to use a variable without assigning it a value also causes an error, but, again, the compiler detects this.
Simple types include types such as numbers and Boolean (true or false) values that make up the fundamental building blocks for your applications. Unlike complex types, simple types cannot have children or attributes. Most of the simple types available are numeric, which at first glance seems a bit strange — surely, you only need one type to store a number?
The reason for the plethora of numeric types is because of the mechanics of storing numbers as a series of 0s and 1s in the memory of a computer. For integer values, you simply take a number of bits (individual digits that can be 0 or 1) and represent your number in binary format. A variable storing N bits enables you to represent any number between 0 and (2N − 1). Any numbers above this value are too big to fit into this variable.
For example, suppose you have a variable that can store two bits. The mapping between integers and the bits representing those integers is therefore as follows:
0 = 00
1 = 01
2 = 10
3 = 11
In order to store more numbers, you need more bits (three bits enable you to store the numbers from 0 to 7, for example).
The inevitable result of this system is that you would need an infinite number of bits to be able to store every imaginable number, which isn't going to fit in your trusty PC. Even if there were a quantity of bits you could use for every number, it surely wouldn't be efficient to use all these bits for a variable that, for example, was required to store only the numbers between 0 and 10 (because storage would be wasted). Four bits would do the job fine here, enabling you to store many more values in this range in the same space of memory.
Instead, a number of different integer types can be used to store various ranges of numbers, which take up differing amounts of memory (up to 64 bits). These types are shown in Table 3.1.
Table 3.1 Integer Types
Type | Alias For | Allowed Values |
sbyte |
System.SByte |
Integer between −128 and 127 |
byte |
System.Byte |
Integer between 0 and 255 |
short |
System.Int16 |
Integer between −32768 and 32767 |
ushort |
System.UInt16 |
Integer between 0 and 65535 |
int |
System.Int32 |
Integer between −2147483648 and 2147483647 |
uint |
System.UInt32 |
Integer between 0 and 4294967295 |
long |
System.Int64 |
Integer between −9223372036854775808 and 9223372036854775807 |
ulong |
System.UInt64 |
Integer between 0 and 18446744073709551615 |
The u
characters before some variable names are shorthand for unsigned, meaning that you can't store negative numbers in variables of those types, as shown in the Allowed Values column of the preceding table.
Of course, you also need to store floating-point values, those that aren't whole numbers. You can use three floating-point variable types: float
, double
, and decimal
. The first two store floating points in the form 6m
× 2
e, where the allowed values for m
and e
differ for each type. decimal
uses the alternative form 6m
× 10
e. These three types are shown in Table 3.2, along with their allowed values of m
and e
, and these limits in real numeric terms.
Table 3.2 Floating-point Types
Type | Alias For | Min M | Max M | Min E | Max E | Approx Min Value | Approx Max Value |
float |
System.Single |
0 | 224 | −149 | 104 | 1.5 × 10−45 | 3.4 × 1038 |
double |
System.Double |
0 | 253 | −1075 | 970 | 5.0 × 10−324 | 1.7 × 10308 |
decimal |
System.Decimal |
0 | 296 | −28 | 0 | 1.0 × 10−28 | 7.9 × 1028 |
In addition to numeric types, three other simple types are available (see Table 3.3).
Table 3.3 Text and Boolean Types
Type | Alias For | Allowed Values |
char |
System.Char |
Single Unicode character, stored as an integer between 0 and 65535 |
bool |
System.Boolean |
Boolean value, true or false |
string |
System.String |
A sequence of characters |
Note that there is no upper limit on the amount of characters making up a string
, because it can use varying amounts of memory.
The Boolean type bool
is one of the most commonly used variable types in C#, and indeed similar types are equally prolific in code in other languages. Having a variable that can be either true
or false
has important ramifications when it comes to the flow of logic in an application. As a simple example, consider how many questions can be answered with true or false (or yes and no). Performing comparisons between variable values or validating input are just two of the programmatic uses of Boolean variables that you will examine very soon.
Now that you've seen these types, consider a short example that declares and uses them. In the following Try It Out you use some simple code that declares two variables, assigns them values, and then outputs these values.
As mentioned in the previous section, you can't just choose any sequence of characters as a variable name. This isn't as worrying as it might sound, however, because you're still left with a very flexible naming system.
The basic variable naming rules are as follows:
_
), or the at symbol (@
).There are also certain keywords that have a specialized meaning to the C# compiler, such as the using
and namespace
keywords shown earlier. If you use one of these by mistake, the compiler complains, however, so don't worry about it.
For example, the following variable names are fine:
myBigVar
VAR1
_test
These are not, however:
99BottlesOfBeer
namespace
It's-All-Over
The previous Try It Out showed two examples of literal values: an integer (17
) and a string (""myInteger" is"
). The other variable types also have associated literal values, as shown in Table 3.4. Many of these involve suffixes, whereby you add a sequence of characters to the end of the literal value to specify the type desired. Some literals have multiple types, determined at compile time by the compiler based on their context (also shown in Table 3.4).
Table 3.4 Literal Values
Type(s) | Category | Suffix | Example/Allowed Values |
bool |
Boolean | None | True or false |
int , uint , long , ulong |
Integer | None | 100 |
uint , ulong |
Integer | u or U |
100U |
long , ulong |
Integer | l or L |
100L |
ulong |
Integer | ul , uL , Ul , UL , lu , lU , Lu , or LU |
100UL |
float |
Real | f or F |
1.5F |
double |
Real | None, d , or D |
1.5 |
decimal |
Real | m or M |
1.5M |
char |
Character | None | 'a' , or escape sequence |
string |
String | None | "a…a" , may include escape sequences |
Earlier in the chapter, you saw a few of the escape sequences you can use in string
literals. Table 3.5 lists these for reference purposes.
Table 3.5 Escape Sequences for String Literals
Escape Sequence | Character Produced | Unicode Value of Character |
' |
Single quotation mark | 0x0027 |
" |
Double quotation mark | 0x0022 |
\ |
Backslash | 0x005C |
|