C# 6 and .NET Core 1.0:Modern Cross:Platform Development
上QQ阅读APP看书,第一时间看更新

Declaring variables

All applications process data. Data comes in, data is processed, and data goes out.

Data usually comes into our program from files, databases, or user input. Data can be put temporarily in variables that will be stored in the memory of the running program. When the program ends, the data in memory is lost. Data is usually output to files and databases or to the screen or a printer.

When using variables, you should think about, first, how much space it takes in memory, and, next, how fast it can be processed.

We control this by picking an appropriate type. You can think of simple common types such as int and double as being differently sized storage boxes. A smaller box would take less memory but may not be processed as quickly.

Naming variables

There are naming conventions for variables and it is best practice to follow them, as shown in the following table:

The following code block shows an example of declaring and initializing a local variable. Note that you can output the name of a variable using a keyword introduced in C# 6, that is, nameof:

double heightInMetres = 1.88;
Console.WriteLine($"The variable {nameof(heightInMetres)} has the value {heightInMetres}.");

Storing text

For text, a single letter such as A is stored as a char type and is assigned using single-quotes around the literal value.

char letter = 'A';

Multiple characters like Bob are stored as a string type and are assigned using double quotes around the literal value:

string name = "Bob";

Storing numbers

Numbers are data that we want to perform an arithmetic calculation on (for example, multiplying).

Tip

A telephone number is not really a number. To decide whether a variable needs to be stored as a number or not, ask yourself whether you need to multiply two telephone numbers together or whether the number includes special characters such as (414)-555-1234. In these cases, the number is really a sequence of characters so should be stored as a string.

Numbers can be natural numbers, such as 42, used for counting (also called whole numbers), they can also be negative numbers, such as -42 (called integers), or they can be real numbers, such as 3.9 (with a fractional part), which are called single or double-precision floating point numbers in computing.

You might know that computers store everything as bits. A bit is either 0 or 1. This is called a binary number system. Humans use a decimal number system.

Storing whole numbers

The following table shows how computers store the number 10. Note the 1 bits in the 8 and the 2 columns; 8 + 2 = 10.

So, 10 in decimal is 00001010 in binary.

Computers can always exactly represent integers (positive and negative whole numbers) using the int type or one of its sibling types such as short.

Storing real numbers

Computers cannot always exactly represent floating point numbers. The float and double types store real numbers using single and double precision floating points.

The following table shows how a computer stores the number 12.75. Note the 1 bits in the 8, 4, ½, and ¼ columns.

8 + 4 + ½ + ¼ = 12¾ = 12.75.

So, 12.75 in decimal is 00001100.1100 in binary.

As you can see, the number 12.75 can be exactly represented using bits. But some numbers can't, as you will see shortly.

Sizes of numbers in memory

In Visual Studio, click on File | Add | New Project…. In the Add New Project dialog, in the Installed Templates list, select Visual C#. In the list at the center, select Console Application, type the name Ch02_Numbers, and then click on OK.

In the Solution Explorer window, right-click on the solution and select Properties or press Alt + Enter. For Startup Project, select Current selection. From now on you can simply click on a project in the Solution Explorer and then press Ctrl + F5 to save, compile, and run that project.

Type the following code inside the Main method:

Console.WriteLine($"int uses {sizeof(int)} bytes and can store numbers in the range {int.MinValue:N0} to {int.MaxValue:N0}.");
Console.WriteLine($"double uses {sizeof(double)} bytes and can store numbers in the range {double.MinValue:N0} to {double.MaxValue:N0}.");
Console.WriteLine($"decimal uses {sizeof(decimal)} bytes and can store numbers in the range {decimal.MinValue:N0} to {decimal.MaxValue:N0}.");

Press Ctrl + F5 and view the output in the console:

int uses 4 bytes and can store numbers in the range -2,147,483,648 to 2,147,483,647.
double uses 8 bytes and can store numbers in the range -179,769,313,486,232,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 to 179,769,313,486,232,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000.
decimal uses 16 bytes and can store numbers in the range -79,228,162,514,264,337,593,543,950,335 to 79,228,162,514,264,337,593,543,950,335.

Note that an int variable uses four bytes of memory and can store positive or negative numbers up to about 2 billion.

A double variable uses eight bytes of memory and can store much bigger values! A decimal variable uses 16 bytes of memory and can store big numbers, but not as big as a double.

Why might a double variable be able to store bigger numbers than a decimal variable yet use half the space in memory? Let's find out!

Comparing double and decimal

In Visual Studio, click on File | Add | New Project…. In the Add New Project dialog, in the Installed Templates list, select Visual C#. In the list at the center, select Console Application, type the name Ch02_NumberAccuracy, and then click on OK.

Enter the following code. Do not worry about understanding the syntax right now, although it isn't too hard to follow:

double a = 0.1;
double b = 0.2;
if (a + b == 0.3)
{
    Console.WriteLine($"{a} + {b} equals 0.3");
}
else
{
    Console.WriteLine($"{a} + {b} does NOT equal 0.3");
}

Press Ctrl + F5 and view the output in the console:

0.1 + 0.2 does NOT equal 0.3

The double type is NOT guaranteed to be accurate. Only use double when accuracy, especially when comparing two numbers, is not important, for example, when measuring a person's height.

The problem with the preceding code is how the computer stores the number 0.1 or multiples of 0.1. To represent 0.1 in binary, the computer stores 1 in the 1/16 column, 1 in the 1/128 column, 1 in the 1/1024 column, and so on. The number 0.1 in decimal is 0.0001001001001 repeating forever:

Tip

Never compare double values using ==. During the First Gulf War, an American patriot missile battery used double values in its calculations. The inaccuracy caused it to fail to track and intercept an incoming Iraqi Scud missile, and 28 soldiers were killed, as you can read about at this link: https://www.ima.umn.edu/~arnold/disasters/patriot.html.

Copy and paste the code you wrote before that used doubles and then modify it to look like the following code:

decimal c = 0.1M; // M indicates a decimal literal value
decimal d = 0.2M;
if (c + d == 0.3M)
{
    Console.WriteLine($"{c} + {d} equals 0.3");
}
else
{
    Console.WriteLine($"{c} + {d} does NOT equal 0.3");
}

Press Ctrl + F5 and view the output in the console:

0.1 + 0.2 equals 0.3

The decimal type is accurate because it actually stores the number as a large integer and shifts the decimal point. For example, 0.1 is stored as 1 with a note to shift the decimal point one place to the left. 12.75 is stored as 1275 with a note to shift the decimal point two places to the left.

Tip

Best Practice

Use int for whole numbers and double for real numbers. Use decimal for money, CAD drawings, general engineering, and wherever accuracy of a real number is important.

The double type has some useful special values: double.NaN means not-a-number and double.Infinity means an infinitely large value. You can use these special values when comparing the value of double variables.

Storing Booleans

Booleans (bool) can only contain one of the two values: true or false, as shown in the following code. They are most commonly used to branch and loop, as you will see in Chapter 3, Controlling the Flow, Converting Types, and Handling Exceptions:

bool happy = true;
bool sad = false;

The object type

There is a special type named object that can store any type of data, but its flexibility comes at the cost of messier code and poor performance due to boxing and unboxing operations when storing a value type. You should avoid it whenever possible.

Add a new Console Application project named Ch02_SpecialTypes and add the following code to the Main method:

object height = 1.88; // storing a double in an object
object name = "Amir"; // storing a string in an object
int length1 = name.Length; // gives compile error!
int length2 = ((string)name).Length; // cast to access members

The object type has been available since the first version of C# but C# 2 and higher versions have better alternatives, which we will cover later, that provide the flexibility we want without the performance overhead.

The dynamic type

There is another special type named dynamic that can also store any type of data, and, like object, its flexibility comes at the cost of performance. Unlike object, the value stored in the variable can have its members invoked without an explicit cast, as shown in the following code:

dynamic anotherName = "Ahmed"; // storing a string in a dynamic object
int length = anotherName.Length; // this compiles but might throw an exception at run-time!

The limitation of dynamic is that Visual Studio cannot show IntelliSense to help you write the code because the compiler doesn't check at build time. Instead, the CLR checks for the member at runtime. The dynamic keyword was introduced in C# 4.

Local variables

Local variables are declared inside methods and they only exist during the call to that method. Once the method returns, the memory allocated to any local variables is released.

Add a new Console Application project named Ch02_Variables. Enter the following code to declare and assign values to some local variables inside the Main method. Note that we specify the type before the name of each variable:

int population = 66000000;
double weight = 1.88; // in kilograms
decimal price = 4.99M; // in pounds sterling
string fruit = "Apples"; // strings use double-quotes
char letter = 'Z'; // chars use single-quotes
Tip

Visual Studio 2015 will show green squiggles under each of the variable names to warn you that the variable is assigned but its value is never used.

Inferring the type of a local variable

You can use the var keyword to declare local variables. The compiler will infer the type from the literal value you assign after the assignment = operator.

A literal number without a decimal point is inferred as an int variable unless you add the L suffix, in which case it infers a long variable. A literal number with a decimal point is inferred as a double unless you add the M suffix, in which case it infers a decimal variable, or the F suffix, in which case it infers a float variable. Double quotes indicate a string variable, single quotes indicate a char, and the true and false values indicates a bool.

Modify your code to use var:

var population = 66000000;
var weight = 1.88; // in kilograms
var price = 4.99M; // in pounds sterling
var fruit = "Apples"; // strings use double-quotes
var letter = 'Z'; // chars use single-quotes
var happy = true;
Tip

Although using var is convenient, some developers avoid using it to make it easier for a code reader to understand the types in use in the code. Personally, I use it whenever the type is obvious. For example, the first statement is just as clear as the second in stating what the type of the xml variable is but is shorter than the second statement:

var xml = new XmlDocument();
XmlDocument xml = new XmlDocument();

Making a value type nullable

Most of the primitive types except string are value types. This means they must have a value. You can determine the default value of a type using the default() operator. The default value of an int variable is 0 (zero):

int defaultValueOfInt = default(int); // 0

Strings are reference types. This means that they can have a null value. The null value is a special value that indicates that the variable does not reference anything (yet).

Sometimes it is convenient to allow a value type to be null. You can do this by adding a question mark as a suffix to the type when declaring a variable, as shown in the following code:

int ICannotBeNull = 4;
ICannotBeNull = default(int); // 0
int? ICouldBeNull = null;
int result1 = ICouldBeNull.GetValueOrDefault(); // 0
ICouldBeNull = 4;
int result2 = ICouldBeNull.GetValueOrDefault(); // 4 

Storing multiple values in an array

When you need to store multiple values of the same type, you can declare an array. For example, you might need to store four names in a string array.

Add the following lines of code to the end of the Main method. It declares an array for storing four strings. Then, it stores strings at index positions 0 to 3 (note that arrays count from zero, so the last item is one less than the length of the array). Finally, it loops through each item in the array using a for statement that we cover in more detail in Chapter 3, Controlling the Flow, Converting Types, and Handling Exceptions:

// declaring the size of the array
string[] names = new string[4];
// storing items at index positions
names[0] = "Kate";
names[1] = "Jack";
names[2] = "Rebecca";
names[3] = "Tom";
for (int i = 0; i < names.Length; i++)
{
    Console.WriteLine(names[i]); // read the item at this index
}
Tip

Arrays are always of a fixed size, so you need to decide how many items you want to store before instantiating them. Arrays are useful for temporarily storing multiple items, but collections are more flexible when adding and removing items dynamically. We cover collections in Chapter 4, Using Common .NET Types.