end 020
This commit is contained in:
parent
34afc69172
commit
6b92b75b34
@ -407,3 +407,10 @@ data that filled gaps in the ourAnimals array.
|
|||||||
Your ability to implement features of the Contoso Pets application based on a
|
Your ability to implement features of the Contoso Pets application based on a
|
||||||
design specification demonstrates your understanding of the iteration and
|
design specification demonstrates your understanding of the iteration and
|
||||||
selection statements.
|
selection statements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Project Files
|
||||||
|
|
||||||
|
- [Final](./ChallengeProject/Final/Program.cs)
|
||||||
|
- [Own](./ChallengeProject/Own/Program.cs)
|
||||||
|
645
020_data_types/020_csharp.md
Normal file
645
020_data_types/020_csharp.md
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
# Choose the correct data type in your C# code
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The C# programming language relies on data types extensively. Data types
|
||||||
|
restrict the kinds of values that can be stored in a given variable, which can
|
||||||
|
be helpful when trying to create error free code. As a developer, you
|
||||||
|
confidently perform operations on your variables because you know in advance
|
||||||
|
that it only stores valid values.
|
||||||
|
|
||||||
|
Suppose your job is to build a new application that must retrieve, manipulate,
|
||||||
|
and store many different kinds of data, including individual numeric values and
|
||||||
|
sequences of numeric and text values. Choosing the right data types is critical
|
||||||
|
to the success of your software development efforts. But what are your options,
|
||||||
|
and what criteria should you use when faced with several data types that seem
|
||||||
|
similar?
|
||||||
|
|
||||||
|
In this module, you learn how your application stores and processes data. You
|
||||||
|
learn that there are two kinds of data types that correspond with the two ways
|
||||||
|
that data is processed. You write code that identifies the maximum and minimum
|
||||||
|
values that can be stored in a particular numeric data type. And, you learn the
|
||||||
|
criteria to use when choosing between several numeric data types for your
|
||||||
|
application.
|
||||||
|
|
||||||
|
By the end of this module, you'll be confident when working with different data
|
||||||
|
types in C# and able to choose the right data type for your particular application.
|
||||||
|
|
||||||
|
### Learning objectives
|
||||||
|
|
||||||
|
- Learn the fundamental differences between value types and reference types.
|
||||||
|
- Describe the properties of many new numeric data types, including new
|
||||||
|
integral types and floating point types.
|
||||||
|
- Write code that returns the maximum and minimum values that numeric data
|
||||||
|
types can store.
|
||||||
|
- Use the new keyword to create new instances of a reference type.
|
||||||
|
- Determine which data type you should choose for a given application.
|
||||||
|
|
||||||
|
## Discover value types and reference types
|
||||||
|
|
||||||
|
With many data types available in C#, choosing the right one to use means that
|
||||||
|
you need to understand when you might choose one data type over another.
|
||||||
|
|
||||||
|
Before discussing why you might choose one data type over another, you need to
|
||||||
|
understand more about data types. You also need to know how data and data types
|
||||||
|
work in C# and .NET.
|
||||||
|
|
||||||
|
### What is data?
|
||||||
|
|
||||||
|
Answering the question "what is data" depends on who you ask, and in what
|
||||||
|
context you're asking it.
|
||||||
|
|
||||||
|
In software development, data is essentially a value that is stored in the
|
||||||
|
computer's memory as a series of bits. A bit is a simple binary switch
|
||||||
|
represented as a `0` or `1`, or rather, "off" and "on." A single bit doesn't
|
||||||
|
seem useful, however when you combine 8 bits together in a sequence, they form
|
||||||
|
a byte. When used in a byte, each bit takes on a meaning in the sequence. In
|
||||||
|
fact, you can represent 256 different combinations with just 8 bits if you use
|
||||||
|
a binary (base-2) numeral system.
|
||||||
|
|
||||||
|
For example, in a binary numeral system, you can represent the number `195` as
|
||||||
|
`11000011`. The following table helps you visualize how this works. The first
|
||||||
|
row has eight columns that correspond to a position in a byte. Each position
|
||||||
|
represents a different numeric value. The second row can store the value of an
|
||||||
|
individual bit, either 0 or 1.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
| | | | | | | | |
|
||||||
|
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||||
|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|
||||||
|
| 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
If you add up the number from each column in the first row that corresponds to
|
||||||
|
a `1` in the second row, you get the decimal equivalent to the binary numeral
|
||||||
|
system representation. In this case, it would be `128 + 64 + 2 + 1 = 195`.
|
||||||
|
|
||||||
|
To work with larger values beyond `255`, your computer stores more bytes
|
||||||
|
(commonly 32-bit or 64-bit). If you're working with millions of large numbers
|
||||||
|
in a scientific setting, you may need to consider more carefully which data
|
||||||
|
types you're using. Your code could require more memory to run.
|
||||||
|
|
||||||
|
#### What about textual data?
|
||||||
|
|
||||||
|
If a computer only understands `0s` and `1s`, then how does it allow you to work
|
||||||
|
with text? Using a system like ASCII (American Standard Code for Information
|
||||||
|
Interchange), you can use a single byte to represent upper and lowercase
|
||||||
|
letters, numbers, tab, backspace, newline and many mathematical symbols.
|
||||||
|
|
||||||
|
For example, if you wanted to store a lower-case letter `a` as a value in my
|
||||||
|
application, the computer would only understand the binary form of that value.
|
||||||
|
To better understand how a lower-case letter `a` is handled by the computer, I
|
||||||
|
need to locate an ASCII table that provides ASCII values and their decimal
|
||||||
|
equivalents. You can search for the terms "ASCII lookup decimal" to locate such
|
||||||
|
a resource online.
|
||||||
|
|
||||||
|
In this case, the lower-case letter `a` is equivalent to the decimal value `97`.
|
||||||
|
Then, you would use the same binary numeral system in reverse to find how an
|
||||||
|
ASCII letter `a` is stored by the computer.
|
||||||
|
|
||||||
|

|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
| | | | | | | | |
|
||||||
|
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||||
|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|
||||||
|
| 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Since `64 + 32 + 1 = 97`, the 8-bit binary ASCII code for `a` is `01100001`.
|
||||||
|
|
||||||
|
It's likely that you'll never need to perform these types of conversions on
|
||||||
|
your own, but understanding the computer's perspective of data is a
|
||||||
|
foundational concept, especially as you're considering data types.
|
||||||
|
|
||||||
|
#### What is a data type?
|
||||||
|
|
||||||
|
A data type is a way a programming language defines how much memory to save for
|
||||||
|
a value. There are many data types in the C# language to be used for many
|
||||||
|
different applications and sizes of data.
|
||||||
|
|
||||||
|
For most of the applications you build in your career, you'll settle on a small
|
||||||
|
subset of all the available data types. However, it's still vital to know
|
||||||
|
others exist and why.
|
||||||
|
|
||||||
|
#### Value vs. reference types
|
||||||
|
|
||||||
|
This module focuses on the two kinds of types in C#: reference types and value
|
||||||
|
types.
|
||||||
|
|
||||||
|
Variables of reference types store references to their data (objects), that is
|
||||||
|
they point to data values stored somewhere else. In comparison, variables of
|
||||||
|
value types directly contain their data. As you learn more about C#, new
|
||||||
|
details emerge related to the fundamental difference between value and
|
||||||
|
reference types.
|
||||||
|
|
||||||
|
#### Simple value types
|
||||||
|
|
||||||
|
Simple value types are a set of predefined types provided by C# as keywords.
|
||||||
|
These keywords are aliases (a nickname) for predefined types defined in the
|
||||||
|
.NET Class Library. For example, the C# keyword `int` is an alias of a value
|
||||||
|
type defined in the .NET Class Library as `System.Int32`.
|
||||||
|
|
||||||
|
Simple value types include many of the data types that you may have used
|
||||||
|
already like `char` and `bool`. There are also many integral and floating-point
|
||||||
|
value types to represent a wide range of whole and fractional numbers.
|
||||||
|
|
||||||
|
### Recap
|
||||||
|
|
||||||
|
Values are stored as bits, which are simple on / off switches. Combining enough
|
||||||
|
of these switches allows you to store just about any possible value.
|
||||||
|
There are two basic categories of data types: value and reference types. The
|
||||||
|
difference is in how and where the values are stored by the computer as your
|
||||||
|
program executes.
|
||||||
|
Simple value types use a keyword alias to represent formal names of types in
|
||||||
|
the .NET Library.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exercise
|
||||||
|
|
||||||
|
### Discover integral types
|
||||||
|
|
||||||
|
In this exercise, you work with integral types. An integral type is a simple
|
||||||
|
value type that represents whole numbers with no fraction (such as `-1`, `0`,
|
||||||
|
`1`, `2`, `3`). The most popular in this category is the `int` data type.
|
||||||
|
|
||||||
|
There are two subcategories of integral types: **signed** and **unsigned**
|
||||||
|
integral types.
|
||||||
|
|
||||||
|
A signed type uses its bytes to represent an equal number of positive and
|
||||||
|
negative numbers. The following exercise gives you exposure to the signed
|
||||||
|
integral types in C#.
|
||||||
|
|
||||||
|
#### Prepare your coding environment
|
||||||
|
|
||||||
|
You're using this C# console project to create, build, and run code samples
|
||||||
|
during this module.
|
||||||
|
|
||||||
|
#### Use the MinValue and MaxValue properties for each signed integral type
|
||||||
|
|
||||||
|
Program.cs should be empty. If it isn't, select and delete all code lines.
|
||||||
|
|
||||||
|
To see the value ranges for the various data types, type the following code
|
||||||
|
into the code editor.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
Console.WriteLine("Signed integral types:");
|
||||||
|
|
||||||
|
Console.WriteLine($"sbyte : {sbyte.MinValue} to {sbyte.MaxValue}");
|
||||||
|
Console.WriteLine($"short : {short.MinValue} to {short.MaxValue}");
|
||||||
|
Console.WriteLine($"int : {int.MinValue} to {int.MaxValue}");
|
||||||
|
Console.WriteLine($"long : {long.MinValue} to {long.MaxValue}");
|
||||||
|
```
|
||||||
|
|
||||||
|
The Program.cs file must be saved before building or running the code.
|
||||||
|
|
||||||
|
At the Terminal command prompt, to run your code, type `dotnet run` and then
|
||||||
|
press Enter.
|
||||||
|
|
||||||
|
You should see the following output:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Signed integral types:
|
||||||
|
sbyte : -128 to 127
|
||||||
|
short : -32768 to 32767
|
||||||
|
int : -2147483648 to 2147483647
|
||||||
|
long : -9223372036854775808 to 9223372036854775807
|
||||||
|
```
|
||||||
|
|
||||||
|
For most non-scientific applications, you likely only need to work with int.
|
||||||
|
Most of the time, you won't need more than a positive to negative 2.14 billion
|
||||||
|
whole numbers.
|
||||||
|
|
||||||
|
### Unsigned integral types
|
||||||
|
|
||||||
|
An unsigned type uses its bytes to represent only positive numbers. The
|
||||||
|
remainder of the exercise introduces the unsigned integral types in C#.
|
||||||
|
|
||||||
|
#### Use the MinValue and MaxValue properties for each unsigned integral type
|
||||||
|
|
||||||
|
Below the previous code passage, add the following code:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
Console.WriteLine("");
|
||||||
|
Console.WriteLine("Unsigned integral types:");
|
||||||
|
|
||||||
|
Console.WriteLine($"byte : {byte.MinValue} to {byte.MaxValue}");
|
||||||
|
Console.WriteLine($"ushort : {ushort.MinValue} to {ushort.MaxValue}");
|
||||||
|
Console.WriteLine($"uint : {uint.MinValue} to {uint.MaxValue}");
|
||||||
|
Console.WriteLine($"ulong : {ulong.MinValue} to {ulong.MaxValue}");
|
||||||
|
```
|
||||||
|
|
||||||
|
Save your code file, and then run your code.
|
||||||
|
|
||||||
|
You should see the following output:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Signed integral types:
|
||||||
|
sbyte : -128 to 127
|
||||||
|
short : -32768 to 32767
|
||||||
|
int : -2147483648 to 2147483647
|
||||||
|
long : -9223372036854775808 to 9223372036854775807
|
||||||
|
|
||||||
|
Unsigned integral types:
|
||||||
|
byte : 0 to 255
|
||||||
|
ushort : 0 to 65535
|
||||||
|
uint : 0 to 4294967295
|
||||||
|
ulong : 0 to 18446744073709551615
|
||||||
|
```
|
||||||
|
|
||||||
|
While a given data type can be used for many cases, given the fact that the
|
||||||
|
`byte` data type can represent a value from 0 to 255, it's obvious that this is
|
||||||
|
intended to hold a value that represents a byte of data. Data stored in files
|
||||||
|
or data transferred across the internet is often in a binary format. When
|
||||||
|
working with data from these external sources, you need to receive data as an
|
||||||
|
array of bytes, then convert them into strings. Many of the methods in the .NET
|
||||||
|
Class Library that deal with encoding and decoding data requires you handle byte
|
||||||
|
arrays.
|
||||||
|
|
||||||
|
### Recap
|
||||||
|
|
||||||
|
- An integral type is a simple value data type that can hold whole numbers.
|
||||||
|
- There are signed and unsigned numeric data types. Signed integral types use 1
|
||||||
|
bit to store whether the value is positive or negative.
|
||||||
|
- You can use the `MaxValue` and `MinValue` properties of numeric data types to
|
||||||
|
evaluate whether a number can fit in a given data type.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exercise
|
||||||
|
|
||||||
|
### Discover floating-point types
|
||||||
|
|
||||||
|
In this exercise, you work with floating-point data types to learn about the
|
||||||
|
nuanced differences between each data type.
|
||||||
|
|
||||||
|
A floating point is a simple value type that represents numbers to the right of
|
||||||
|
the decimal place. Unlike integral numbers, there are other considerations
|
||||||
|
beyond the maximum and minimum values you can store in a given floating-point
|
||||||
|
type.
|
||||||
|
|
||||||
|
#### Evaluate floating-point types
|
||||||
|
|
||||||
|
First, you must consider the digits of precision each type allows. Precision is
|
||||||
|
the number of value places stored after the decimal point.
|
||||||
|
|
||||||
|
Second, you must consider the manner in which the values are stored and the
|
||||||
|
impact on the accuracy of the value. For example, `float` and `double` values are
|
||||||
|
stored internally in a binary (base 2) format, while `decimal` is stored in a
|
||||||
|
decimal (base 10) format. Why does this matter?
|
||||||
|
|
||||||
|
Performing math on binary floating-point values can produce results that may
|
||||||
|
surprise you if you're used to decimal (base 10) math. Often, binary
|
||||||
|
floating-point math is an approximation of the real value. Therefore, `float` and
|
||||||
|
`double` are useful because large numbers can be stored using a small memory
|
||||||
|
footprint. However, `float` and `double` should only be used when an
|
||||||
|
approximation is useful. For example, being a few thousandths off when
|
||||||
|
calculating the splatter of a snowball in a video game is close enough.
|
||||||
|
|
||||||
|
When you need more a more precise answer, you should use `decimal`. Each value
|
||||||
|
of type `decimal` has a relatively large memory footprint, however performing
|
||||||
|
math operations gives you a more precise result. So, you should use decimal
|
||||||
|
when working with financial data or any scenario where you need an accurate
|
||||||
|
result from a calculation.
|
||||||
|
|
||||||
|
#### Use the MinValue and MaxValue properties for each signed float type
|
||||||
|
|
||||||
|
- Delete or use the line comment operator `//` to comment out all of the code from
|
||||||
|
the previous exercises.
|
||||||
|
|
||||||
|
- To see the value ranges for the various data types, update your code in the
|
||||||
|
editor as follows:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
Console.WriteLine("");
|
||||||
|
Console.WriteLine("Floating point types:");
|
||||||
|
Console.WriteLine($"float : {float.MinValue} to {float.MaxValue} (with ~6-9 digits of precision)");
|
||||||
|
Console.WriteLine($"double : {double.MinValue} to {double.MaxValue} (with ~15-17 digits of precision)");
|
||||||
|
Console.WriteLine($"decimal: {decimal.MinValue} to {decimal.MaxValue} (with 28-29 digits of precision)");
|
||||||
|
```
|
||||||
|
|
||||||
|
The Program.cs file must be saved before building or running the code.
|
||||||
|
|
||||||
|
At the Terminal command prompt, to run your code, type `dotnet run` and then
|
||||||
|
press Enter.
|
||||||
|
|
||||||
|
You should see the following output:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Floating point types:
|
||||||
|
float : -3.402823E+38 to 3.402823E+38 (with ~6-9 digits of precision)
|
||||||
|
double : -1.79769313486232E+308 to 1.79769313486232E+308 (with ~15-17 digits of precision)
|
||||||
|
decimal: -79228162514264337593543950335 to 79228162514264337593543950335 (with 28-29 digits of precision)
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, `float` and `double` use a different notation than is used by
|
||||||
|
`decimal` to represent its largest and smallest possible values. But what does
|
||||||
|
this notation mean?
|
||||||
|
|
||||||
|
#### Deciphering large floating-point values
|
||||||
|
|
||||||
|
Because floating-point types can hold large numbers with precision, their
|
||||||
|
values can be represented using "E notation", which is a form of scientific
|
||||||
|
notation that means "times 10 raised to the power of." So, a value like `5E+2`
|
||||||
|
would be the value 500 because it's the equivalent of `5 x 10^2`, or `5 x 10²`.
|
||||||
|
|
||||||
|
### Recap
|
||||||
|
|
||||||
|
- A floating-point type is a simple value data type that can hold fractional
|
||||||
|
numbers.
|
||||||
|
- Choosing the right floating-point type for your application requires you to
|
||||||
|
consider more than just the maximum and minimum values that it can hold. You
|
||||||
|
must also consider how many values can be preserved after the decimal, how the
|
||||||
|
numbers are stored, and how their internal storage affects the outcome of math
|
||||||
|
operations.
|
||||||
|
- Floating-point values can sometimes be represented using "E notation" when
|
||||||
|
the numbers grow especially large.
|
||||||
|
- There's a fundamental difference in how the compiler and runtime handle
|
||||||
|
decimal as opposed to float or double, especially when determining how much
|
||||||
|
accuracy is necessary from math operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exercise - Discover reference types
|
||||||
|
|
||||||
|
Reference types include arrays, classes, and strings. Reference types are
|
||||||
|
treated differently from value types regarding the way values are stored when
|
||||||
|
the application is executing.
|
||||||
|
|
||||||
|
In this exercise, you learn how reference types are different from value types,
|
||||||
|
and how to use the `new` operator to associate a variable with a value in the
|
||||||
|
computer's memory.
|
||||||
|
|
||||||
|
### How reference types are different from value types
|
||||||
|
|
||||||
|
A value type variable stores its values directly in an area of storage called
|
||||||
|
the stack. The stack is memory allocated to the code that is currently running
|
||||||
|
on the CPU (also known as the stack frame, or activation frame). When the stack
|
||||||
|
frame has finished executing, the values in the stack are removed.
|
||||||
|
|
||||||
|
A reference type variable stores its values in a separate memory region called
|
||||||
|
the heap. The heap is a memory area that is shared across many applications
|
||||||
|
running on the operating system at the same time. The .NET Runtime communicates
|
||||||
|
with the operating system to determine what memory addresses are available, and
|
||||||
|
requests an address where it can store the value. The .NET Runtime stores the
|
||||||
|
value, and then returns the memory address to the variable. When your code uses
|
||||||
|
the variable, the .NET Runtime seamlessly looks up the address stored in the
|
||||||
|
variable, and retrieves the value that's stored there.
|
||||||
|
|
||||||
|
You'll next write some code that illustrates these ideas more clearly.
|
||||||
|
|
||||||
|
### Define a reference type variable
|
||||||
|
|
||||||
|
Delete or use the line comment operator `//` to comment out all of the code
|
||||||
|
from the previous exercises.
|
||||||
|
|
||||||
|
Update your code in the editor as follows:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
int[] data;
|
||||||
|
```
|
||||||
|
|
||||||
|
The previous code defines a variable that can hold a value of type int array.
|
||||||
|
|
||||||
|
At this point, `data` is merely a variable that could hold a reference, or
|
||||||
|
rather, a memory address of a value in the heap. Because it's not pointing to a
|
||||||
|
memory address, it's called a null reference.
|
||||||
|
|
||||||
|
Create an instance of `int` array using the `new` keyword
|
||||||
|
|
||||||
|
Update your code in the editor to create and assign a new instance of `int`
|
||||||
|
array, using the following code:
|
||||||
|
|
||||||
|

|
||||||
|
```cs
|
||||||
|
int[] data;
|
||||||
|
data = new int[3];
|
||||||
|
```
|
||||||
|
|
||||||
|
The `new` keyword informs .NET Runtime to create an instance of `int` array,
|
||||||
|
and then coordinate with the operating system to store the array sized for
|
||||||
|
three int values in memory. The .NET Runtime complies, and returns a memory
|
||||||
|
address of the new `int` array. Finally, the memory address is stored in the
|
||||||
|
variable data. The `int` array's elements default to the value `0`, because that
|
||||||
|
is the default value of an `int`.
|
||||||
|
|
||||||
|
Modify the code example to perform both operations in a single line of code
|
||||||
|
|
||||||
|
The two lines of code in the previous step are typically shortened to a single
|
||||||
|
line of code to both declare the variable, and create a new instance of the
|
||||||
|
`int` array. Modify the code from step 3 to the following.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
int[] data = new int[3];
|
||||||
|
```
|
||||||
|
|
||||||
|
While there's no output to observe, hopefully this exercise added clarity to
|
||||||
|
how the C# syntax relates to the steps of the process for working with
|
||||||
|
reference types.
|
||||||
|
|
||||||
|
#### What's different about the C# string data type?
|
||||||
|
|
||||||
|
The `string` data type is also a reference type. You might be wondering why a
|
||||||
|
`new` operator wasn't used when declaring a string. This is purely a convenience
|
||||||
|
afforded by the designers of C#. Because the `string` data type is used so
|
||||||
|
frequently, you can use this format:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
string shortenedString = "Hello World!";
|
||||||
|
Console.WriteLine(shortenedString);
|
||||||
|
```
|
||||||
|
|
||||||
|
Behind the scenes, however, a new instance of `System.String` is created and
|
||||||
|
initialized to "Hello World!".
|
||||||
|
|
||||||
|
#### Practical concerns using value and reference types
|
||||||
|
|
||||||
|
1. **Value Type (int)**: In this example, `val_A` and `val_B` are integer value types.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
int val_A = 2;
|
||||||
|
int val_B = val_A;
|
||||||
|
val_B = 5;
|
||||||
|
|
||||||
|
Console.WriteLine("--Value Types--");
|
||||||
|
Console.WriteLine($"val_A: {val_A}");
|
||||||
|
Console.WriteLine($"val_B: {val_B}");
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see the following output:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
--Value Types--
|
||||||
|
val_A: 2
|
||||||
|
val_B: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
When `val_B` = `val_A` is executed, the value of `val_A` is copied and stored in
|
||||||
|
`val_B`. So, when `val_B` is changed, `val_A` remains unaffected.
|
||||||
|
|
||||||
|
2. **Reference Type (array)**: In this example, `ref_A` and `ref_B` are array
|
||||||
|
reference types.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
int[] ref_A= new int[1];
|
||||||
|
ref_A[0] = 2;
|
||||||
|
int[] ref_B = ref_A;
|
||||||
|
ref_B[0] = 5;
|
||||||
|
|
||||||
|
Console.WriteLine("--Reference Types--");
|
||||||
|
Console.WriteLine($"ref_A[0]: {ref_A[0]}");
|
||||||
|
Console.WriteLine($"ref_B[0]: {ref_B[0]}");
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see the following output:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
--Reference Types--
|
||||||
|
ref_A[0]: 5
|
||||||
|
ref_B[0]: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
When `ref_B` = `ref_A` is executed, `ref_B` points to the same memory
|
||||||
|
location as `ref_A`. So, when `ref_B[0]` is changed, `ref_A[0]` also changes
|
||||||
|
because they both point to the same memory location. This is a key difference
|
||||||
|
between value types and reference types.
|
||||||
|
|
||||||
|
### Recap
|
||||||
|
|
||||||
|
- Value types can hold smaller values and are stored in the stack. Reference
|
||||||
|
types can hold large values, and a new instance of a reference type is created
|
||||||
|
using the `new` operator. Reference type variables hold a reference (the memory
|
||||||
|
address) to the actual value stored in the heap.
|
||||||
|
- Reference types include arrays, strings, and classes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Choose the right data type
|
||||||
|
|
||||||
|
You've been introduced to the difference between value types and reference type
|
||||||
|
s, as well as integral and floating point types.
|
||||||
|
|
||||||
|
Suppose your job is to build a new application that retrieves, manipulates, and
|
||||||
|
stores different types of data. Which data types do you use?
|
||||||
|
|
||||||
|
In some cases, it is an easy choice. For example, when you need to work with
|
||||||
|
text, then you default to using the `string` data type unless you need to
|
||||||
|
perform a significant amount of concatenation.
|
||||||
|
|
||||||
|
But what about working with numeric data? There are 11 different options. How
|
||||||
|
do you choose the right data type?
|
||||||
|
|
||||||
|
### Choose the right data type
|
||||||
|
|
||||||
|
With so many data types to choose from, what criteria should you use to choose
|
||||||
|
the right data type for the particular situation?
|
||||||
|
|
||||||
|
When evaluating your options, you must weigh several important considerations.
|
||||||
|
Often there's no one single correct answer, but some answers are more correct
|
||||||
|
than others.
|
||||||
|
|
||||||
|
#### Choose the data type that meets the boundary value range requirements of your application
|
||||||
|
|
||||||
|
Your choice of a data type can help to set the boundaries for the size of the
|
||||||
|
data you might store in that particular variable. For example, if you know a
|
||||||
|
particular variable should only store a number between 1 and 10,000 otherwise
|
||||||
|
it's outside of the boundaries of what would be expected, you would likely
|
||||||
|
avoid `byte` and `sbyte` since their ranges are too low.
|
||||||
|
|
||||||
|
Furthermore, you would likely not need `int`, `long`, `uint`, and `ulong`
|
||||||
|
because they can store more data than is necessary. Likewise, you would
|
||||||
|
probably skip `float`, `double`, and `decimal` if you didn't need fractional
|
||||||
|
values. You might narrow it down to `short` and `ushort`, of which both may be
|
||||||
|
viable. If you're confident that a negative value would have no meaning in your
|
||||||
|
application, you might choose `ushort` (positive unsigned integer, 0 to 65,535).
|
||||||
|
Now, any value assigned to a variable of type `ushort` outside of the boundary
|
||||||
|
of 0 to 65535 would throw an exception, thereby subtly helping you enforce a
|
||||||
|
degree of sanity checking in your application.
|
||||||
|
|
||||||
|
#### Start with choosing the data type to fit the data (not to optimize performance)
|
||||||
|
|
||||||
|
You may be tempted to choose the data type that uses the fewest bits to store
|
||||||
|
data thinking it improves your application's performance. However, some of the
|
||||||
|
best advice related to application performance (that is, how fast your
|
||||||
|
application runs) is to not "prematurely optimize". You should resist the
|
||||||
|
temptation to guess at the parts of your code, including the selection of data
|
||||||
|
types that may impact your application's performance.
|
||||||
|
|
||||||
|
Many assume that because a given data type stores less information it must use
|
||||||
|
less of the computer's processor and memory than a data type that stores more
|
||||||
|
information. Instead, you should choose the right fit for your data, then later
|
||||||
|
you can empirically measure the performance of your application using special
|
||||||
|
software that provides factual insights to the parts of your application that
|
||||||
|
negatively impact performance.
|
||||||
|
|
||||||
|
#### Choose data types based on the input and output data types of library functions used
|
||||||
|
|
||||||
|
Suppose you want to work with a span of years between two dates. Since the
|
||||||
|
application is a business application, you might determine that you only need a
|
||||||
|
range from about 1960 to 2200. You might think to try to work with byte since
|
||||||
|
it can represent numbers between 0 and 255.
|
||||||
|
|
||||||
|
However, when you look at the built-in methods on the `System.TimeSpan` and
|
||||||
|
`System.DateTime` classes, you realize they mostly accept values of type
|
||||||
|
`double` and `int`. If you choose `sbyte`, you'll constantly be casting back
|
||||||
|
and forth between `sbyte` and `double` or `int`. In this case, it might make
|
||||||
|
more sense to choose `int` if you don't need subsecond precision, and `double`
|
||||||
|
if you do need subsecond precision.
|
||||||
|
|
||||||
|
#### Choose data types based on impact to other systems
|
||||||
|
|
||||||
|
Sometimes, you must consider how the information will be consumed by other
|
||||||
|
applications or other systems like a database. For example, SQL Server's type
|
||||||
|
system is different from C#'s type system. As a result, some mapping between
|
||||||
|
the two must happen before you can save data into that database.
|
||||||
|
|
||||||
|
If your application's purpose is to interface with a database, then you would
|
||||||
|
likely need to consider how the data is stored and how much data is stored. The
|
||||||
|
choice of a larger data type might impact the amount (and cost) of the physical
|
||||||
|
storage required to store all the data your application will generate.
|
||||||
|
|
||||||
|
#### When in doubt, stick with the basics
|
||||||
|
|
||||||
|
While you've looked at several considerations, as you're getting started, for
|
||||||
|
simplicity's sake you should prefer a subset of basic data types, including:
|
||||||
|
|
||||||
|
- `int` for most whole numbers
|
||||||
|
- `decimal` for numbers representing money
|
||||||
|
- `bool` for true or false values
|
||||||
|
- `string` for alphanumeric value
|
||||||
|
|
||||||
|
#### Choose specialty complex types for special situations
|
||||||
|
|
||||||
|
Don't reinvent data types if one or more data type already exists for a given
|
||||||
|
purpose. The following examples identify where a specific .NET data types can
|
||||||
|
be useful:
|
||||||
|
|
||||||
|
- `byte`: working with encoded data that comes from other computer systems or
|
||||||
|
using different character sets.
|
||||||
|
- `double`: working with geometric or scientific purposes. double is used
|
||||||
|
frequently when building games involving motion.
|
||||||
|
- `System.DateTime` for a specific date and time value.
|
||||||
|
- `System.TimeSpan` for a span of years / months / days / hours / minutes /
|
||||||
|
seconds / milliseconds.
|
||||||
|
|
||||||
|
### Recap
|
||||||
|
|
||||||
|
There are considerations when choosing data types for your code, and often more
|
||||||
|
than one option. Think through your choices, and unless you have a good reason,
|
||||||
|
try to stick with the basic types like `int`, `decimal`, `string`, and `bool`.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Choosing the right data type for your application is a vital programming skill.
|
||||||
|
Your goal was to understand the differences between each data type to make an
|
||||||
|
informed decision on the data types in your code.
|
||||||
|
|
||||||
|
You started building a mental model of how data is stored as your application
|
||||||
|
executes. You have experience working with new integral and floating point data
|
||||||
|
types. You have criteria you can use when deciding which data types to employ
|
||||||
|
in your code. Finally, you understand why you need to use the `new` keyword when
|
||||||
|
creating instances of reference types.
|
36
020_data_types/test_app/Program.cs
Normal file
36
020_data_types/test_app/Program.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Console.WriteLine("-------------------------------------------");
|
||||||
|
Console.WriteLine("Signed integral types:");
|
||||||
|
Console.WriteLine($"sbyte : {sbyte.MinValue} to {sbyte.MaxValue}");
|
||||||
|
Console.WriteLine($"short : {short.MinValue} to {short.MaxValue}");
|
||||||
|
Console.WriteLine($"int : {int.MinValue} to {int.MaxValue}");
|
||||||
|
Console.WriteLine($"long : {long.MinValue} to {long.MaxValue}");
|
||||||
|
Console.WriteLine("-------------------------------------------");
|
||||||
|
Console.WriteLine("Unsigned integral types:");
|
||||||
|
Console.WriteLine($"byte : {byte.MinValue} to {byte.MaxValue}");
|
||||||
|
Console.WriteLine($"ushort : {ushort.MinValue} to {ushort.MaxValue}");
|
||||||
|
Console.WriteLine($"uint : {uint.MinValue} to {uint.MaxValue}");
|
||||||
|
Console.WriteLine($"ulong : {ulong.MinValue} to {ulong.MaxValue}");
|
||||||
|
|
||||||
|
Console.WriteLine("-------------------------------------------");
|
||||||
|
Console.WriteLine("Floating point types:");
|
||||||
|
Console.WriteLine($"float : {float.MinValue} to {float.MaxValue} (with ~6-9 digits of precision)");
|
||||||
|
Console.WriteLine($"double : {double.MinValue} to {double.MaxValue} (with ~15-17 digits of precision)");
|
||||||
|
Console.WriteLine($"decimal: {decimal.MinValue} to {decimal.MaxValue} (with 28-29 digits of precision)");
|
||||||
|
Console.WriteLine("-------------------------------------------");
|
||||||
|
int val_A = 2;
|
||||||
|
int val_B = val_A;
|
||||||
|
val_B = 5;
|
||||||
|
|
||||||
|
Console.WriteLine("--Value Types--");
|
||||||
|
Console.WriteLine($"val_A: {val_A}");
|
||||||
|
Console.WriteLine($"val_B: {val_B}");
|
||||||
|
|
||||||
|
int[] ref_A= new int[1];
|
||||||
|
ref_A[0] = 2;
|
||||||
|
int[] ref_B = ref_A;
|
||||||
|
ref_B[0] = 5;
|
||||||
|
|
||||||
|
Console.WriteLine("--Reference Types--");
|
||||||
|
Console.WriteLine($"ref_A[0]: {ref_A[0]}");
|
||||||
|
Console.WriteLine($"ref_B[0]: {ref_B[0]}");
|
||||||
|
Console.WriteLine("-------------------------------------------");
|
10
020_data_types/test_app/test_app.csproj
Normal file
10
020_data_types/test_app/test_app.csproj
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -24,3 +24,4 @@ Following
|
|||||||
- [Looping logic using the do-while and while](./017_Looping_logic_using_do-while_and_while/017_csharp.md)
|
- [Looping logic using the do-while and while](./017_Looping_logic_using_do-while_and_while/017_csharp.md)
|
||||||
- [Conditional branching and looping](./018_Conditional_branching_and_looping/018_csharp.md)
|
- [Conditional branching and looping](./018_Conditional_branching_and_looping/018_csharp.md)
|
||||||
- [Branching and looping structures](./019_branching_and_looping_structures/019_csharp.md)
|
- [Branching and looping structures](./019_branching_and_looping_structures/019_csharp.md)
|
||||||
|
- [Choose the correct data type](/020_data_types/020_csharp.md)
|
||||||
|
Loading…
Reference in New Issue
Block a user