Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1714562
  • 博文数量: 177
  • 博客积分: 9416
  • 博客等级: 中将
  • 技术积分: 2513
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-06 16:08
文章分类

全部博文(177)

文章存档

2013年(4)

2012年(13)

2011年(9)

2010年(71)

2009年(12)

2008年(11)

2007年(32)

2006年(25)

分类:

2007-09-12 11:34:17

问题是什么
刚接触C#不久,对于值类型和引用类型的区别还是有点糊涂。后来看了一个不能工作的Swap()(下面有示例)方法之后,才发现,其实引用类型相当于C/C++中的指针——因为有声明:“string aStr = null;”是正确的。为了让自己以后可以追溯一下,所以把今天的理解记录下来,写在这里。

如何理解
C#中关于引用类型的定义是这样的:
“……值类型与引用类型的基本区别是他们在内存中的存储方式。……引用类型变量的地址存放在栈上,但实际的对象存放在堆上。……”
 
在上一篇文章里我说过,指针其实是一个ulong,从上面的描述中可以看出,引用类型其实也是一个ulong(地址),也就是说,“Object obj;”中,obj被分成两部分,一部分是其地址,另一部分是真正的Object实例。这两部分紧密联系在一起,形成一个引用类型对象。
 
引用类型的定义暗示我们,C#中引用类型相当于C++的指针,只不过C#语言本身作了一些工作,将指针和其指向的对象紧密地联系在一起了。只不过我们无法通过强制类型转换来获取这个值——也许有,但是我比较菜,不知道,呵呵。另外,引用类型的定义还暗示我们,C/C++函数调用时以传值方式传递参数的规则,在C#中同样有效。最简单的例子:
// C# code. Non dereferencing.
public void Swap(T lhs, T rhs)
{
    T temp = lhs;
    lhs = rhs;
    rhs = temp;
}
调用该函数结果是什么样子呢?运行一下下面的代码就知道结果:传进去的引用类型的对象根本就没有改变(以string为例):
string a = "aaaa";
string b = "bbbb";
Swap(a, b);
System.Console.WriteLine("a = " + a + ", b = " + b);
输出结果:a = aaaa, b = bbbb
 
因为实际上是对地址进行操作,传入的参数就是地址,因此可以将其翻译成C++(方便起见,使用int类型):
// C++ code. Non dereferencing.
template
void Swap(T * lhs, T * rhs)
{
    T * ptemp = lhs;
    lhs = rhs;
    rhs = ptemp;
}
这个函数也不能达到交换实参的目的,因为此处操作的是地址,与上一个例子一样。也就是说,C/C++和C#中函数调用时以传值方式传递参数。
 
那么为什么不是与C++的引用相似呢?可以看以下代码:
// C++ code
template
void Swap(T& lhs, T& rhs)

{
    int temp = lhs;
    lhs = rhs;
    lhs = temp;
}
对这个函数的调用将正确的交换两个实参。
 
C#要想通过函数调用来修改引用类型的对象,有两种方式:
1、dereference该引用以获取实际对象,即改变存放在堆上的对象本身,而不是存放在栈上的地址(没有进行dereference),这与C++的指针是相同的,也是前两个例子说明的问题。用deference的话,而且T实现了拷贝自身的方法,那么实现如下:
// C# code. Dereferencing.
// NOTE: T must have CopyTo() method.
public void Swap(T lhs, T rhs)
{
    T temp = new T();
    lhs.CopyTo(temp);
    rhs.CopyTo(lhs);
    temp.CopyTo(rhs);
}
翻译成C++,就是如下代码:
// C++ code. Dereferencing.
// NOTE: T must have operator "=" overloaded.
template
void Swap(T * lhs, T * rhs)
{
    int temp = *lhs;
    *lhs = *rhs;
    *rhs = temp;
}

2、使用ref修饰符(从另一个角度说明C#引用的行为更像C++的指针),如下:
//可以工作的Swap
// NOTE: T doesn't need to have CopyTo() method implemented.
//       If you don't need to modify the reference itself, "ref" is not needed.
private void Swap(ref T lhs, ref T rhs)
{
    T temp = lhs;
    lhs = rhs;
    rhs = temp;
}
原因很简单,使用了ref,将其翻译成C++就是:
/ C++ code. Dereferencing.
// NOTE: T doesn't need to have operator "=" overloaded.
template
void Swap(T **lhs, T **rhs)
{
    T * temp = *lhs;
    *lhs = *rhs;
    *rhs = temp;
}

至于这里面涉及到的应该使用引用还是指针的问题,那就是另外一个话题了。关于指针本身,可以参考“指针是通往地狱的捷径”。

PS:由于对C#水平实在太浅,理解难免出现偏差,任何错误,请大虾指正!上述代码均为合法代码。

参考:
Programming C#, 3rd edition.

 
Copyleft (C) 2007-2009 raof01.
本文可以用于除商业外的所有用途。此处“用途”包括(但不限于)拷贝/翻译(部分或全部),不包括根据本文描述来产生代码及思想。若用于非商业,请保留此 权利声明,并标明文章原始地址和作者信息;若要用于商业,请与作者联系(raof01@gmail.com),否则作者将使用法律来保证权利。
阅读(15900) | 评论(25) | 转发(0) |
给主人留下些什么吧!~~

twtyypmb2015-07-24 16:29:21

C++的引用和C#的引用完全是两回事,最根本的区别就是C++的引用没有内存消耗,而C#有,C#的引用在某种意义上说就是指针,所以除了C++的引用传参之外,其他传参都是把参数复制了一份,都有内存消耗,归根结底还是值传递

fera2009-12-08 17:51:05

As stated in "Java for C++ programmer", "It is sometimes said that Java doesn't have pointers. That is not true. In fact, objects can only be referenced with pointers. More precisely, variables can hold primitive values (such as integers or floating-point numbers) or references (pointers) to objects." C# is the descendant of C++ and Java, this statement is true for C#.

chinaunix网友2009-11-03 14:29:52

“补充一点,本文中所谓C#参数传递是传值方式指的是引用值被传递,即对象在栈上的地址,而不是以传值方式来传递整个对象。整个文章的意思也包含了这个意思。” 这个说法可以接受。

chinaunix网友2009-11-03 14:26:11

多谢LZ的分享,但是窃以为用String做这个例子没有意义,可以使用Object。 在C#里面,String是被特殊处理的,对String的操作已经值类型化了。在现实中,程序员不会写出Non dereferencing这样的例子,就是因为这个原因。 另外,上面某楼说得也不对,C#里面默认的传递并不是值传递,还是要看参数的类型是值类型还是引用类型。即使不使用ref和out来传递引用类型的对象,也可以在函数内部改变该对象,这种做法是不被推荐的,但是并不是C#语言不允许的。传递值类型的“变量”时,可以使用ref和out对值类型进行装箱,从而达到引用传递的效果。 这篇文章日期久远了,大概LZ又有新的心得了吧。

fera2009-07-29 21:53:07

To 楼上的 C++和C#一样,把引用做成了类似指针的东西。至于为什么,建议你参考一下我的另一篇:C: Stack, Parameter Passing and Value Return 如果你没有读懂这篇文章,没关系,再读几遍──我的同事朋友们都能读懂,相信你也可以做到;或者根据你自己的理解,尝试理解/运行一下文中的代码。 我是否需要学习基本功无须你来指点,谢谢!