由於基礎不夠扎實啊,最近又翻出一個基礎知識的舊賬出來解惑了。
起因是因為突然發現string和自定義類同為引用類型,作為傳入參數,在改變值方面有所不同,疑惑。
所以個人研究了下,查證資料。發現,果然是一千個人一千個哈姆雷特。最后總結加個人理解,不知正確與否。
但總歸是自己能把結果給理順了,分析或者解釋不正確那可能就是個人的見識不夠了,大家可以觀評下。給點意見。
下面貼代碼:
View Code
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace ClassObjectAndStringDiffDemo
6 {
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //作為傳入參數的變量或對象,以下稱呼為‘原變量’或‘原對象’
12 string str = "123";//"123"是一個對象,相當於String str = new String("123");賦值的過程其實內存地址指向也已改變
13
14 Console.WriteLine("/********************************/");
15 Console.WriteLine("1:傳參:均傳遞的是傳遞類型的對象值的Copy,");
16 Console.WriteLine(" 不同的是值類型的Copy的為值,引用類型的Copy的為對象的引用地址");
17 Console.WriteLine("2:String定義類型為Class ,是引用類型,其他的基本數值類型,為struct,所以為值類型,");
18 Console.WriteLine(" 所以string的賦值過程為對象的內存交換,");
19 Console.WriteLine(" string str = \"123\";//\"123\"是一個對象,相當於String str = new String(\"123\");");
20 Console.WriteLine("3:不管傳參 的類型是引用類型還是值類型,均可稱為按值傳遞,");
21 Console.WriteLine(" 因為,傳遞的是Copy,值類型為值的Copy,引用類型為引用地址的Copy");
22 Console.WriteLine("4:在傳參函數的內部,操作的為Copy,值類型因為操作的為傳入變量(值)的副本(新的內存地址),");
23 Console.WriteLine(" 所以改變的是副本(新的內存地址),與原變量無關,操作的不是指向同一塊的內存;");
24 Console.WriteLine(" 引用類型因為傳入的為變量(對象)的地址值的副本,雖然為Copy副本,但是仍然指向原變量的內存地址,");
25 Console.WriteLine(" 所以副本和原變量指向為同一塊內存地址,副本操作的即為原變量的內存地址的內容。");
26 Console.WriteLine("5:string和自定義的類的對象同作為傳入參數,但操作結果不同的原因是:string由於其定義時的某種特殊性,");
27 Console.WriteLine(" 即聲明初始化與常規類不同,不需要構造函數去構造,所以在表現上的不同,隱藏了它的實質是與同為引用");
28 Console.WriteLine(" 類型的常規類的相同的特性,string的賦值過程,即是把一個string對象的內存地址給予了傳入的copy副本");
29 Console.WriteLine(" 即相同於PerSon類的對象實例,副本ps1,新對象ps2,ps1=ps2,這樣之后操作ps1對象字段,其實操作的ps1也就不是原來的副本,");
30 Console.WriteLine(" 也就不是傳入原對象的副本內存地址了,而是新的ps2的內存地址指向,這樣就不能起作用了");
31 Console.WriteLine("/********************************/");
32
33 Console.WriteLine("Str初始化值");
34 Console.WriteLine("Str:{0}",str);
35
36 ChangeString(str);
37 Console.WriteLine("Str做為不帶修飾參數傳遞,為按地址(引用)傳遞");
38 Console.WriteLine("Str:{0}", str);
39
40 ChangeString(ref str);
41 Console.WriteLine("Str帶ref修飾傳遞,為按地址(引用)傳遞");
42 Console.WriteLine("Str:{0}",str);
43
44 PerSon ps = new PerSon("111");
45
46 Console.WriteLine("Person初始化name值");
47 Console.WriteLine("Person.name:{0}",ps.name);
48
49 ChangePerSonNew(ps);
50 Console.WriteLine("Person對象做為不帶修飾參數傳遞,為按地址(引用)傳遞,改值方式不同下");
51 Console.WriteLine("Person.name:{0}", ps.name);
52
53 ChangePerSonNew(ref ps);
54 Console.WriteLine("Person對象做為不帶修飾參數傳遞,為按地址(引用)傳遞,改值方式不同下");
55 Console.WriteLine("Person.name:{0}", ps.name);
56
57 ChangePerSonNew2(ref ps);
58 Console.WriteLine("Person對象做為不帶修飾參數傳遞,為按地址(引用)傳遞,改值方式不同下");
59 Console.WriteLine("Person.name:{0}", ps.name);
60
61 ChangePerSon(ps);
62 Console.WriteLine("Person對象做為不帶修飾參數傳遞,為按地址(引用)傳遞,改值方式不同上");
63 Console.WriteLine("Person.name:{0}",ps.name);
64
65
66 int k = 1;
67
68 Console.WriteLine("k初始化值");
69 Console.WriteLine("k:{0}", k);
70
71 ChangeVal(k);
72 Console.WriteLine("k不帶修飾參數傳遞,按值傳遞");
73 Console.WriteLine("k:{0}",k);
74
75 ChangeVal(ref k);
76 Console.WriteLine("k帶ref修飾參數傳遞,按引用傳遞");
77 Console.WriteLine("k:{0}", k);
78
79 Console.ReadLine();
80 }
81 /// <summary>
82 ///
83 /// </summary>
84 /// <param name="s">引用類型,ref作用相當於指針,這樣傳入的為原變量的地址,原變量必須先初始化</param>
85 static void ChangeString(ref string s)
86 {
87 //ref 起的作用,個人根據結果猜測,應該是保持s副本的內存地址引用在賦值過程不改變,只改變值
88 s = "465";//s此時指向的仍為原變量的內存地址,但值改變,副本的地址指向為原變量的內存,而值改變,那么就改變的原變量的指向內存的內容
89
90 //此情況相當於下面ChangePerSon(PerSon p)
91 }
92
93 /// <summary>
94 ///
95 /// </summary>
96 /// <param name="s">引用類型,此處傳入的也為原變量的內存地址</param>
97 static void ChangeString(string s)
98 {
99 s = "456";//此處s副本傳入時指向的為原變量的內存地址,此時賦值后,指向的為‘456’的內存地址,副本的內存指向變了,不影響原變量的內存內容
100
101 //此情況相當於下面ChangePerSonNew(PerSon pson)
102
103 //此就相當於string賦值時,隱藏看不見的代碼
104 //string str = new string("456");
105 //假設string類有一個字段,'456'就相當於相當於string類的那個字段的值
106 //s=str;s對象指向str對象。只是副本的更改,不涉及原對象的更改
107 }
108 //由於string類型比較特殊,看下面的值類型比較容易看出區別
109
110 static void ChangeVal(int i)
111 {
112 i = 2;//此處i傳入的為原變量的值的副本(新的內存地址,保存的原變量的值),所以改變副本i的值,不影響原變量內存的內容
113 //所以結果無變化
114 }
115
116 static void ChangeVal(ref int i)
117 {
118 i = 3;//此處傳入的為原變量的內存地址的副本(新的內存地址,保存的原變量的內存地址,即新的內存地址指向原變量的內存地址)
119 //此時修改值,即修改的為的原變量的內存中保存的內容的值,所以會結果會有變化
120 }
121
122
123 /// <summary>
124 ///
125 /// </summary>
126 /// <param name="p">引用類型,傳入原對象的內存地址的副本</param>
127 static void ChangePerSon(PerSon p)
128 {
129 p.name = "000";//name字段的內存指向變為‘000’的內存指向,但p的內存指向未變(原對象的地址),所以,p.name 指向的為‘000’的內存地址
130 //所以表現的出來的結果是值變了,其實name 指向的內存已不是原有的那塊內存
131 }
132 /// <summary>
133 ///
134 /// </summary>
135 /// <param name="p">引用類型,傳入原對象的內存地址的副本</param>
136 static void ChangePerSonNew(PerSon pson)
137 {
138 PerSon pso = new PerSon();
139 pso.name = "222";
140 pson = pso;//pson副本的內存指向已改變為pso的內存地址,所以不影響原變量的內存中的內容
141 }
142
143 /// <summary>
144 ///
145 /// </summary>
146 /// <param name="p">引用類型,傳入原對象的內存地址的副本</param>
147 static void ChangePerSonNew(ref PerSon pson)
148 {
149 PerSon pso = new PerSon();
150 pso.name = "333";
151 pson = pso;//ref 有保持內存地址不改變,只改變值的作用,對象包括字段
152 }
153
154 /// <summary>
155 ///
156 /// </summary>
157 /// <param name="p">引用類型,傳入原對象的內存地址的副本</param>
158 static void ChangePerSonNew2(ref PerSon pson)
159 {
160 PerSon pso = new PerSon();
161 pso.name = "444";
162 pson.name = pso.name;//
163 pson = pso;//ref 有保持內存地址不改變,只改變值的作用
164 }
165
166 sealed class PerSon
167 {
168 public string name;
169 public PerSon(string n)
170 {
171 this.name = n;
172 }
173 public PerSon()
174 { }
175 }
176 }
177 }
大家看完后給點意見,幫我修正下,當然如果有更通俗易懂,而且專業的說法,請大家踴躍分享哈,比較本文中個人
猜測的語言比較多,沒辦法,沒有時間去查證更多的高深知識。
敬請專家指證.
