先看例子:
class TestZhiYin
{
static void Main(string[] args)
{
int i = 100;
args ar=new args();
string str = "One";//特殊的引用类型
ChangeValueyin(ref i);
ChangeValueyin(ref ar);
ChangeStingyin( ref str);
Console.WriteLine(i);//200
Console.WriteLine(ar.j);//600
Console.WriteLine(str);//Two
// ChangeValuezhi(i);
// ChangeValuezhi(ar);
// ChangeStringzhi(str);
// Console.WriteLine(i);//100
// Console.WriteLine(ar.j);//600
// Console.WriteLine(str);//One
}
static void ChangeValueyin(ref int iVlaue)
{
iVlaue = 200;
Console.WriteLine(iVlaue);
}
static void ChangeValuezhi(int iVlaue)
{
iVlaue = 300;
Console.WriteLine(iVlaue);
}
static void ChangeValueyin(ref args ar)
{
ar.j=600;
Console.WriteLine(ar.j);
}
static void ChangeValuezhi(args ar)
{
ar.j=600;
Console.WriteLine(ar.j);
}
static void ChangeStingyin( ref string sValue)
{
sValue = "Two";
Console.WriteLine(sValue);
}
static void ChangeStringzhi(string sValue)
{
sValue = "Two";
Console.WriteLine(sValue);
}
}
public class args
{
public int j=5;
}
这里先说明一下:无论是值类型,还是引用类型不加ref、out关键字都是按值传递的。值类型传递的是值本身的拷贝,引用类型传递的是引用本身的拷贝。对值拷贝的操作不会影响到原来的值;对引用拷贝的操作(拷贝引用和原来引用指向同一个对象)可以影响原来的对象,但是特例string除外。
加上ref、out就是按引用传递了(就是传递的真实内存地址),对于值类型来说就是值的内存地址,对于引用类型来说,就是这个引用的内存地址。这样的操作,都会改变函数外变的量。
下面进行结果分析:
第一次执行(按地址传参):
int i = 100;
args ar=new args();
string str = "One";//特殊的引用类型
ChangeValueyin(ref i);
ChangeValueyin(ref ar);
ChangeStingyin( ref str);
Console.WriteLine(i);//200
Console.WriteLine(ar.j);//600
Console.WriteLine(str);//Two
其结果对比:
i=100-----执行函数后i=200
ar.j=5-----执行函数后ar.j=600
str="One"-----执行函数后str="Two"
无论是值类型,还是引用类型,按地址传递都将改变其原来值。
第二次执行(按值传参):
ChangeValuezhi(i);
ChangeValuezhi(ar);
ChangeStringzhi(str);
Console.WriteLine(i);//100
Console.WriteLine(ar.j);//600
Console.WriteLine(str);//One
其结果对比:
i=100-----执行函数后i=100
ar.j=5-----执行函数后ar.j=600
str="One"-----执行函数后str="One"
可以看出,当按值传参的时候,i与ar.j执行函数后结果是不一样的。
i没有被改变,因为传递的是i=100这个值的一个拷贝,对它的任何操作不会对原来的i=100有任何改变。
args类中j的值被改变了,因为args是引用类型,当按值(这个值就是args引用的一个拷贝)传递args时,
传递的是args引用的拷贝,引用拷贝和原来的引用都指向同一个对象,所以j被改变了。
再看str定义的是string类型,string也是引用类型,为什么没有像args一样?
string是特殊的引用类型,它具有恒定性,也就是说,一个字符串创建以后,不能再对它进行任何更改
对它更改都会重新创建一个新的字符串
比如:string str="avb"执行object.ReferenceEquals(str,str.ToUpper())将得到false
如果按一般引用类型,对原有引用上的修改不会引起重新创建一个新的对象。
如果对一个字符进行频繁的操作,这时你应该考虑用StringBuilder,StringBuilder维护的是一个字符串数组
对它的操作,会体现到它本身,而不是去创建另一个字符串对象,这样比对string的操作节省了内存空间。
另外补充:
ref 参数传入前必须被初始化,out不用,out需要在过程中对它初始化
可以通过ref和out来实现方法重载,但又不允许通过区分ref和out来实现方法重载
比如上面可以实现ChangeValueyin ref或者out的重载
ChangeValueyin(int iVlaue)
ChangeValueyin(ref int iVlaue)
这样将不被允许
ChangeValueyin(out int iVlaue)
ChangeValueyin(ref int iVlaue)
仅代表个人观点,如果认识有什么不足还请指教!!!