A deep understanding of C #'s value passing and reference passing calls,
Value Transfer and Reference call are related to almost all mainstream languages. Next I will talk about my understanding of value transfer and reference transfer calls in C.
1. General understanding of value transfer and reference transfer in C #
- If the passed parameter is a primitive type (int, float, etc.) or struct (struct), it is called to pass the value.
- If the passed parameter is a class, it is a reference call.
- If the input parameter contains the ref or out keyword, it is called by reference.
The verification sample code is as follows:
View sourceprint?
01 using System;
02
03 public class ArgsByRefOrValue
04 {
05 public static void Main (string [] args)
06 {
07 // Experiment 1. Call for passing values-primitive type
08 int I = 10;
09 Console. WriteLine ("before call ChangeByInt: I =" + I. ToString ());
10 ChangeByInt (I );
11 Console. WriteLine ("after call ChangeByInt: I =" + I. ToString ());
12
13 Console. writeLine ("========================================== ========= ");
14 // Experiment 2. Call value transfer-struct
15 Person_val p_val = new Person_val ();
16 p_val.name = "old val name ";
17 Console. WriteLine ("before call ChangeByStruct: p_val.name =" + p_val.name );
18 ChangeByStruct (p_val );
19 Console. WriteLine ("after call ChangeByStruct: p_val.name =" + p_val.name );
20
21 Console. writeLine ("========================================== ========= ");
22 // experiment 3. Upload Reference call-class
23 Person_ref p_ref = new Person_ref ();
24 p_ref.name = "old ref name ";
25 Console. WriteLine ("before call ChangeByClass: p_ref.name =" + p_ref.name );
26 ChangeByClass (p_ref );
27 Console. WriteLine ("after call ChangeByClass: p_ref.name =" + p_ref.name );
28
29 Console. writeLine ("========================================== ========= ");
30 // Experiment 4. Upload Reference call -- use ref
31 Person_ref p = new Person_ref ();
32 p. name = "old ref name ";
33 Console. WriteLine ("before call ChangeByClassRef: p. name =" + p. name );
34 ChangeByClassRef (ref p );
35 Console. WriteLine ("after call ChangeByClassRef: p. name =" + p. name );
36
37 Console. ReadKey (true );
38}
39
40 static void ChangeByInt (int I)
41 {
42 I = I + 10;
43 Console. WriteLine ("when calling ChangeByInt: I =" + I. ToString ());
44}
45
46 static void ChangeByStruct (Person_val p_val)
47 {
48 p_val.name = "new val name ";
49 Console. WriteLine ("when calling ChangeByStruct: p_val.name =" + p_val.name );
50}
51
52 static void ChangeByClass (Person_ref p_ref)
53 {
54 p_ref.name = "new ref name ";
55 Console. WriteLine ("when calling ChangeByClass: p_ref.name =" + p_ref.name );
56}
57
58 static void ChangeByClassRef (ref Person_ref p)
59 {
60 p. name = "new ref name ";
61 Console. WriteLine ("when calling ChangeByClassRef: p. name =" + p. name );
62}
63}
64
65 public struct Person_val
66 {
67 public string name;
68}
69
70 public class Person_ref
71 {
72 public string name;
73}
The running result is as follows:
It seems that the above Code isLab 3AndLab 4Is the same, that is, for a class, no matter the ref or out is added, it is a reference call.
In fact, this is only a superficial phenomenon. If you change the code a bit, the results will be different.
Modify the code above and add two more experiments.
001 using System;
002
003 public class ArgsByRefOrValue
004 {
005 public static void Main (string [] args)
006 {
007 // Experiment 1. Call for passing values -- primitive type
008 int I = 10;
009 Console. WriteLine ("before call ChangeByInt: I =" + I. ToString ());
010 ChangeByInt (I );
011 Console. WriteLine ("after call ChangeByInt: I =" + I. ToString ());
012
013 Console. writeLine ("========================================== ========= ");
014 // Experiment 2. Call value transfer-struct
015 Person_val p_val = new Person_val ();
016 p_val.name = "old val name ";
017 Console. WriteLine ("before call ChangeByStruct: p_val.name =" + p_val.name );
018 ChangeByStruct (p_val );
019 Console. WriteLine ("after call ChangeByStruct: p_val.name =" + p_val.name );
020
021 Console. writeLine ("========================================== ========= ");
022 // experiment 3. Upload Reference call-class
023 Person_ref p_ref = new Person_ref ();
024 p_ref.name = "old ref name ";
025 Console. WriteLine ("before call ChangeByClass: p_ref.name =" + p_ref.name );
026 ChangeByClass (p_ref );
027 Console. WriteLine ("after call ChangeByClass: p_ref.name =" + p_ref.name );
028
029 Console. writeLine ("========================================== ========= ");
030 // Experiment 4. Upload Reference call -- use ref
031 Person_ref p = new Person_ref ();
032 p. name = "old ref name ";
033 Console. WriteLine ("before call ChangeByClassRef: p. name =" + p. name );
034 ChangeByClassRef (ref p );
035 Console. WriteLine ("after call ChangeByClassRef: p. name =" + p. name );
036
037 Console. writeLine ("========================================== ========= ");
038 // Experiment 5. Upload Reference call -- the Class re-creates an object in the called Function
039 Person_ref p_ref_new = new Person_ref ();
040 p_ref_new.name = "old new ref name ";
041 Console. WriteLine ("before call ChangeByClassNew: p_ref_new.name =" + p_ref_new.name );
042 ChangeByClassNew (p_ref_new );
043 Console. WriteLine ("after call ChangeByClassNew: p_ref_new.name =" + p_ref_new.name );
044
045 Console. writeLine ("========================================== ========= ");
046 // Experiment 6. Upload Reference call -- use ref to re-create an object in the called Function
047 Person_ref p_new = new Person_ref ();
048 p_new.name = "old new ref name ";
049 Console. WriteLine ("before call ChangeByClassRefNew: p_new.name =" + p_new.name );
050 ChangeByClassRefNew (ref p_new );
051 Console. WriteLine ("after call ChangeByClassRefNew: p_new.name =" + p_new.name );
052
053 Console. ReadKey (true );
054}
055
056 static void ChangeByInt (int I)
057 {
058 I = I + 10;
059 Console. WriteLine ("when calling ChangeByInt: I =" + I. ToString ());
060}
061
062 static void ChangeByStruct (Person_val p_val)
063 {
064 p_val.name = "new val name ";
065 Console. WriteLine ("when calling ChangeByStruct: p_val.name =" + p_val.name );
066}
067
068 static void ChangeByClass (Person_ref p_ref)
069 {
070 p_ref.name = "new ref name ";
071 Console. WriteLine ("when calling ChangeByClass: p_ref.name =" + p_ref.name );
072}
073
074 static void ChangeByClassRef (ref Person_ref p)
075 {
076 p. name = "new ref name ";
077 Console. WriteLine ("when calling ChangeByClassRef: p. name =" + p. name );
078}
079
080 static void ChangeByClassNew (Person_ref p_ref_new)
081 {
082 p_ref_new = new Person_ref ();
083 p_ref_new.name = "new ref name ";
084 Console. WriteLine ("when calling ChangeByClassNew: p_ref_new.name =" + p_ref_new.name );
085}
086
087 static void ChangeByClassRefNew (ref Person_ref p_new)
088 {
089 p_new = new Person_ref ();
090 p_new.name = "new ref name ";
091 Console. WriteLine ("when calling ChangeByClassRefNew: p_new.name =" + p_new.name );
092}
093}
094
095 public struct Person_val
096 {
097 public string name;
098}
099
100 public class Person_ref
101 {
102 public string name;
103}
The running result is:
Lab 5Even if the parameter is a class, if no ref is added, it is also called.
The following introduces my understanding.
2. If there is no ref, even if the parameter is of the reference type (class), it can be regarded as a value passing call.
When the parameter is of the reference type, a copy of the address of the reference type is passed. "A copy of the address of the reference type" is the "value" of the value transfer call ".
Note that what is passed here is the address of this reference typeOne copyInstead of the address of the reference type.
The following figure illustrates the memory usage in experiment 3, Experiment 5, and Experiment 6.
2.1 First, experiment 3
The memory figure in experiment 3 is as follows. The real parameter is the Person_ref object outside the ChangeByClass function, and the form parameter is the Person_ref object in the ChangeByClass function.
We can see that after the new arguments are passed out, the memory is allocated on the managed stack, and the object pointer is saved on the stack.
After the ChangeByClass function is called,Real parameter p_valCopy a copyParametersNote:P_val (real parameter)AndP_val (parameter)Is to point to the same address on the managed stack.
SoWhen no ref is available, even if the parameter is of the reference type (class), it can be regarded as a value passing call.The value here is the address of the object in the managed heap (0x1000 ).
After the ChangeByClass function is calledP_val (parameter)Modified the value of the name attribute becauseP_val (real parameter)AndP_val (parameter)Is pointing to the same address on the hosting stack, soP_val (real parameter)The name attribute is also modified.
2.2 and then Experiment 5
From the execution results of experiment 3 above, it seems that the reference call is passed, because the change of the form parameter leads to the change of the real parameter.
The following Experiment 5 shows that,P_val (parameter)AndP_val (real parameter)Not the same variable,P_val (real parameter).
We can see that the first step is the same as experiment 3, but it is different after the ChangeByClassNew function is called.
In the ChangeByClassNew functionP_val (parameter)The memory is re-allocated (new Operation) so that it points to the new address (0x1100), for example:
SoP_val (parameter)When the name attribute of is changed,P_val (real parameter)The name attribute of is still unchanged.
2.3 Experiment 6
I think Experiment 6 is a real reference transfer call. This is the first image.
After the ref keyword is added to the parameter, the address of the object in the managed heap (0x1000) is not actually passed, but on the stack.P_val (real parameter)(0x0001 ).
So here the real parameters and the form parameters are the same thing on the stack, there is no difference. I think this is the real transfer Reference call.
Then the ChangeByClassRefNew function is called.P_val (parameter)The memory is re-allocated (new Operation) so that it points to the new address (0x1100 ).
BecauseP_val (parameter)YesP_val (real parameter), SoP_val (parameter)After the name attribute of is changedP_val (real parameter)The name attribute is also changed.
The previously allocated object (address 0x1000) has not been referenced and will be recycled by GC at any time.
3. Conclusion
- If the passed parameter is a primitive type (int, float, etc.) or struct (struct), it is called to pass the value.
- If the input parameter contains the ref or out keyword, it is called by reference.
- If the passed parameter is a class and there is no ref or out Keyword: