Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1716383
  • 博文数量: 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),否则作者将使用法律来保证权利。
阅读(15927) | 评论(25) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2009-07-27 12:01:37

无语,楼主你需要学习下基本功了。 C#传值或者传引用是根据目标类型的,哪些属于引用,哪些属于值可以自己看vs目录下的C#编程指南 引用和指针完全不是一个概念,哪怕在C++中也不是。 引用类型的真实对象是位于heap上的,值类型和指向对象的引用是位于stack上的 垃圾回收的时候因为heap上的对象被重新排列了,如果stack上是指针,会导致指向的对象混乱,引用则不会,因为它不仅仅是指向一个地址,而是更紧密的连接

chinaunix网友2008-02-03 16:55:06

啥乱7八糟的 投诉你