今天在看一段C#代码的时候,发现对ref和out两个关键不了解,所以就查阅了C#语言相关的教程,深入的学习了一下。
ref关键字的作用是使参数按引用类型传递,这样控制权传递给调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。
这里提到了引用类型,其实在C#中共有两种类型,一种是值类型(结构体、数值类型、bool型、枚举、可空类型),另一种是引用类型(自定义类、接口、委托、数组、字符串类型、object)。值类型和引用类型的区别在于以下几点:
1、值类型直接存储其值,变量本身就包含了实例数据,而引用类型保存的只是实例数据的内存引用。因此,一个值类型变量就永远不会影响到其他的值类型变量,而两个引用类型变量则很有可能指向同一地址,从而发生相互影响。
2、从内存分配上来看,值类型通常分配在线程的堆栈上,作用域结束时,所占空间自行释放,效率高,无需进行地址转换,而引用类型通常分配在托管堆上,由GC来控制其回收,需要进行地址转换,效率降低,这也正是c#需要定义两种数据类型的原因之一。
3、值类型均隐式派生自System.ValueType,而System.ValueType又直接派生于 System.Object,每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值,注意所有的值类型都是密封(sealed)的,所以无法派生 出新的值类型。而且System.ValueType本身是一个类类型,而不是值类型,因为它重写了object的Equals()方法,所以对值类型将 按照实例的值来比较,而不是比较引用地址。
4、C# 的统一类型系统,使得值类型可以转化为对象来处理,这就是常说的装箱和拆箱。由于装拆箱需要装建全新对象或做强制类型转换,这些操作所需时间和运算要远远大于赋值操作,因此不提倡使用它,同时也要尽量避免隐式装拆箱的发生。
理解了C#中值类型与引用类型的区别再来看ref关键字就会非常简单了。下面通过一个小例子来说明ref关键字的作用。
下面这段代码的功能是实现两个整数按由小到大的顺序进行排序。
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ref1
{
class Program
{
/// <summary>
/// 从小到大进行排序
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
public static void sawp(int a, int b)
{
int c = 0;
if (a > b)
{
c = a;
a = b;
b = c;
}
Console.WriteLine("a的值为:{0},b的值为:{1}", a, b);
}
static void Main(string[] args)
{
int x = 6;
int y = 4;
sawp(x, y);
Console.WriteLine("x的值为:{0},y的值为:{1}", x, y);
Console.ReadLine();
}
}
}
运行结果如下:
程序运行的结果出人预料,仔细分析后就会发现,swap函数的参数是通过值传递的,它只是将x、y的副本值进行了交换,而内存中的值是没有改变的。现在我们在参数的前面加上ref关键字。
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ref1
{
class Program
{
/// <summary>
/// 从小到大进行排序
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
public static void sawp(ref int a,ref int b)
{
int c = 0;
if (a > b)
{
c = a;
a = b;
b = c;
}
}
static void Main(string[] args)
{
int x = 6;
int y = 4;
sawp(ref x,ref y);
Console.WriteLine("x的值为:{0},y的值为:{1}", x, y);
Console.ReadLine();
}
}
运行结果如下:
可以看到,程序给出了正确的结果。下面来说说out关键字,out关键字的功能跟ref关键字的功能很相似,out关键字的作用是导致参数通过应用来传递。它们的区别就是ref关键要求变量必须在传递之前进行初始化,而out不需要。
下面通过一个小例子来学习一下out关键字。
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ref1
{
class Program
{
static int MaxIndex(int[] m_Array, out int m_Index)
{
m_Index = 0;
int m_Max = m_Array[0];
for (int i = 0; i < m_Array.Length; i++)
{
if (m_Array[i] > m_Max)
{
m_Max = m_Array[i];
m_Index = i;
}
}
return m_Max;
}
static void Main(string[] args)
{
int[] Array = new int[5] { 12, 22, 55, 44, 11 };
int maxIndex;//使用out关键字,此处不用初始化
Console.WriteLine("数组中最大的值是:{0}", MaxIndex(Array, out maxIndex));
Console.WriteLine("最大数的索引号是:{0}", maxIndex + 1);
Console.ReadLine();
}
}
}
运行结果如下:
其实上面的程序也可以使用ref关键字来写:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ref1
{
class Program
{
/// <summary>
/// 返回数组的最大值及最大值的索引值
/// </summary>
/// <param name="m_Array">数组</param>
/// <param name="m_Index">最大值的索引值</param>
/// <returns>数组中的最大值</returns>
static int MaxIndex(int[] m_Array, ref int m_Index)
{
m_Index = 0;
int m_Max = m_Array[0];
for (int i = 0; i < m_Array.Length; i++)
{
if (m_Array[i] > m_Max)
{
m_Max = m_Array[i];
m_Index = i;
}
}
return m_Max;
}
static void Main(string[] args)
{
int[] Array = new int[5] { 12, 22, 55, 44, 11 };
int maxIndex=0;//使用ref关键字,此处需要初始化
Console.WriteLine("数组中最大的值是:{0}", MaxIndex(Array, ref maxIndex));
Console.WriteLine("最大数的索引号是:{0}", maxIndex + 1);
Console.ReadLine();
}
}
}
运行结果如下: