设为首页 加入收藏

TOP

把 ref 和 out 关键字说透 (一)
2014-11-23 23:36:36 来源: 作者: 【 】 浏览:29
Tags:ref out 关键字

ref 和 out 的区别

网上有很多这方面的文章,但是大部分人总是纠结于他们在原理上的那一点点细微的区别,所以导致了难以区分它们,也不知道什么时候改用哪一个了。

但是如果从试用场景的角度对它们进行区别的话,以后你一定不会再纠结了。

当你明白它们的适用场景后,再去扣其中的原理,使用中的一些问题也就迎刃而解了~

简单的来说,它们的区别在于:

ref 关键字 是作用是把一个变量的引用传入函数,和 C/C++ 中的指针几乎一样,就是传入了这个变量的栈指针。

out 关键字 的作用是当你需要返回多个变量的时候,可以把一个变量加上 out 关键字,并在函数内对它赋值,以实现返回多个变量。

几个简单的演示

上面说了 ref 和 out 的作用,非常简单,但是在具体使用的时候却遇到了很多麻烦,因为 C# 中本身就区分了引用类型和值类型。

我先举几个例子,来看看会出现哪些诡异的情况

代码段一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void Main( string [] args)
{
int a;
Test1( out a); //编译通过
int b;
Test2( ref b); //编译失败
}
static void Test1( out int a)
{
a = 1;
}
static void Test2( ref int b)
{
b = 1;
}

这两个关键字看起来用法一样,为什么会有合格现象?

网上的答案很简单: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 ;
}

新建三个 object,object是引用类型;三个函数,分别是 out,ref和普通调用;执行了一样的语句;最后的结果为什么是这样呢?

如果你只是从浅层次理解了 out 和 ref 的区别,这个问题你一定回答不上了。(我以前也不知道)

所以,这是为什么呢?继续往下看。

^_^ 相信很多人晕了,我的目的达到了。(邪恶的笑~~)

那么,下面,我为大家从两个角度来分析一下。

对于值类型来说,加 out、加 ref 和什么都不加有什么共同点和区别?

对于引用类型来说,加 out、加 ref 和什么都不加有什么共同点和区别?

问题一:关于值类型

普通的传递值类型很简单了,传的只是一个值,没难度,平时都是这么用的,很好区分,所以这里就不惨和进去了。

接下来是 ref 和 out 的区别,为什么要了解区别呢?当然是为了了解怎么用它们,简单的来说就是需要了解:什么时候该用哪个。

个人总结有几个原则:

如果你是为了能多返回一个变量,那么就应该用 out:

用 out 关键字有几个好处:可以不关心函数外是否被赋值,并且如果在函数内没有赋值的话就会编译不通过。(提醒你一定要返回)

你可以把它当成是另一种形式的 return 来用,我们来做一个类比:

return 语句的特点:接收 return 的变量事先不需要赋值(当然如果赋值了也没关系),在函数内必须 return。

可以看到 out 关键字的作用、行为、特点 和 return 是完全一样的。因为它当初设计的目的就是为了解决这个问题的。

如果你想要像引用类型那样调用值类型,那你就可以 ref:

传入值类型的引用后,你可以用它,也可以不用它,你也可以重新修改它的各个属性,而函数外也可以随之改变。

我们来把 “传值类型的引用” 和 “传引用类型” 来做一个类比:

1
2
3
4
5
6
7
8
9
10
11
static void Main( string [] args)
{
int a;
Test1( ref a); //错误 1 使用了未赋值的局部变量“a”
object b;
Test2(b); //错误 2 使用了未赋值的局部变量“b”
}
static void Test1( ref int a) { }
static void Test2( object b) { }

传入加了 ref 的值类型 和 传入一个引用类型 的作用、行为、特点都是类似的。

同样,他们同时要遵守一个原则:传入前必须赋值,这个是为什么呢?

如果赋值后,传入两个函数的分别是 int a 的指针 和 object b 的指针。

而不赋值的话,a 和 b 根本还不存在,那它们又怎么会有地址呢?

注意:如果你只写了 object a ,而在后面的代码中没有赋值,它并没有真正地分配内存。

我们可以看一下三个操作的 IL 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void Main( string [] args)
{
//IL_0000: nop
object a; //没做任何事
//IL_0002: ldnull
//IL_0003: stloc.1
object b = null ; //在栈中增加了一个指针,指向 null
//IL_0004: newobj instance void [mscorlib]System.Object::.ctor()
//IL_0009: stloc.2
object c = new object (
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C语言移位 下一篇c语言:计算输入字符个数及字母出..

评论

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