CLR via C# 以传引用的方式向方法传递参数

CLR默认所有方法都传值。传递引用类型的对象时,对象引用(或者说指向对象的指针)被传递给方法。注意引用(或指针)本身是传值的,意味着方法能修改对象,而调用者能看到这些修改。对于值类型的实例,传给方法的是实例的一个副本,意味着方法将获得它专用的一个值类型的实例副本,调用者中的实例不受影响。

重要提示:在方法中,必须知道传递的每个参数是引用类型还是值类型,处理参数的代码显著有别。

CLR允许以传引用而非传值的方式传递参数。C#用关键字out或ref支持这个功能。两个关键字都告诉C#编译器生成元数据来指明该参数是传引用的。编译器将生成代码来传递参数的地址,而非传递参数本身。

CLR不区分out和ref,意味着无论用哪个关键字,都会生成相同的IL代码。另外,元数据也几乎完全一致,只有一个bit除外,它用于记录声明方法时指定的是out还是ref。但C#编译器是将这两个关键字区别对待的,而且这个区别决定了由哪个方法负责初始化所引用的对象。如果方法的参数用out来标记,表明不指望调用者在调用方法之前初始化好了对象。被调用的方法不能读取参数的值,而且在返回前必须向这个值写入。相反,如果方法的参数用ref来标记,调用者就必须在调用该方法前初始化参数的值,被调用的方法可以读取值以及/或者向值写入。

//--

在上述代码中,x在Method的栈帧中声明。然后,x的地址传递给GetVal。GetVal的v是一个指针,指向Method栈帧中的int值。在GetVal内部,v指向的那个int被更改为10。GetVal返回时,Method的x就有了一个为10的值。

为大的值类型使用out,可提升代码的执行效率,因为它避免了在进行方法调用时复制值类型实例的字段。

//--

在上述代码中,x也在Method的栈帧中声明,并初始化为5。然后,x的地址传给AddVal。AddVal的v是一个指针,指向Method栈帧中的int值。在AddVal内部,v指向的那个int要求必须是已经初始化的。因此,AddVal可在任何表达式中使用该初始值。AddVal还可更改这个值,新值会“返回”调用者。

//--

out

ref

 

综上所述,从IL和CLR的角度看,out和ref是同一码事:都导致传递指向实例的一个指针。但从编译器的角度看,两者是有区别的。根据市out还是ref,编译器会按照不同的标准类验证你写的代码是否正确。

//--

C#要求必须在调用方法时指定out或ref。因为语言设计者认为调用者应显式表明意图。

另外,CLR允许根据使用out还是ref参数对方法进行重载。在C#中是合法的。

但是两个重载方法只有out和ref的区别则不合法,因为两个签名的元数据形式完全相同。

//--

下面展示如何用ref关键字实现一个用于交换两个引用类型的方法:

为了交换两个string你或许以为可以这样:

但是无法通过编译。问题在于,对于以传引用的方式传给方法的变量,它的类型必须是与方法签名中声明的类型相同。

你可以这么写:

传递的参数之所以必须与方法预期的参数匹配,原因是保障类型安全。

//--

可以使用泛型类修正这些方法:

完美运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值