C# 方法传参时,传入值类型、引用类型、string引用类型、ref修饰参数时分析

由于基础不够扎实啊,最近又翻出一个基础知识的旧账出来解惑了。

起因是因为突然发现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 }

大家看完后给点意见,帮我修正下,当然如果有更通俗易懂,而且专业的说法,请大家踊跃分享哈,比较本文中个人

猜测的语言比较多,没办法,没有时间去查证更多的高深知识。

 

敬请专家指证.

posted on 2012-01-14 11:08  未来证明现在  阅读(3042)  评论(4编辑  收藏  举报

导航