设为首页 加入收藏

TOP

把 ref 和 out 关键字说透 (二)
2014-11-23 23:36:36 来源: 作者: 【 】 浏览:28
Tags:ref out 关键字
); //在栈中增加了一个指针,指向新建的 object 对象
}

传入引用类型的目的是把一个已经存在的对象的地址传过去,而如果你只是进行了 object a 声明,并没做复制,这行代码跟没做任何事!

所以,除非你使用了 out 关键字,在不用关键字和用 ref 关键字的情况下,你都必须事先复制。 out 只是一种特殊的 return

总结:

现在你是否明白,当变量什么情况下该用什么关键字了吗?其实有时候 ref 和 out 都可以达到目的,你需要根据你的初衷,和它们的特点,来衡量一下到底使用哪个了!

另外,我们来看看两个同样的函数,用 out 和 ref 时候的 IL 代码

原函数:

1
2
3
4
5
6
7
8
private static void Test1( out int a)
{
a = 1;
}
private static void Test2( ref int a)
{
a = 1;
}

IL代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.method private hidebysig static
void Test1 (
[ out ] int32& a
) cil managed
{
// Method begins at RVA 0x2053
// Code size 5 (0x5)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.1
IL_0003: stind.i4
IL_0004: ret
} // end of method Program::Test1
.method private hidebysig static
void Test2 (
int32& a
) cil managed
{
// Method begins at RVA 0x2059
// Code size 5 (0x5)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.1
IL_0003: stind.i4
IL_0004: ret
} // end of method Program::Test2

发现了吗? 它们在函数内部完全是一样的!因为他们的原理都是传入了这个变量的引用。只是 out 关键字前面出现了一个标记 [out]

它们在原理上的区别主要在于编译器对它们进行了一定的限制。

最上面“代码段一”中的问题你现在明白了吗?

问题二:关于引用类型

对于值类型来说,最难区别的是 ref 和 out,而对于引用类型来说就不同了。

首先,引用类型传的是引用,加了 ref 以后也是引用,所以它们是一样的?暂时我们就这么认为吧~ 我们暂时认为它们是一样的,并统称为:传引用。

所以,对于引用类型来说,out 和 传引用 的区别跟对于值类型传 ref 和 out 的区别类似,具体适用场景也和值类型类似,所以就不多加阐述了。

虽然我们说直接传和加 ref 都可以统称为传引用,但是它们还是有区别的!而且是一个很隐蔽的区别。

我们再来看一下最上面的代码段二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void Main( string [] args)
{
object a = new object (), b = new object (), c = new object ();
Test1( out a);
Test2( ref b);
Test3(c);
//最终 a,b,c 分别是什么?
//a,b = null
//c 还是 object
}
static void Test1( out object a)
{
a = null ;
}
static void Test2( ref object b)
{
b = null ;
}
static void Test3( object c)
{
c = null ;
}

out 关键字就相当于 return ,所以内部赋值为 null ,就相当于 return 了 null

可是,为什么引用类型还要加 ref 呢?它本身部已经是引用了吗?为什么加了以后就会有天大的区别呢?!

用一句话概括就是:不加 ref 的引用是堆引用,而加了 ref 后就是栈引用! @_@ 好搞啊。。什么跟什么?让我们一步步说清楚吧!

正常的传递引用类型:

\

加了 ref 的传递引用类型:

\

这两张图对于上面那句话的解释很清楚了吧?

如果直接传,只是分配了一个新的栈空间,存放着同一个地址,指向同一个对象。

内外指向的都是同一个对象,所以对 对象内部的操作 都是同步的。

但是,如果把函数内部的 obj2 赋值了 null,只是修改了 obj2 的引用,而 obj1 依然是引用了原来的对象。

所以上面的例子中,外部的变量并没有收到影响。

同样,如果内部的对象作了 obj2 = new object() 操作以后,也不会对外部的对象产生任何影响!

而加了 ref 后,传入的不是 object 地址,传入的是 object 地址的地址!

所以,当你对 obj2 赋 null 值的时候,其实是修改了 obj1 的地址,而自身的地址没变,还是引用到了 obj1

虽然在函数内部的语句是一样的,其实内部机制完全不同。我们可以看一下IL代码,一看就知道了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.method private hidebysig static
void Test1 (
object a
) cil managed
{
// Method begins at RVA 0x2053
// C
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C语言移位 下一篇c语言:计算输入字符个数及字母出..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: