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

C#基础-ref、out

2013-12-07 20:37 267 查看
1.默认情况下,C#假定所有的方法参数传递都是传值的。

如下面的方法:

publicstaticvoidMain(string[]args)
{
intval=5;

//调用AddValue方法,aVal会重新拷贝一份val的值(即aVal为val的一个实例副本),方法内部的操作并不会改变val的值。
AddValue(val);

//val值还是5,并没有加1
Console.WriteLine(val);
Console.ReadLine();
}

publicstaticvoidAddValue(intaVal)
{
aVal=aVal+1;
}
如果期望在调用AddValue方法以后,val加一,有两种方案:
1、修改AddValue,将修改后的值作为返回值,并赋值给val。
2、使用ref关键字、out关键字。传入参数时,不再传入一个原来类型的拷贝,而是直接传入原有值类型的地址(类似于引用类型),这样在方法内部的任意修改都会影响到传入的值类型。
2.引用类型的ref、out
引用类型作为方法参数传入时,对象的引用(或者指向该对象的指针)会传入方法参数,在方法内部对该对象的修改,被调用者也能直接看到。
这样是不是就不需要ref、out关键字了呢?
eg:
其实,引用类型作为方法参数传入时,理论上还是会新建一个变量,该变量指向和原有引用类型的指向相同。
如果在调用方法内部修改了引用类型的属性,使用ref和不使用ref没有区别,参考代码为:

publicstaticvoidMain(string[]args)
{
Useruser=newUser();user.Name="Lisa";

//不使用关键字ref,在调用方法内部修改User对象的属性,也会影响传入参数User.
//因为此时的User和方法参数aUser都指向同一份引用。
ChangeUser(user);
Console.WriteLine(user.Name);

//使用关键字ref,在调用方法内部修改User对象的属性,也会影响传入参数User
//因为此时的User和方法参数aUser是同一个对象。
ChangeUser(refuser);
Console.WriteLine(user.Name);

Console.ReadLine();
}

publicstaticvoidChangeUser(UseraUser)
{
aUser.Name="Alan1";
}

publicstaticvoidChangeUser(refUseraUser)
{
aUser.Name="Alan2";
}
输出结果为:
Alan1
Alan2
此时看不出不使用ref和使用ref的区别,同样对在方法内部修改User的Name属性,效果一样
如果在调用方法内部修改了引用类型的指向,使用ref和不使用ref有区别,参考代码为:

publicstaticvoidMain(string[]args)
{
Useruser=newUser();

//不使用关键字ref,在调用方法内部修改User对象的引用,不会影响传入参数User.
//因为在栈中有两个独立的指针user、aUser
ChangeUser(user);
if(user!=null)
{
Console.WriteLine("user1isnotnull");
}
else
{
Console.WriteLine("user1isnull");
}

//使用关键字ref,在调用方法内部修改User对象的引用,会影响传入参数User.
//因为在栈中仅有一个指针user
ChangeUser(refuser);
if(user!=null)
{
Console.WriteLine("user2isnotnull");
}
else
{
Console.WriteLine("user2isnull");
}

Console.ReadLine();
}

publicstaticvoidChangeUser(UseraUser)
{
aUser=null;
}

publicstaticvoidChangeUser(refUseraUser)
{
aUser=null;
}
输出结果为:
user1isnotnull
user2isnull
此时能够明细看错不使用ref和使用ref的区别,同样对在方法内部赋值User==null,效果不一样
引用关系图为:
引用类型Val本身


调用Change(aVal)方法以后:


调用Change(refaVal)方法以后:


从上可知,如果需要真正的指向同一个引用,在方法内部的任何改变都会影响到传入参数,还是需要使用ref和out关键字。
3.从CLR来说,ref和out本身是一样的,都导致传递指向实例的指针。
唯一的区别是:
ref参数需要调用者在调用方法之前初始化参数的值(强制要求,不赋值编译不通过)
out参数不指望调用者在调用方法之前初始化参数默认值,即使调用者之前赋值,在调用方法内部也会重新赋值(即调用者的赋值会被替换,赋值无效,所以不建议调用者提前赋值)。
4.其它
a.允许对方法进行ref和out进行重载(即认为使用ref/out跟不使用ref/out函数签名不一致)
以下方法能够编译通过。


publicstaticvoidChangeUser(UseraUser)
{
aUser=null;
}

publicstaticvoidChangeUser(refUseraUser)
{
aUser=null;
}
编译通过

但是,如果两个方法只有ref和out的区别,是不允许的。因为两个方法签名的元数据是完全相同的。
以下方法编译不过


publicstaticvoidChangeUser(refUseraUser)
{
aUser=null;
}
publicstaticvoidChangeUser(outUseraUser)
{
aUser=null;
}
编译不过:
错误:“ChangeUser”不能定义仅在ref和out上有差别的重载方法


b.使用ref/out关键字时,方法定义和调用时参数类型必须一致。

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐
章节导航