详解值类型与引用类型 结合ref (利用案例一步步分析内存的变化)

储存数组其实不是对象,而是对象的引用地址(储存对象的计算机的内存地址)
值类型,当方法调用的时候,因为传递进 方法的值只是原变量的一个副本,所以改变了副本不会改变原变量。
引用类型,当方法调用的时候,因为传递进方法的也是一个副本,只不过是 引用的副本,这个引用的副本也是指向 原对象的,所以修改了引用副本就会影响到对象。

如果结合这个案例,彻底把这个过程分析清楚:
分析案例:

class Program
{
static void Main(string[] args)
{
int[] ints = { 1, 2, 3 };
int[] intsCopy = ints;//复制数组,使变量引用内存中相同的数组对象
Change(ints);//调用方法,传入引用
//ints :2 4 6
//intsCopy:2 4 6
int[] ints_second = { 1, 2, 3 };
int[] ints_secondCopy = ints_second;
ChangeAndRef(ref ints_second);//注意ref传入方法的引用是哪一个,那么 该引用就指向方法体内的新对象。


//ints_second:11 12 13
//ints_secondCopy:2 4 6
}
static void Change(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i] *= 2;
}
array = new int[] { 11, 12, 13 };
}
static void ChangeAndRef(ref int[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i] *= 2;
}
array = new int[] { 11, 12, 13 };
}


第一步:int[] ints;这个是声明一个引用变量,这个时候不开辟内存
第二步:给数组初始化或者赋值,就会开辟内存,这里的开辟内存是值 用引用变量---指向--内存的地址--这个地址上有 一个对象(对象是占用内存的)
第三步:int[] intsCopy=ints;这是 声明一个 引用变量,让这个变量也---指向--内存中的地址---对象
第四步:调用方法,这个时候注意了,引用类型是把引用变量的副本信息,传递进方法里,这个引用变量的副本---对应---对象
第五步:因为引用副本所对应的地址也是内存中的对象,所以会修改对象,因此 原引用变量---对应内存中地址的对象(改变)
第六步:因为在方法体内 new 了一个新对象,就是开辟了一个新内存,那么方法体的引用变量是可以 指向这块内存的地址的,但是方法体外部的引用对象是无法指向这个方法体内的对象的,所以我们看到 方法1新 new int[] 未能改变引用变量--对应的对象

核心:

第二个方法体:利用ref 使得 引用了引用变量,也就是说修改了引用变量指向的对象。所以array=new int[],改变了原来的ints_second的引用--对象,但是并未修改ints_secondCopy的引用变量---所对应的对象。这个方法体:1.改变了原Ints_second的对象  2.在方法体内new一块新内存,让ints_second 指向了这块新内存上的对象

值类型:
案例1:(void情况)

class Program
{
static void Main(string[] args)
{
int i = 5;//这里是Int类型所以是值类型
Console.WriteLine("The current i is {0}",i);//5

Change(i);
Console.WriteLine("The now i is {0}",i);//5 ,这里可以看出值类型不会改变原变量的值
}


static void Change(int i)
{
i *= 2;
Console.WriteLine("The i is {0}",i);//10
}
}


 如果使用ref参数的话,那么方法体传递的就是对i的引用,那么就是会修改原i所对应的值的。


案例2:(有返回值的情况)

 class Program
{
static void Main(string[] args)
{
int i = 5;//这里是Int类型所以是值类型
Console.WriteLine("The current i is {0}", i);//5

int new_i=Change(i);
Console.WriteLine("The now i is {0}", i);//5 ,这里可以看出值类型不会改变原变量的值
Console.WriteLine("The new_i is {0}",new_i);//10,这里可以看出值类型的是把副本的值改变了,其实并没有改变原变量的值
}
static int Change(int i)
{
i *= 2;
Console.WriteLine("The i is {0}", i);//10
return i;
}
}

引用类型:
案例3:(void情况)

class Program
{
static void Main(string[] args)
{
int[] ints = { 1, 2, 3, 4, 5 };
Console.WriteLine("Lengths is {0}", ints.Length);//5
Console.WriteLine("The current ints is:");
foreach (int i in ints)
{
Console.Write(i + ""); // 1 2 3 4 5
}

int[] intsCopy = ints;//复制一份给intsCopy

Console.WriteLine();
ChangeArray(ints);
Console.WriteLine("The Now ints is:"); // 2 4 6 8 10,这里不一样了,变量引用的对象改变了!!
foreach (int i in ints)
{
Console.Write(i + "");
}

Console.WriteLine("The Now intsCopy is :");
foreach (int i in intsCopy)
{
Console.Write(i + "");//2 4 6 8 10 ,注意!!这里是原变量的复制版本居然也改变了,说明引用类型被方法调用修改是修改原变量的。
}
}
static void ChangeArray(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = array[i] * 2;
}
}
}

案例4:(有返回值)

class Program
{
static void Main(string[] args)
{
int[] ints = { 1, 2, 3, 4, 5 };
Console.WriteLine("Lengths is {0}", ints.Length);//5
Console.WriteLine("The current ints is:");
foreach (int i in ints)
{
Console.Write(i + ""); // 1 2 3 4 5
}
Console.WriteLine();
int[] intsCopy = ints;//复制一份给intsCopy
int[] new_ints = ChangeArray(ints);
Console.WriteLine("The new_ints is:");
foreach (int i in new_ints)
{
Console.Write(i + "");//2 4 6 8 10
}
Console.WriteLine();
ChangeArray(ints);
Console.WriteLine("The Now ints is:"); // 4 8 12 16 20
foreach (int i in ints)
{
Console.Write(i + "");
}
Console.WriteLine();
Console.WriteLine("The Now intsCopy is :");
foreach (int i in intsCopy)
{
Console.Write(i + "");//4 8 12 16 20
}

}
static int[] ChangeArray(int[] array)//定义方法修改数组
{
for (int i = 0; i < array.Length; i++)
{
array[i] = array[i] * 2;
}
return array;
}
}

运行结果:
Lengths is 5
The current ints is:
1 2 3 4 5
The new_ints is:
2 4 6 8 10
The Now ints is:
4 8 12 16 20
The Now intsCopy is :
4 8 12 16 20


 

原文地址:https://www.cnblogs.com/IAmBetter/p/2289584.html