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
|
||||
design specification demonstrates your understanding of the iteration and
|
||||
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)
|
||||
- [Conditional branching and looping](./018_Conditional_branching_and_looping/018_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