您的位置:首页 > 编程语言 > C#

C# ref深入理解

2013-07-26 14:59 281 查看
做.NET开发的人,肯定对ref和out关键字非常了解:

ref功能:

ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。简单点说就是,使用了refout的效果就几乎和C中使用了指针变量一样。它能够让你直接对原数进行操作,而不是对那个原数的Copy进行操作。

若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。例如:
class RefExample
{
static void Method(ref int i)
{
i = 44;
}
static void Main()
{
int val = 0;
Method(ref val); // val is now 44
}
}
使用时注意:

传递到 ref 参数的参数必须最先初始化。这与 out 不同,out 的参数在传递之前不需要显式初始化。
尽管 refout 在运行时的处理方式不同,但它们在编译时的处理方式是相同的。因此,如果一个方法采用 ref 参数,而另一个方法采用 out 参数,则无法重载这两个方法。
例如,从编译的角度来看,以下代码中的两个方法是完全相同的,因此将不会编译以下代码:
class CS0663_Example
{
// compiler error CS0663: "cannot define overloaded
// methods that differ only on ref and out"
public void SampleMethod(ref int i)
{
}
public void SampleMethod(out int i)
{
}
}
但是,如果一个方法采用 refout 参数,而另一个方法不采用这两类参数,则可以进行重载,如下所示:
class RefOutOverloadExample
{
public void SampleMethod(int i)
{
}
public void SampleMethod(ref int i)
{
}
}
以上的知识相信大家都能理解,也没有什么问题,下面思考如下的两个案例的输出结果为?
案例1:
class Program
{
static void Main(string[] args)
{
int[] arr = {100,200,300};
Console.WriteLine("调有方法前:");
foreach (int item in arr)
{
Console.Write(item+"\t");
}
Console.WriteLine("\n------------------------------------");
//调用方法
RefDemo1(arr);
Console.WriteLine("调有方法后:");
foreach (int item in arr)
{
Console.Write(item+"\t");
}
Console.WriteLine();
}
static void RefDemo1(int[] parr)
{
parr = new int[5] { 1, 2, 3, 4, 5 };
}
}

问:以上的案例1的输出结果是?
案例2:
class Program
{
static void Main(string[] args)
{
int[] arr =
Console.WriteLine("调有方法前:");
foreach (int item in arr)
{
Console.Write(item+"\t");
}
Console.WriteLine("\n------------------------------------");
//调用方法
RefDemo2(ref arr);
Console.WriteLine("调有方法后:");
foreach (int item in arr)
{
Console.Write(item+"\t");
}
Console.WriteLine();
}
static void RefDemo2(ref int[] parr)
{
parr = new int[5] { 1, 2, 3, 4, 5 };
}
}
问:以上的案例2的输出结果是?
下面对以上的两个案例进行分析:
案例1答案:

调有方法前:
100 200 300
------------------------------------
调有方法后:
100 200 300
请按任意键继续. . .

分析结果:对于案例1,首先 {100,200,300};在堆中划分内存空间,假设开辟的内存空间地址为0X7799,则栈中的arr中存放0X7799,即,arr指向堆中开辟的内存空间,调用 RefDemo1方法时,给形参在栈中开辟内存空间,并传arr的内容到parr中,这样arr和parr中的内容都是0X7799,即都指向堆中的同一空间,后面执行parr = new int[5] { 1, 2, 3, 4, 5 };时,因为new会重新开辟新的内存空间,这样导致parr重新赋了新的地址,这样parr中的内容与arr中的内容已经不是同一个地址,但是arr中的内容并没有发生变化,还指向最初在堆中开辟的空间,所以打印前后结果是一样的,如上面结果所示。

案例2答案:

调有方法前:
100 200 300
------------------------------------
调有方法后:
1 2 3 4 5
请按任意键继续. . .

分析结果:对于案例2,首先 {100,200,300};在堆中划分内存空间,假设开辟的内存空间地址为0X7799,则栈中的arr中存放0X7799,即,arr指向堆中开辟的内存空间,调用 RefDemo1方法时,因为是ref传参,这样arr就有了一个别名叫parr(即在栈中不再开辟新的空间给parr,也就是arr和parr的地址是一样的,内容也是一样的,且都指向刚开辟的堆中的空间),后面执行parr = new int[5] { 1, 2, 3, 4, 5 };时,因为new会重新开辟新的内存空间,这样导致parrt和arr重新赋了新的地址,这样parr中的内容与arr中的内容也是同一个地址,它们一起指向新开辟的堆中的空间,所以打印前后结果是一不样的,如上面结果所示。

总结:ref的参数传递,形参的改变一定会影响到实参。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: