下面贴代码:
代码如下 | 复制代码 |
using System; using System.Collections.Generic; using System.Text; namespace ClassObjectAndStringDiffDemo { class Program { static void Main(string[] args) { //作为传入参数的变量或对象,以下称呼为‘原变量’或‘原对象’ string str = "123";//"123"是一个对象,相当于String str = new String("123");赋值的过程其实内存地址指向也已改变 Console.WriteLine("/********************************/"); Console.WriteLine("1:传参:均传递的是传递类型的对象值的Copy,"); Console.WriteLine(" 不同的是值类型的Copy的为值,引用类型的Copy的为对象的引用地址"); Console.WriteLine("2:String定义类型为Class ,是引用类型,其他的基本数值类型,为struct,所以为值类型,"); Console.WriteLine(" 所以string的赋值过程为对象的内存交换,"); Console.WriteLine(" string str = "123";//"123"是一个对象,相当于String str = new String("123");"); Console.WriteLine("3:不管传参 的类型是引用类型还是值类型,均可称为按值传递,"); Console.WriteLine(" 因为,传递的是Copy,值类型为值的Copy,引用类型为引用地址的Copy"); Console.WriteLine("4:在传参函数的内部,操作的为Copy,值类型因为操作的为传入变量(值)的副本(新的内存地址),"); Console.WriteLine(" 所以改变的是副本(新的内存地址),与原变量无关,操作的不是指向同一块的内存;"); Console.WriteLine(" 引用类型因为传入的为变量(对象)的地址值的副本,虽然为Copy副本,但是仍然指向原变量的内存地址,"); Console.WriteLine(" 所以副本和原变量指向为同一块内存地址,副本操作的即为原变量的内存地址的内容。"); Console.WriteLine("5:string和自定义的类的对象同作为传入参数,但操作结果不同的原因是:string由于其定义时的某种特殊性,"); Console.WriteLine(" 即声明初始化与常规类不同,不需要构造函数去构造,所以在表现上的不同,隐藏了它的实质是与同为引用"); Console.WriteLine(" 类型的常规类的相同的特性,string的赋值过程,即是把一个string对象的内存地址给予了传入的copy副本"); Console.WriteLine(" 即相同于PerSon类的对象实例,副本ps1,新对象ps2,ps1=ps2,这样之后操作ps1对象字段,其实操作的ps1也就不是原来的副本,"); Console.WriteLine(" 也就不是传入原对象的副本内存地址了,而是新的ps2的内存地址指向,这样就不能起作用了"); Console.WriteLine("/********************************/"); Console.WriteLine("Str初始化值"); Console.WriteLine("Str:{0}",str); ChangeString(str); Console.WriteLine("Str做为不带修饰参数传递,为按地址(引用)传递"); Console.WriteLine("Str:{0}", str); ChangeString(ref str); Console.WriteLine("Str带ref修饰传递,为按地址(引用)传递"); Console.WriteLine("Str:{0}",str); PerSon ps = new PerSon("111"); Console.WriteLine("Person初始化name值"); Console.WriteLine("Person.name:{0}",ps.name); ChangePerSonNew(ps); Console.WriteLine("Person对象做为不带修饰参数传递,为按地址(引用)传递,改值方式不同下"); Console.WriteLine("Person.name:{0}", ps.name); ChangePerSonNew(ref ps); Console.WriteLine("Person对象做为不带修饰参数传递,为按地址(引用)传递,改值方式不同下"); Console.WriteLine("Person.name:{0}", ps.name); ChangePerSonNew2(ref ps); Console.WriteLine("Person对象做为不带修饰参数传递,为按地址(引用)传递,改值方式不同下"); Console.WriteLine("Person.name:{0}", ps.name); ChangePerSon(ps); Console.WriteLine("Person对象做为不带修饰参数传递,为按地址(引用)传递,改值方式不同上"); Console.WriteLine("Person.name:{0}",ps.name); int k = 1; Console.WriteLine("k初始化值"); Console.WriteLine("k:{0}", k); ChangeVal(k); Console.WriteLine("k不带修饰参数传递,按值传递"); Console.WriteLine("k:{0}",k); ChangeVal(ref k); Console.WriteLine("k带ref修饰参数传递,按引用传递"); Console.WriteLine("k:{0}", k); Console.ReadLine(); } /// /// /// /// 引用类型,ref作用相当于指针,这样传入的为原变量的地址,原变量必须先初始化 static void ChangeString(ref string s) { //ref 起的作用,个人根据结果猜测,应该是保持s副本的内存地址引用在赋值过程不改变,只改变值 s = "465";//s此时指向的仍为原变量的内存地址,但值改变,副本的地址指向为原变量的内存,而值改变,那么就改变的原变量的指向内存的内容 //此情况相当于下面ChangePerSon(PerSon p) } /// /// /// /// 引用类型,此处传入的也为原变量的内存地址 static void ChangeString(string s) { s = "456";//此处s副本传入时指向的为原变量的内存地址,此时赋值后,指向的为‘456’的内存地址,副本的内存指向变了,不影响原变量的内存内容 //此情况相当于下面ChangePerSonNew(PerSon pson) //此就相当于string赋值时,隐藏看不见的代码 //string str = new string("456"); //假设string类有一个字段,'456'就相当于相当于string类的那个字段的值 //s=str;s对象指向str对象。只是副本的更改,不涉及原对象的更改 } //由于string类型比较特殊,看下面的值类型比较容易看出区别 static void ChangeVal(int i) { i = 2;//此处i传入的为原变量的值的副本(新的内存地址,保存的原变量的值),所以改变副本i的值,不影响原变量内存的内容 //所以结果无变化 } static void ChangeVal(ref int i) { i = 3;//此处传入的为原变量的内存地址的副本(新的内存地址,保存的原变量的内存地址,即新的内存地址指向原变量的内存地址) //此时修改值,即修改的为的原变量的内存中保存的内容的值,所以会结果会有变化 } /// /// /// /// 引用类型,传入原对象的内存地址的副本 static void ChangePerSon(PerSon p) { p.name = "000";//name字段的内存指向变为‘000’的内存指向,但p的内存指向未变(原对象的地址),所以,p.name 指向的为‘000’的内存地址 //所以表现的出来的结果是值变了,其实name 指向的内存已不是原有的那块内存 } /// /// /// /// 引用类型,传入原对象的内存地址的副本 static void ChangePerSonNew(PerSon pson) { PerSon pso = new PerSon(); pso.name = "222"; pson = pso;//pson副本的内存指向已改变为pso的内存地址,所以不影响原变量的内存中的内容 } /// /// /// /// 引用类型,传入原对象的内存地址的副本 static void ChangePerSonNew(ref PerSon pson) { PerSon pso = new PerSon(); pso.name = "333"; pson = pso;//ref 有保持内存地址不改变,只改变值的作用,对象包括字段 } /// /// /// /// 引用类型,传入原对象的内存地址的副本 static void ChangePerSonNew2(ref PerSon pson) { PerSon pso = new PerSon(); pso.name = "444"; pson.name = pso.name;// pson = pso;//ref 有保持内存地址不改变,只改变值的作用 } sealed class PerSon { public string name; public PerSon(string n) { this.name = n; } public PerSon() { } } } } |
大家看完后给点意见,帮我修正下,当然如果有更通俗易懂,而且专业的说法,请大家踊跃分享哈,比较本文中个人