C # type Basics

Source: Internet
Author: User
Tags mscorlib

Introduction
The purpose of this article is to introduce the Prototype (Prototype) mode in the design mode, but if you want to clearly understand this mode, you need to understand the Object Clone (Object Clone ), clone is actually object replication. Replication is divided into Shallow Copy and Deep Copy, and also by how to Copy referenced members. This introduces the reference type and value type, as well as related basic knowledge such as object judgment, packing, and unpacking.
So I just wrote a new article, starting from the base to the top of the basic type. I just want to express my understanding of this topic. One is to summarize and review the topic, and the other is to exchange experiences. Maybe some of my understandings are biased and I hope to correct them. If the previous basic content is too simple for you, you can skip to read it.
Value Type and reference type
Let's first briefly review the type system in C. C # has two types: Value Type and Reference Type ). Value and reference types are divided by how they are allocated in computer memory. Value types include structure and enumeration, and reference types include class, interface, and delegate. There is also a special value Type called Simple Type, such as byte and int. These Simple types are actually aliases of the FCL class library Type, such as declaring an int Type, it is actually declaring a System. int32 structure type. Therefore, all the operations defined in Int32 can be applied to the int type, such as "123. Equals (2 )".
All value types are implicitly inherited from System. valueType type (note System. valueType itself is a class type), System. valueType and all reference types are inherited from System. object base class. You cannot explicitly inherit a class from the structure, because C # does not support multiple inheritance, and the structure has been implicitly inherited from ValueType.
NOTE: A stack is a kind of data structure that comes first and foremost. In the memory, variables are allocated to the stack for operations. Heap is the memory area used to allocate space for Type instances (objects). It creates an object on the stack, it will pass the object address to the variable on the stack (in turn, it is called that the variable points to this object, or the variable references this object ).
1. Value Type
When a Variable of the value type is declared, the Variable itself contains all fields of the value type, and the Variable will be allocated on the Thread Stack.
If we have such a value type, it represents a point in a straight line:
Public struct ValPoint {
Public int x;

Public ValPoint (int x ){
This. x = x;
}
}
When we write a declaration statement for such a variable in the program:
ValPoint vPoint1;
The actual effect is to declare the vPoint1 variable. The variable itself contains all fields of the value type (that is, all the data you want ).

NOTE: If you observe the MSIL code, you will find that the variable has not been pressed to the stack, because. maxstack (maximum number of stacks) is 0. The command for entering the stack is not displayed. This indicates that only operations on variables are performed on the stack.
Because the variable already contains all fields of the value type, you can perform operations on it now (operations on the variable are actually a series of inbound and outbound operations ).
VPoint1.x = 10;
Console. WriteLine (vPoint. x); // output 10
NOTE: If vPoint1 is a reference type (such as class), an NullReferenceException will be thrown during runtime. Because vPoint is a value type and does not have a reference, NullReferenceException will never be thrown.
If you do not assign a value to vPoint. x and directly write Console. WriteLine (vPoint. x), a compilation error occurs: the unassigned local variable is used. This error is caused by a. Net constraint: All elements must be initialized before use. For example, such a statement will also cause this error:
Int I;
Console. WriteLine (I );
To solve this problem, we can use this method: the compiler implicitly creates a non-parameter constructor for the structure type. In this constructor, structure members are initialized. All value type members are assigned 0 or a value equivalent to 0 (for Char type). All reference types are assigned null values. (Therefore, the Struct type cannot declare a constructor without parameters ). Therefore, we can create a ValPoint type variable using the implicitly declared constructor:
ValPoint vPoint1 = new ValPoint ();
Console. WriteLine (vPoint. x); // The output is 0.
We split the expression in the first sentence of the code from "=" into two parts:
On the left of ValPoint vPoint1, create a ValPoint variable vPoint on the stack. No value is assigned to all the members of the structure. Press the vPoint to the stack before performing the new ValPoint () operation.
The new ValPoint () operator on the right does not allocate memory. It only calls the default constructor of the ValPoint structure and initializes all fields of the vPoint structure according to the constructor.
Note that the new operator does not allocate memory. It only calls the default constructor of the ValPoint structure to initialize all vPoint fields. How can I explain this?
Console. WriteLine (new ValPoint (). x); // normal, output is 0
In this case, a temporary variable is created and initialized using the default constructor of the structure. I know that I am unconvincing, so let's take a look at the ms il code. To save space, I just excerpted some:
. Locals init ([0] valuetype Prototype. ValPoint CS $0 $0000) // declare a Temporary Variable
IL_0000: nop
IL_0001: ldloca. s CS $0 $0000 // press the temporary variable to stack
IL_0003: initobj Prototype. ValPoint // initialize this variable
In this case, the MSIL code is:
. Locals init ([0] valuetype Prototype. ValPoint vPoint) // declare vPoint
IL_0000: nop
IL_0001: ldloca. s vPoint // press the vPoint to stack
IL_0003: initobj Prototype. ValPoint // use initobj to initialize this variable
So when we use a custom constructor, what if ValPoint vPoint = new ValPoint (10? The following code shows that the call command (instruction) is used to call our custom constructor and pass 10 to the parameter list.
. Locals init ([0] valuetype Prototype. ValPoint vPoint)
IL_0000: nop
IL_0001: ldloca. s vPoint // press the vPoint to stack
IL_0003: ldc. i4.s 10 // press 10 on the stack
// Call the constructor and PASS Parameters
IL_0005: call instance void Prototype. ValPoint:. ctor (int32)
It doesn't matter if the above MSIL code is unclear, and sometimes the result is enough. When MSIL code is available, I will translate some good articles for you.
2. Reference Type
When a referenced type variable is declared, the referenced type variable is allocated to the stack. This variable is used to save the memory address of the referenced type instance on the stack, the variable itself does not contain the data of the object. If such a variable is declared, the value of the variable is null because no instance of the type has been created on the stack, which means that the variable does not point to any type instance (the object on the stack ). The type declaration of a variable is used to restrict the types that can be saved by this variable.
If we have such a class, it still represents a point in the straight line:
Public class RefPoint {
Public int x;

Public RefPoint (int x ){
This. x = x;
}
Public RefPoint (){}
}
When we write only one declaration statement:
RefPoint rPoint1;
The effect is the same. It only creates a variable on the stack that does not contain any data or point to any object (does not contain the address of the object created and then stacked.

When we use the new operator:
RPoint1 = new RefPoint (1 );
This will happen:
Create an Instance or Object on the application Heap and allocate a memory address to it.
The instance reference is automatically passed to the constructor. (Because of this, you can use this in the constructor to access this instance .)
Call this type of constructor.
Returns the instance reference (memory address) and assigns the value to the rPoint variable.

3. Simple Type
Many articles and books always like to use an int type as the value type and an Object type as the reference type to illustrate this problem. In this article, a custom structure and class are used to describe the value type and reference type respectively. This is because the simple type (such as int) has some CLR-implemented behaviors that may cause misunderstanding of some operations.
For example, if we want to compare whether two int types are equal, we usually do this:
Int I = 3;
Int j = 3;
If (I = j) Console. WriteLine ("I equals to j ");
However, for custom value types, such as structures, you cannot use "=" to determine whether they are equal. Instead, you need to use the Equals () method on the variables.
For another example, we know that string is a reference type, And we compare them to see if they are equal. We usually do this:
String a = "123456"; string B = "123456 ";
If (a = B) Console. WriteLine ("a Equals to B ");
In fact, we will see later that when "=" is used to compare the referenced type variables, it compares whether they point to the same object on the stack. The objects a and B point to are obviously different objects, but the objects contain the same values. Therefore, for the string type, CLR compares them to actually compare values, instead of referencing.
In order to avoid the confusion caused by the above, the custom structure and class will be used to describe the object judgment and other parts separately.
Packing and unpacking
This part of content can be very simple. This article will only give a brief review. Simply put, packing is to convert a value type to an equivalent reference type. It consists of the following steps:
Allocate memory to the newly generated object (the object contains data and the object itself has no name) on the stack.
Copy the value of the value type variable on the stack to the object on the stack.
Return the address of the object created on the stack to the reference type variable (from the programmer's point of view, the variable name is like the name of the object on the stack ).
When we run such code:
Int I = 1;
Object boxed = I;
Console. WriteLine ("Boxed Point:" + boxed );
Yes:

The MSIL code is as follows:
. Method private hidebysig static void Main (string [] args) cel managed
{
. Entrypoint
// Code size 19 (0x13)
. Maxstack 1 // The maximum number of stacks is 1.
. Locals init ([0] int32 I, // declare variable I (1st variables, index 0)
[1] object boxed) // declare the variable boxed (2nd variables with an index of 1)
IL_0000: nop
IL_0001: ldc. i4.s 10 // #1 Press stack 10
IL_0003: stloc.0 // #2 10 output stack, assign the value to I
IL_0004: ldloc.0 // #3 press the I stack
IL_0005: box [mscorlib] System. Int32 // #4 I out of stack, boxed for I (copy value to heap, return address)
IL_000a: stloc.1 // #5 assign the return value to the variable boxed
IL_000b: ldloc.1 // press the boxed Stack
// Call the WriteLine () method
IL_000c: call void [mscorlib] System. Console: WriteLine (object)
IL_0011: nop
IL_0012: ret
} // End of method Program: Main
The unboxing function converts a boxed reference type to a value type:
Int I = 1;
Object boxed = I;
Int j;
J = (int) boxed; // display the declared type after unpacking
Console. WriteLine ("UnBoxed Point:" + j );
Note that the UnBox operation needs to display the type of the switch after declaring the binning. It can be completed in two steps:
Obtain the address of the packed object.
Copy the value from the object on the stack to the value variable on the stack.
Object Determination
Because we need to mention object cloning (replication), we should have a way to know whether the two objects before and after replication are equal. Therefore, before proceeding to the following chapters, we must first understand how to judge objects.
NOTE: I have the opportunity to study this part in depth. I would like to thank Microsoft for its open-source and VS2008 FCL debugging functions. For more information about how to Debug FCL Code, see using ing Visual Studio to Debug. NET Framework Source Code.
We first define two types used as examples. They represent a point in a straight line. The only difference is that one is the reference type class, and the other is the value type struct:
Public class RefPoint {// define a reference type
Public int x;
Public RefPoint (int x ){
This. x = x;
}
}

Public struct ValPoint {// define a Value Type
Public int x;
Public ValPoint (int x ){
This. x = x;
}
}
1. Judge the reference type.
We first judge the object of the reference type, and we know that in System. in the Object base type, the instance method Equals (object obj), static method Equals (object objA, object objB), static method ReferenceEquals (object objA, object objB) are defined) to determine objects.
Let's take a look at these three methods first. Note where # number is used in the code, and I will directly reference it later:
Public static bool ReferenceEquals (Object objA, Object objB)
{
Return objA = objB; // #1
}

Public virtual bool Equals (Object obj)
{
Return InternalEquals (this, obj); // #2
}

Public static bool Equals (Object objA, Object objB ){
If (objA = objB) {// #3
Return true;
}

If (objA = null | objB = null ){
Return false;
}

Return objA. Equals (objB); // #4
}
Let's first look at the ReferenceEquals (object objA, object objB) method. It actually returns objA = objB. Therefore, unless necessary, we use objA = objB in a unified way (eliminating the ReferenceEquals method ). In addition, for simple examples, we do not consider the case where the object is null.
Let's look at the first code:
// Copy Object Reference
Bool result;
RefPoint rPoint1 = new RefPoint (1 );
RefPoint rPoint2 = rPoint1;

Result = (rPoint1 = rPoint2); // return true;
Console. WriteLine (result );

Result = rPoint1.Equals (rPoint2); // #2 returns true;
Console. WriteLine (result );
In this article, we should think about a stack, a heap, and the effects of each statement on these two structures at all times. In this Code, a new RefPoint instance (object) is created on the stack and Its x field is initialized to 1; create the variable rPoint1 and rPoint1 on the stack to save the address of the object on the stack. When rPoint1 is assigned to rPoint2, no new object is created on the stack, instead, the address of the previously created object is copied to rpoint2. In this case, rPoint1 and rPoint2 point to the same object on the stack.
From the ReferenceEquals () method name, we can see that it determines whether two referenced variables point to the same variable. If yes, true is returned. Such equality is called equal references (rPoint1 = rPoint2 is equivalent to ReferenceEquals ). Because they point to the same object, operations on rPoint1 will affect rPoint2:

Note that the System. Object static Equals (Object objA, Object objB) method returns true if the two variables reference the same value at #3. Therefore, we can expect the above Code rPoint1.Equals (rPoint2); In #3, true will be returned. But we didn't call static Equals (), directly called the entity method, and finally called InternalEquals () of #2, and returned true. (InternalEquals () has no data to query and can only be tested through debugging ).
Let's look at the second case of the reference type:
// Create an object of the new reference type with the same Member values
RefPoint rPoint1 = new RefPoint (1 );
RefPoint rPoint2 = new RefPoint (1 );

Result = (rPoint1 = rPoint2 );
Console. WriteLine (result); // return false;

Result = rPoint1.Equals (rPoint2 );
Console. WriteLine (result); // #2 returns false
The above Code creates two types of instances on the stack and initializes them with the same value. Then, their addresses are assigned to the stack variables rPoint1 and rPoint2 respectively. At this time, if #2 returns false, we can see that for the reference type, even if the instance (object) of the type contains the same value, if the variable points to a different object, it is not equal.
2. Simple value type determination
Note the title of this section: simple value type determination. How is this simple definition? If a value type member only contains a value type, it is called a simple value type for the moment. If a value type member contains a reference type, we call it a complex value type. (Note: This is only the definition I have made in this article .)
We should remember that we mentioned earlier that the value type will be implicitly inherited from System. the ValueType type, while the ValueType type covers the base class System. the Equals () method of the Object type. When the Equals () method is called on the value type, the Equals () method of the ValueType type is called (). So let's take a look at what this method looks like. We still use # number to identify what will be referenced later.
Public override bool Equals (Object obj ){
If (null = obj ){
Return false;
}
RuntimeType thisType = (RuntimeType) this. GetType ();
RuntimeType thatType = (RuntimeType) obj. GetType ();

If (thatType! = ThisType) {// If two objects are not of the same type, false is returned directly.
Return false;
}

Object thisObj = (Object) this;
Object thisResult, thatResult;

If (CanCompareBits (this) // #5
Return FastEqualsCheck (thisObj, obj); // #6

// Retrieve all fields of the Value Type Using Reflection
FieldInfo [] thisFields = thisType. GetFields (BindingFlags. Instance | BindingFlags. Public | BindingFlags. NonPublic );
// Traverse fields to compare Fields
For (int I = 0; I <thisFields. Length; I ++ ){
ThisResult = (RtFieldInfo) thisFields [I]). InternalGetValue (thisObj, false );
ThatResult = (RtFieldInfo) thisFields [I]). InternalGetValue (obj, false );

If (thisResult = null ){
If (thatResult! = Null)
Return false;
}
Else
If (! ThisResult. Equals (thatResult) {// #7
Return false;
}
}

Return true;
}
Let's take a look at the first piece of code:
// Copy the Structure Variable
ValPoint vPoint1 = new ValPoint (1 );
ValPoint vPoint2 = vPoint1;

Result = (vPoint1 = vPoint2); // compilation error: the "=" operator cannot be applied to the ValPoint.
Console. WriteLine (result );

Result = Object. ReferenceEquals (vPoint1, vPoint2); // implicit packing, pointing to different objects on the stack
Console. WriteLine (result); // return false
We first created a variable vPoint1 on the stack, which already contains all fields and data. Then a copy of vPoint1 is copied to vPoint2 on the stack. In general sense, we think it should be equal. Next we will try to compare them. We can see that we cannot use "=" to directly judge them. This will return a compilation error. If we call the static ReferenceEquals () method of the System. Object base class, an interesting thing happens: it returns false. Why? Let's take a look at the signature of the ReferenceEquals () method. It accepts the Object type, that is, the reference type. When we pass the vPoint1 and vPoint2 value types, an implicit packing is performed, and the effect is equivalent to the following statement:
Object boxPoint1 = vPoint1;
Object boxPoint2 = vPoint2;
Result = (boxPoint1 = boxPoint2); // return false
Console. WriteLine (result );
As we have already mentioned in the packing process, the operations above are equivalent to creating two objects on the stack. The objects contain the same content (different addresses ), then return the object address to boxPoint1 and boxPoint2 on the stack respectively, and then compare whether boxPoint1 and boxPoint2 point to the same object. Obviously not, false is returned.
Next, add the following code:
Result = vPoint1.Equals (vPoint2); // #5 returns true; #6 returns true;
Console. WriteLine (result); // Output true
Because they all inherit from the ValueType type, the Equals () method on ValueType is called. Inside the method body, #5 CanCompareBits (this) returns true, CanCompareBits (this) in this method, according to the comments from Microsoft, consciousness is: if the object member has a reference to the stack, false is returned. If the object member does not exist, true is returned. According to the ValPoint definition, it only contains an int type field x. Naturally, there is no reference to other objects on the stack, so true is returned. From the name CanCompareBits of #5, we can see whether bitwise comparison can be performed. After true is returned, #6 naturally performs bitwise comparison.
Next, let's make some changes to vPoint2 to see what will happen:
VPoint2.x = 2;
Result = vPoint1.Equals (vPoint2); // #5 returns true; #6 returns false;
Console. WriteLine (result );
3. Complex Value Type Determination
Till now, we have not reached the position of the above methods, that is, the part after CanCompareBits returns false. We have previously deduced the condition that CanCompareBits returns false (the value type includes the reference type). Now we only need to implement it. We define a new Line structure, which represents a Line segment on a straight Line. Let's make one of its members a value type ValPoint, one member a reference type RefPoint, and then compare it.
/* Define the structure type ValLine,
Public struct ValLine {
Public RefPoint rPoint; // reference type member
Public ValPoint vPoint; // value type member
Public Line (RefPoint rPoint, ValPoint vPoint ){
This. rPoint = rPoint;
This. vPoint = vPoint;
}
}
*/

RefPoint rPoint = new RefPoint (1 );
ValPoint vPoint = new ValPoint (1 );

ValLine line1 = new ValLine (rPoint, vPoint );
ValLine line2 = line1;

Result = line1.Equals (line2); // a boxing operation exists. Call ValueType. Equals ()
Console. WriteLine (result); // return True
The process of this example is much more complex. Before starting, let's first think about how to pack line1.Equals (line2. If further determination is required, it is obvious that it cannot judge whether the variable is referenced on the same object. This makes no sense, because it always returns false (two objects are created on the heap after packing ). So how should we determine? Compare the members (fields) of the object on the stack one to one, and the members are divided into two types: Value Type and reference type. For the reference type, determine whether the reference is equal; For the value type, if it is a simple value type, then judge as described in the previous section; if it is a complex type, it is a recursive call, of course, until it is either a reference type or a simple value type.
NOTE: reflection is required for one-to-one comparison of fields. For more information about reflection, see the reflection series in. Net.
Now let's take a look at the actual process, as we expected. To avoid the frequent drag of the scroll bar to view the ValueType Equals () method, I copied some of them:
Public override bool Equals (Object obj ){

If (CanCompareBits (this) // #5
Return FastEqualsCheck (thisObj, obj); // #6
// Use reflection to retrieve all fields of the type (or members of the type)
FieldInfo [] thisFields = thisType. GetFields (BindingFlags. Instance | BindingFlags. Public | BindingFlags. NonPublic );
// Compare Fields
For (int I = 0; I <thisFields. Length; I ++ ){
ThisResult = (RtFieldInfo) thisFields [I]). InternalGetValue (thisObj, false );
ThatResult = (RtFieldInfo) thisFields [I]). InternalGetValue (obj, false );

If (thisResult = null ){
If (thatResult! = Null)
Return false;
}
Else
If (! ThisResult. Equals (thatResult) {#7
Return false;
}
}

Return true;
}
Enter the Equals () method on ValueType, # false is returned at 5;
Go to the for loop and traverse fields.
The first field is the RefPoint reference type. #7. Call the Equals () method of System. Object to reach #2, and return true.
The second field is of the ValPoint value type. #7. Call the Equals () method of System. ValType, that is, the current method. Recursive call.
Enter the Equals () method of ValueType again. Because ValPoint is a simple value type, #5 CanCompareBits returns true, and #6 FastEqualsCheck returns true.
The layer Equals () method returns true.
Exit the for loop.
The outer Equals () method returns true.
Object Replication
Sometimes it may take a lot of time to create an object. For example, an object needs to be retrieved from a remote database to fill in the data, or the object needs to be read from the hard disk. In this case, if an object already exists and a new object is created, the method of copying the existing object may be used instead of recreating a new object. This section describes how to copy objects.
1. Simple Replication
Simple replication and deep replication are divided by how to copy the members of an object (member. A member of an object may be a value type or a reference type. When we perform a simple copy of an object, the value type member will copy itself (the value type variable itself contains all the data, and the value type variable will be copied by Bit During the copy ); for a reference type member (note that it will reference another object), just copy the reference and do not create the referenced object. The result is: The reference members of the new object and the reference members of the Copied object point to the same object.
In the above example, if the object we want to copy (RefLine) is defined as follows (to avoid looking up, I will post the code here ):
// The object to be replicated in a shortest. Note that it is of the reference type.
Public class RefLine {
Public RefPoint rPoint;
Public ValPoint vPoint;
Public Line (RefPoint rPoint, ValPoint vPoint ){
This. rPoint = rPoint;
This. vPoint = vPoint;
}
}
// Define a reference type member
Public class RefPoint {
Public int x;
Public RefPoint (int x ){
This. x = x;
}
}
// Define a value type member
Public struct ValPoint {
Public int x;
Public ValPoint (int x ){
This. x = x;
}
}
First, create an object to be copied:
RefPoint rPoint = new RefPoint (1 );
ValPoint vPoint = new ValPoint (1 );
RefLine line = new RefLine (rPoint, vPoint );
The actual result is ):

So when we copy it, it will look like this (newLine is a pointer to the newly Copied object, embodied as a variable of the reference type in the Code ):

According to this definition, let's recall the content we mentioned above. We can conclude that when copying a structure type member, we can directly create a new structure type variable, then assign a value to it, which is equivalent to performing a simple copy. You can also consider that the structure type implicitly implements a simple copy. If we define the above RefLine as a structure (Struct) and the structure type is ValLine, rather than a class, we can simply copy it like this:
ValLine newLine = line;
The actual situation is as follows:

Now you have figured out what is simple replication and how to copy the structure. So how can we copy a reference type in a simple way? In. net Framework, there is an ICloneable interface, we can implement this interface for shallow replication (or deep replication, it is controversial here, some foreign users think that ICloneable should be identified as Obsolete and should be replaced by IShallowCloneable and IDeepCloneble ). This interface only requires one method Clone (), which returns a copy of the current object. We don't need to implement this method by ourselves (of course we can). In the System. Object base class, there is a protected MemeberwiseClone () method, which is used for simple replication. Therefore, for the reference type, you only need to call this method if you want to implement the simple copy:
Public object Clone (){
Return MemberwiseClone ();
}
Now let's do a test:
Class Program {
Static void Main (string [] args ){

RefPoint rPoint = new RefPoint (1 );
ValPoint vPoint = new ValPoint (1 );
RefLine line = new RefLine (rPoint, vPoint );

RefLine newLine = (RefLine) line. Clone ();
Console. writeLine ("Original: line. rPoint. x = {0}, line. vPoint. x = {1} ", line. rPoint. x, line. vPoint. x );
Console. writeLine ("Cloned: newLine. rPoint. x = {0}, newLine. vPoint. x = {1} ", newLine. rPoint. x, newLine. vPoint. x );

Line. rPoint. x = 10; // modify the original line reference type member rPoint
Line. vPoint. x = 10; // modify the original line value type member vPoint
Console. writeLine ("Original: line. rPoint. x = {0}, line. vPoint. x = {1} ", line. rPoint. x, line. vPoint. x );
Console. writeLine ("Cloned: newLine. rPoint. x = {0}, newLine. vPoint. x = {1} ", newLine. rPoint. x, newLine. vPoint. x );

}
}
Output:
Original: line. rPoint. x = 1, line. vPoint. x = 1
Cloned: newLine. rPoint. x = 1, newLine. vPoint. x = 1
Original: line. rPoint. x = 10, line. vPoint. x = 10
Cloned: newLine. rPoint. x = 10, newLine. vPoint. x = 1
It can be seen that the Copied object and the original object are connected, and their reference member fields still reference the same object on the stack.
2. Deep Replication
As a matter of fact, you may have already thought of the time when deep replication is used. Deep replication also copies the objects pointed to by reference members. The actual process is to create a new reference member pointing to the object, and then copy the data contained in the object.
Deep replication may become very complex, because the object pointed to by the referenced Member may contain another reference type member. The simplest example is a linear linked list.
If an object member contains a reference to the linear linked list structure, the simple COPY Copies only the reference of the first node, and the deep COPY Copies the linked list itself, and copy the data on each node.
Consider our previous example. If we want to perform a deep copy, how should we implement our Clone () method?
Public object Clone () {// deep copy
RefPoint rPoint = new RefPoint (); // create a new object for the reference type
RPoint. x = this. rPoint. x; // copy the value of the currently referenced type member to the new object.
ValPoint vPoint = this. vPoint; // value type, direct value assignment
RefLine newLine = new RefLine (rPoint, vPoint );
Return newLine;
}
It can be seen that it would be too troublesome to perform deep replication for each object like this. We can perform deep replication on the object using serialization/deserialization: serialize the object to the memory before deserialization. In this way, the object is deeply copied:
Public object Clone (){
BinaryFormatter bf = new BinaryFormatter ();
MemoryStream MS = new MemoryStream ();
Bf. Serialize (MS, this );
Ms. Position = 0;

Return (bf. Deserialize (MS ));;
}
Let's do a test:
Class Program {
Static void Main (string [] args ){
RefPoint rPoint = new RefPoint (1 );
ValPoint vPoint = new ValPoint (2 );

RefLine line = new RefLine (rPoint, vPoint );
RefLine newLine = (RefLine) line. Clone ();

Console. WriteLine ("Original line. rPoint. x = {0}", line. rPoint. x );
Console. WriteLine ("Cloned newLine. rPoint. x = {0}", newLine. rPoint. x );

Line. rPoint. x = 10; // change the value of the referenced member of the original object.
Console. WriteLine ("Original line. rPoint. x = {0}", line. rPoint. x );
Console. WriteLine ("Cloned newLine. rPoint. x = {0}", newLine. rPoint. x );
}
}
Output:
Original line. rPoint. x = 1
Cloned newLine. rPoint. x = 1
Original line. rPoint. x = 10
Cloned newLine. rPoint. x = 1
It can be seen that the reference members of the two objects have been separated. Changing the value of the referenced object of the original object does not affect the Copied object.
Note: If you want to serialize an object, the object itself and all its custom members (classes and structures) must be marked using the Serializable feature. Therefore, to run the above Code, the classes we previously defined need to be marked as follows:
[Serializable ()]
Public class RefPoint {/* omitted */}
NOTE: For more information about attributes, see reflection in. Net.
Summary
This article briefly reviews the types in C.
We first discuss two types in C #-value type and reference type, and then briefly review the packing/unpacking operations. Next, we will discuss in detail the object determination in C. Finally, we have discussed the differences between simple replication and deep replication.

Author: "Computer Encyclopedia (only used for technology .."

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.