ArticleDirectory
- 1.2.1 Basic Data Types
- 1.2.2 value type and reference type
- 1.2.3 var type
Chapter 1 Program Design
"To become a real programmer, we need a baptism ."
"Program = Data Structure +Algorithm." Such a formula is very incisive. It directly describes the nature of the program beyond the surface layer. In addition, such a few simple words also let us understand "what should we learn ?". People have programs to do everything, but they are not aware of them. Some people list their daily actions in a table, which is "Program writing ".
The focus of this chapter:
◆ Basic Data Type
◆ Value type and reference type
◆ Var type
1.2 Data Type
In this article, we first start from introducing the basic data types, and then quickly enter into the discussion about the reference type and value type. For all developers, it is particularly important to be familiar with the application differences between the reference type and the value type. CompileCodeDuring the process, improper use of these two types will cause program bugs and performance problems.
1.2.1 Basic Data Types
So far, only a small number of basic data types have been used, and the book only takes them with no detailed explanation. A large number of types exist in C #, and different types can be merged to create new types. However, there are several types in C # That are very simple, and they are considered as the basis of all other types. These types are called predefined type or primitive type ). The basic types of C # language include eight integer types, two floating point types, one high precision type, one boolean type, one character type, and one string type. Next we will discuss these basic data types.
1. Integer type
C # language has eight integer types. we can select the most appropriate data type to store data to avoid wasting resources.
|
Type |
Size |
Range |
Bcl name |
|
Sbyte |
8-digit |
-128 ~ 127 |
System. sbyte |
|
Byte |
8-digit |
0 ~ 255 |
System. byte |
|
Short |
16-bit |
-32768 ~ 32767 |
System. int16 |
|
Ushort |
16-bit |
0 ~ 65535 |
System. uint16 |
|
Int |
32-bit |
-2147483648 ~ 2147483 647 |
System. int32 |
|
Uint |
32-bit |
0 ~ 4294967295 |
System. int32 |
|
Long |
64-bit |
-9223372036854775808 ~ 9223372036854775807 |
System. int64 |
|
Ulong |
64-bit |
0 ~ 18 446 744 073 709 551 615 |
System. int64 |
All basic types of C # Have a short name and a complete name. The complete name corresponds to the type named in BCl. This name is the same in all languages and uniquely identifies the type in the Assembly. Because basic data types are the basis of other types, C # provides short names or abbreviations for the complete names of basic data types. In fact, from the perspective of the compiler, the two names are exactly the same, and the same code will be generated eventually. As a matter of fact, if you check the final generated template Code, there is no indicationSource codeWhich name is used in. Here is an example:
1 using system;
2 namespace Microsoft. Example
3 {
4 public class testint
5 {
6 static void main (string [] ARGs)
7 {
8 byte bytevalue = 2;
9 short partial value =-4;
10 int intvalue =-8;
11 long longvalue =-10;
12 console. writeline ("byte type variable value:" + bytevalue );
13 console. writeline ("short type variable value:" + SHORT value );
14 console. writeline ("int type variable value:" + intvalue );
15 console. writeline ("long type variable value:" + longvalue );
16}
17}
18}
In the above Code, we define a series of integer types. The main difference between them is: the capacity of different types is different, and the positive and negative values are different. Row 3 defines a byte type, which can only store positive integers. In many cases, because int can save both positive numbers and negative numbers, uint can only save positive integers. All, we follow this mindset, it is easy to confuse, think that byte can save a positive integer, or save a negative integer. In fact, this is not the case. There is no ubyte type at all, and only sbyte is available. You must remember this.
The final output result is:
Byte type variable value: 2
The short type variable value is-4.
Int type variable value:-8
Long type variable value:-10
In real projects, when we define the variable type, we usually do not use the intvalue method to name the variable. This method is called the Hungary naming method, that is, we put the variable type before the variable, the advantage of this method is that the type of variables is clear at a glance. We write this here because of this reason, readers can quickly separate different variables. In the project, the type prompting function of the development tool (IDE) is already very powerful and does not need to be done at all.
2. Floating Point Type
The precision of floating point is variable. If the read value is a floating point number of 0.1, it may easily be 0.099 999 999 999 999 999 or 0.100 000 000 000 000 1 or another number very close to 0.1. Because the original number is too large. According to the definition, the precision of a floating point number is proportional to the number of numbers it contains. To be accurate, the accuracy depends on the number of valid numbers, rather than a fixed value, such as ± 0. 01.
Type size range: effective number of the Bcl name
Float 32-bit ± 1. 5 × 1045 ~ ± 3. 4 × 1038 system. Single 7
Double 64-bit ±5. 0 × 10324 ~ ± 1. 7 × 10308 system. Double 15 ~ 16
3. Decimal type
C # There is a numeric type with 128-bit precision. It is suitable for big and accurate computing, especially financial computing.
Type size range: effective number of the Bcl name
Decimal 128 bits ± 1. 0 × 1028 ~ ± 7. 9 × 1028 system. Decimal 28 ~ 29
Unlike floating-point numbers, the decimal type ensures that all decimal numbers in the range are accurate. Therefore, for the decimal type, 0.1 is 0.1, rather than an approximate value. However, although the decimal type has a higher precision than the floating point type, it has a smaller range. Therefore, an overflow error may occur when converting from a floating point type to a decimal type. In addition, decimal computing speed is a little slower.
Unless the range is exceeded, the decimal number indicates the exact decimal number. Conversely, using a floating point to represent a decimal number may cause a rounding error. The difference between the decimal and C # Floating Point types is that the decimal type index is a decimal number, while the floating point type index is binary.
By default, if you enter a literal value with a decimal point, the compiler automatically interprets it as a double type. On the contrary, an integer (with no decimal point) is usually int by default, provided that the value is not too large to be stored with Int. If the value is too large, the compiler will interpret it as long. In addition, the C # compiler allows assignment to a non-int value type, provided that the literal value is valid for the target data type. For example, both short S = 42 and byte B = 77 are allowed. Here is an example:
1 namespace Microsoft. Example
2 {
3 Public class testdoubleanddecimal
4 {
5 static void main (string [] ARGs)
6 {
7 double doublevalue = 1.618033988749895; // specify a double literal
8 decimal decimalvalue = 1.618033988749895 m; // specify a decimal literal
9 console. writeline ("Double type variable value is" + doublevalue); // output doublevalue
10 console. writeline ("decimal type variable value is" + decimalvalue); // output decimalvalue
11}
12}
13}
In the above Code, we define a double type data doublevalue in row 7th. Its value is 1.618033988749895 and the output result is 1.61803398874989. As mentioned above, C # has many different numerical types. A literal value is directly put into the C # code. Because the number of decimal points is of the double data type by default, the output is 1.61803398874989 (the last number 5 is lost), which meets the expected double value precision.
Row 3: We define a decimal type of data. To view a number with complete precision, the literal value must be explicitly declared as a decimal type. This is done by appending an M (or m). The output is 1.618033988749895.
Now, the code output is the same as the expected result: 1.618033988749895. Note: d Indicates double. M indicates decimal because this data type is often used in monetary calculation. You can also use F and D as suffixes to explicitly declare a literal as float or double.
The final output result is:
The double type variable value is 1.61803398874989.
The decimal type variable value is 1.618033988749895.
4. boolean type
Another C # basic type is Boolean bool, which can also be called a condition type. In condition statements and expressions, it is used to indicate true or false. The allowed values include the keywords true and false. The bcl name of bool is system. boolean. A boolean literal value uses the keywords true and false. For example, to compare two strings without case sensitivity, you can call the string. Compare () method and pass the Boolean literal value true.
For example, compare two strings in case-insensitive Mode
String option = "/help ";
Int comparison = string. Compare (option, "/help", true );
In this example, we compare the content and the word volume/help of the variable option in case-insensitive mode and assign the result to comparison. Theoretically, a bit is enough to accommodate a Boolean value, but the actual size of the bool data type is one byte. Here is an example:
1 namespace Microsoft. Example
2 {
3 Public class testbool
4 {
5 static void main (string [] ARGs)
6 {
7 bool boolflag = false; // defines a bool-type data.
8 console. writeline ("bool type variable value is" + boolflag. tostring (); // output result
9}
10}
11}
In the above Code, Row 3 defines a bool type data and assigns the value false. The bool type has only two values: true and false. Then, we output the results in the 8th line of code.
The final output result is:
Boolean type variable value is false
5. Character Type
The character type char is used to represent 16 characters. Its value range corresponds to the Unicode Character Set. Technically, the size of a char is equivalent to the size of a 16-bit unsigned integer (ushort). The latter value ranges from 0 ~ 65 535. However, char is a unique type in C # and should not be treated like this. The bcl name of char is system. Char.
Unicode is an international standard used to represent characters in most human languages. It facilitates computer systems to build localized applications and display local characters for different languages and cultures. Unfortunately, not all Unicode characters can be represented by a 16-bit char. When we first proposed the Unicode concept, its designers thought that 16 bits were sufficient. However, as more languages are supported, the original assumption is incorrect. The result is that some Unicode characters are composed of a pair of char called "proxy items", with a total of 32 characters.
To enter a literal of the character type, put the character in a pair of single quotes, such as 'A '. All keyboard characters can be entered like this, including letters, numbers, and special characters. Some characters cannot be directly inserted into the source code, which requires special processing. These characters have a backslash (\) prefix and follow a special character code. The backslash and special character code are collectively called escape sequences ). For example, '\ n' indicates a line break, while' \ t' indicates a tab. Because the backslash marks the beginning of an escape sequence, it cannot be used to directly represent a backslash character, but '\' must be used to represent a backslash character.
Here is an example:
1 namespace Microsoft. Example
2 {
3 Public class testchar
4 {
5 static void main (string [] ARGs)
6 {
7 char charvalue = '\ ''; // defines a char type variable.
8 system. Console. writeline ("output result:" + charvalue); // output result
9}
10}
11}
In the above Code, we define a charvalue variable of the char type in row 7th, and then assign the value '\ ''to it '\''. The character type uses single quotes to hold characters. Then we use writeline to output the result to the console.
The output result is :'
6. string type
The basic string type of C # Is string, and its Bcl name is system. String. For developers who are familiar with other languages, some characteristics of string may be unexpected. For example, a string prefix @ and a string is of an unchangeable type.
String can input the literal string into the code. Put the text in double quotation marks ("), just like in the helloworld program.
In C #, you can use the @ symbol before a string to indicate that the escape sequence is not processed. In this way, the result is a literal (verbatim string literal), which not only treats the backslash as a normal character, but also interprets all the blank characters verbatim. For example, you can use a literal string to display a triangle.
1 using system;
2 class teststring
3 {
4 static void main ()
5 {
6 system. Console. Write (@ "begin // note that the @ symbol is used here
7 \ N // note that the linefeed "\ n" is used, but it is not escaped.
8 end ");
9}
10}
In the above Code, the @ symbol is used in line 1 to indicate that the escape sequence is ignored and output literally. In actual projects, this function is very helpful. We often use it to process path strings, which is simple and clear. In line 3 of the Code, we first line feed with a carriage return before entering "\ n. Note that the carriage return line in the output result does not produce the effect of "\ n". Instead, we perform the carriage return line during code writing. After all the @ symbols are used, the carriage return and line feed will also be carried out. "\ n" is completely ignored.
The final output result is:
Begin
\ N
End
1.2.2 value type and reference type
First, variables are the basic unit for storing information. For computers, variables are equivalent to a piece of memory space. C # variables in the language can be divided into two types: Value Type and reference type:
Value Type: basic data type, structure type, enumeration type, etc.
Reference Type: Class, array, interface, etc.
(1) Value Type and reference type memory allocation
The value type is in the stack, and the reference type is allocated to the storage unit in the heap. The stack allocates memory space during compilation, which is clearly defined in the Code, and the heap is the memory space dynamically allocated in the program running, the memory size can be dynamically allocated based on the program running status. Therefore, the value type always occupies a predefined number of bytes in the memory (for example, int occupies 4 bytes, that is, 32 bytes ). When a value type variable is declared, the runtime allocates memory space in the stack and stores the values contained in the variable .. Net automatically maintains a stack pointer, which contains the address of the next available memory space in the stack. The stack is first imported and then output, and the top variables in the stack always leave the scope before the following variables. When a variable leaves the scope, the stack pointer moves down the number of bytes occupied by the released variable and still points to the next available address. Note: value-type variables must be initialized during use.
Variables of the reference type are allocated with a memory space in the heap. This memory space contains references to another memory location, which is an address in the managed heap, that is, the place where the actual value of this variable is stored .. Net also automatically maintains a heap pointer, which contains the address of the next available memory space in the heap, but the heap does not release the memory when the object is not used ,.. NET will periodically perform garbage collection. The garbage collector recursively checks all object references in the application. When it finds that the reference is no longer valid, the memory used by the object cannot be accessed from the program, this memory can be recycled (except for objects fixed in memory by the fixed keyword ).
However, the value type allocates memory on the stack, while the reference type allocates memory on the managed stack. More detailed and accurate descriptions are as follows:
1. For a value-type instance, if it is a local variable in the method, it is created on the thread stack. If the instance is a member of the type, it is part of the type member, other types of fields are stored on the managed stack,
2. An instance of the reference type is created on the managed stack. If its byte is smaller than 85000 bytes, it is directly created on the managed stack. Otherwise, it is created on the LOH large object stack.
For example:
Public class test
{
Private int I; // as part of the test instance, it is created together with the test instance on the managed stack.
Public test ()
{
Int J = 0; // as a local real volume, the instance of J is created on the thread stack where the code is executed.
}
}
(2) memory allocation in the nested Structure
The so-called nested structure means that the reference type is nested with the value type, or the value type is nested with the reference type.
The reference type nested value type is the most common. The preceding example is a typical example. The value type is inline in the reference type.
When a value type is nested with a reference type, the reference type is used as a variable of the value type member, and the reference type is retained on the stack, but the reference type still needs to be allocated memory in the heap.
(3) Array Memory Allocation
Consider the case when the array Members are of the value type and reference type:
A member is a value type. For example, int [] arr = new int [5]. Arr will save a reference pointing to the address of 4*5 bytes in the managed heap (INT occupies 4 bytes), and assign all elements to 0;
Reference Type: myclass [] arr = new myclass [5]. Arr creates a reference pointing to the managed heap in the thread stack. All elements are set to null.
(4) Impact of value type and reference type when passing Parameters
Because value types directly store their data in the stack, when a value type parameter is passed to a method, a new copy of the value is created and passed, any modification to the parameter will not cause the variable passed to the method to be modified. The reference type only contains references and does not contain actual values. Therefore, any modifications made to parameters in the method body will affect the variables of the reference type passed to the method call. Here is an example:
1 using system;
2 namespace Microsoft. Example
3 {
4 public class testpassparam
5 {
6 static void main ()
7 {
8 int I = 0; // defines a variable of the value type (integer ).
9 int [] intarr = {0}; // defines a variable of the reference type (array ).
10 setvalues (I, intarr); // PASS Parameters
11 console. writeline ("I = {0}, intarr [0] = {1}", I, intarr [0]); // output result
12}
13 public static void setvalues (int I, int [] intarr)
14 {
15 I = 10; // change the value type
16 intarr [0] = 10; // change the value of the reference type
17}
18}
19}
In the above Code, we define a value type variable I in line 8th, which is an integer type data. In row 9th, we define a variable intarr of the reference type, which is an array. The knowledge about arrays will be detailed later, here we only need to know that it is a reference type. Then we call the setvalues method to pass the parameters of the value type and reference type to the past for modification.
Finally, we found in the output result of row 11th that the change of the Value Type I in the setvalues method is invalid, and it is still 0, however, the value changes of the reference type intarr in the setvalues method are retained, and the value is 10.
The final output result is:
I = 0, intarr [0] = 10
(5) packing and unpacking
Packing converts a value type to an object type, while unpacking explicitly converts an object type to a value type. For packing, it refers to copying a copy of The boxed value type for conversion. For unpacking, you need to pay attention to the compatibility of the type, for example, you cannot convert an object whose value is "a" to an int type.
Here is an example:
1 using system;
2 namespace Microsoft. Example
3 {
4 public class testbox
5 {
6 static void main ()
7 {
8 int I = 10; // defines an integer variable.
9 object o = I; // perform the packing operation
10 if (O is int) // determine whether to pack
11 {
12 console. writeline ("I already packed ");
13}
14 Int J = (INT) O; // unpack
15 console. writeline ("J has been split ");
16 console. writeline ("I value is" + I );
17 console. writeline ("J value:" + J );
18}
19}
20}
In the above Code, we define an integer variable I in row 8th, And then we perform the packing operation in row 9th. If the packing is successful, the reference of O is the int type. We can use o. GetType () to know that O is of the int type. In row 14th, we unbox the O variable through forced type conversion to get an integer data. We assign this data to the J integer variable for saving.
The final output result is:
I has been boxed
J has been split
The I value is 10.
The value of J is 10.
(6) about string
String is a reference type, but it is different from other reference types. We can look at the following two aspects:
1. The string class inherits from the object class. Instead of system. valuetype.
2. String is essentially a char [], while array is a reference type. It also allocates memory in the managed heap. However, when a string is passed as a parameter, the value type is characteristic. When a variable of the string type is passed into the method for processing, the value of this variable does not change after the method is left. The reason is that a new object is created every time the string value is modified.
1.2.3 var type
A Var keyword is added to C #3.0. This keyword is similar to JavaScript var. We can use VaR to declare any type of local variables. But there is another difference. in C #, it is just a keyword and does not represent a new type. It is only responsible for telling the compiler, the variable type must be inferred based on the initialization expression, and can only be a local variable. After var declares the variable, the variable type is determined and will not be changed. This is essentially different from JavaScript.
The VaR keyword can declare any type of local variables as follows:
VaR name = "CSHARP ";
VaR names = new string [] {"C #", "VB", "c ++ "};
It is equivalent to the following statement:
String name = "CSHARP ";
String [] names = new string [] {"C #", "VB", "c ++ "};
Note: The VaR type must be assigned at the same time during declaration, because the specific type of declaration depends on the expression type on the right of the value pair. The expression type cannot be null. the compiler cannot deduce the type of the output variable based on null.
This section introduces two concepts closely related to the VaR Keyword: Implicit local variables and anonymous types.
1. Implicit local variables
When we introduced the concept of the VaR keyword above, we knew that we could assign the value to the VaR type of the local variable, which is an implicit type inferred from the type of the expression on the right, instead of explicit type. The implicit type can be a built-in type, a user-defined type, or a type defined in the. NET Framework class library.
The following example demonstrates how to declare an implicit local variable using the VaR Keyword:
VaR name = "CSHARP ";
VaR names = new string [] {"C #", "VB", "c ++ "};
When we first introduced the concept, we used Partial Variables of the implicit type. Here we use the same example to avoid confusion. One thing to understand is that the VaR keyword does not mean a "variant", nor does it mean that the variable is a loosely typed variable or is bound later. It only indicates that the compiler determines and assigns the most appropriate type.
The VaR keyword can be used in the following context:
In the for initialization statement: For (VAR x = 1; x <10; X ++)
In the foreach initialization statement: foreach (VAR item in list ){...}
In the using statement: Using (var file = new streamreader ("C: \ myfile.txt ")){...}
For more information about for, foreach, and using, see the "Basic Structure" section. In many cases, VaR is optional, but it only provides syntax convenience.
2. Anonymous type
The anonymous type provides a convenient way to encapsulate a set of read-only attributes into an object without explicitly defining the object type. The type name is generated by the compiler and cannot be used at the source code level. The types of these attributes are inferred by the compiler. The following example shows two anonymous types initialized by Attributes named amount and message respectively.
VaR v = new {amount = 100, message = "hello "};
V is an anonymous type. Note that V is a variable name, not a class name. There is also a var above. What is this? This is an implicit local variable. The anonymous type is usually used in the select clause of the query expression to return the attribute subset of each object in the source sequence. For more information about the query, see the LINQ query expression.
The anonymous type is a class type consisting of the new operator and one or more public read-only attributes. Other types of class members (such as methods or events) are not allowed ). The anonymous type cannot be forcibly converted to any interface or type other than the object.
The most common solution is to use other types of attributes to initialize the anonymous type. In the following example, assume that a class named product contains the color and price attributes and other attributes that you are not interested in. Products is a collection of product objects. The anonymous type declaration starts with the new keyword. It initializes a new type that only uses the two attributes of product. This causes a small amount of data to be returned in the query.
VaR productquery =
From prod in products
Select New {prod. Color, prod. Price };
Foreach (var v in productquery)
{
Console. writeline ("color = {0}, price = {1}", V. Color, V. Price );
}
When assigning an anonymous type to a variable, you must use VaR to construct and initialize the variable. This is because only the compiler can access the basic name of the anonymous type and cannot use this name at the source code level.
Finally, I would like to tell you that using VAR too much may make the source code less readable. We recommend that you use VaR only when necessary, that is, it is used only when the variable is used to store an anonymous type or an anonymous type set. I think the emergence of VaR actually exists completely in combination with the anonymous type. There are also many applications in LINQ, that is, when the object is of the anonymous type or the object is of an unpredictable type. Code such as VAR age = 10; it is better to use less. This type of code is safe and highly readable.