C#Reference和指针有什么区别?

时间:2009-01-09 23:20:45

标签: c# pointers reference

我不太了解C#引用和指针之间的区别。他们都指向记忆中的一个地方不是吗?我能弄清楚的唯一区别是指针不是那么聪明,不能指向堆上的任何东西,免于垃圾收集,只能引用结构或基类型。

我提出的一个原因是,人们需要很好地理解指针(从C,我猜)才能成为一名优秀的程序员。许多学习更高级语言的人都错过了这个,因此有这个弱点。

我只是不知道指针的复杂程度如何?它基本上只是对内存中某个地方的引用不是吗?它可以返回它的位置并直接与该位置的对象进行交互吗?

我错过了一个重要的观点吗?

10 个答案:

答案 0 :(得分:116)

指针和引用之间存在轻微但非常重要的区别。指针指向内存中的某个位置,而引用指向内存中的对象。指针不是“类型安全”,因为您无法保证它们所指向的内存的正确性。

以下面的代码为例

int* p1 = GetAPointer();

这是类型安全的,因为GetAPointer必须返回与int *兼容的类型。然而,仍然无法保证* p1实际上会指向一个int。它可以是char,double或只是指向随机存储器的指针。

然而,引用指向特定对象。对象可以在内存中移动,但引用不能无效(除非您使用不安全的代码)。在这方面,参考文献比指针更安全。

string str = GetAString();

在这种情况下,str具有两个状态之一1)它指向没有对象,因此为空或2)它指向有效的字符串。而已。 CLR保证了这种情况。它不能也不会用于指针。

答案 1 :(得分:42)

C#引用可以,并且将由垃圾收集器重定位,但是普通指针是静态的。这就是为什么我们在获取指向数组元素的指针时使用fixed关键字,以防止它被移动。

编辑:从概念上讲,是的。它们或多或少相同。

答案 2 :(得分:12)

引用是一个“抽象”指针:你不能用引用进行算术运算,也不能用它的值来玩任何低级技巧。

答案 3 :(得分:6)

首先,我认为你需要在你的sematics中定义一个“指针”。你的意思是你可以使用fixed在不安全的代码中创建指针吗?你的意思是IntPtr你可能是来自本地电话或Marshal.AllocHGlobal吗?你的意思是GCHandle?所有这些都是基本相同的东西 - 存储内容的存储器地址的表示 - 无论是类,数字,结构还是其他。而且为了记录,他们当然可以在堆上。

指针(以上所有版本)都是固定项目。 GC不知道该地址是什么,因此无法管理对象的内存或生命。这意味着您将失去垃圾收集系统的所有好处。您必须手动管理对象内存,否则可能会发生泄漏。

另一方面,引用几乎是GC知道的“托管指针”。它仍然是一个对象的地址,但现在GC知道目标的细节,所以它可以移动它,进行压缩,最终化,处理以及托管环境所做的所有其他好东西。

真正的主要区别在于你如何以及为何使用它们。对于托管语言中的绝大多数情况,您将使用对象引用。指针对于互操作以及对快速工作的罕见需求变得很方便。

编辑:实际上here's a good example当您在托管代码中使用“指针”时 - 在这种情况下它是一个GCHandle,但完全相同的事情可以通过AllocHGlobal完成或者使用固定在字节数组或结构上。我倾向于选择GCHandle,因为它对我来说更像是“.NET”。

答案 4 :(得分:5)

指针指向内存地址空间中的某个位置。引用指向数据结构。数据结构都是垃圾收集器(用于压缩内存空间)所有时间(好吧,不是经常,但是时不时地)。另外,正如您所说,没有引用的数据结构将在一段时间后收集垃圾。

此外,指针仅在不安全的上下文中可用。

答案 5 :(得分:4)

我认为开发人员理解指针的概念非常重要 - 也就是理解间接。这并不意味着他们必须使用指针。同样重要的是要理解参考的概念与指针的概念不同,虽然只是微妙的,但是参考的实现几乎总是是< / strong>指针。

也就是说,保存引用的变量只是一个指针大小的内存块,其中包含指向该对象的指针。但是,此变量的使用方式与使用指针变量的方式不同。在C#(和C和C ++,...)中,指针可以像数组一样索引,但引用不能。在C#中,垃圾收集器跟踪引用,指针不能。在C ++中,指针可以重新分配,引用不能。在语法和语义上,指针和引用是完全不同的,但在机械上,它们是相同的。

答案 6 :(得分:4)

指针可以指向应用程序地址空间中的任何字节。 引用受.NET环境的严格约束,控制和管理。

答案 7 :(得分:4)

引用和指针之间的主要区别在于指针是位的集合,其内容仅在主动用作指针时才重要,而引用不仅包含一组位,还包含一些元数据这使得基础框架了解其存在。如果指针存在于内存中的某个对象,并且该对象被删除但指针未被擦除,则指针的继续存在不会造成任何伤害,除非或直到尝试访问它指向的内存。如果没有尝试使用指针,没有什么可以关心它的存在。相比之下,基于引用的框架(如.NET或JVM)要求系统始终可以识别存在的每个对象引用,并且现有的每个对象引用必须始终为null或者标识对象它的正确类型。

请注意,每个对象引用实际上都封装了两种信息:(1)它标识的对象的字段内容,以及(2)对同一对象的其他引用的集合。虽然系统没有任何机制可以快速识别对象存在的所有引用,但是对象存在的其他引用集通常可能是引用封装的最重要的东西(尤其是当Object类型的东西用作锁定令牌之类的东西。虽然系统为每个对象保留了一些数据用于GetHashCode,但是对象除了存在于它们之外的引用集之外没有真正的标识。如果X保存对象的唯一现有引用,则将X替换为具有相同字段内容的新对象的引用将不具有可识别的效果,除非更改GetHashCode()返回的位,甚至不能保证这种效果。

答案 8 :(得分:1)

关于使它们有点复杂的指针不是它们的本质,而是你可以用它们做什么。当你有一个指向指针的指针时。那是真正开始变得有趣的时候。

答案 9 :(得分:0)

引用超过指针的最大好处之一是更大的简单性和可读性。与往常一样,当你简化某些东西时,你会更容易使用,但是以灵活性和控制为代价,你可以使用低级别的东西(正如其他人提到的那样)。

指针经常被批评为“丑陋”。

class* myClass = new class();

现在每次使用它时,您必须首先通过

取消引用它
myClass->Method() or (*myClass).Method()

尽管失去了一些可读性并增加了复杂性,人们仍然需要经常使用指针作为参数,这样你就可以修改实际的对象(而不是通过值传递),并且不需要复制大对象就可以获得性能提升。

对我来说,这就是为什么引用首先是“天生”的,以提供与指针相同的好处,但没有所有指针语法。现在你可以传递实际的对象(而不仅仅是它的值)并且你有一种更可读,更正常的方式与对象进行交互。

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

C ++引用与C#/ Java引用的不同之处在于,一旦为其分配了值,就无法重新赋值(并且必须在声明它时分配它)。这与使用const指针(一个无法重新指向另一个对象的指针)相同。

Java和C#是非常高级的现代语言,它清理了多年来在C / C ++中积累的许多混乱,指针绝对是需要“清理”的东西之一。

至于你对知识指针的评论使你成为一个更强大的程序员,在大多数情况下都是如此。如果你知道'怎么'有效,而不是仅仅使用它而不知道我会说这通常可以给你一个优势。边缘总是变化多少。毕竟,在不知道如何实现的情况下使用某些东西是OOP和接口的众多优点之一。

在这个具体的例子中,了解指针有什么帮助你引用?理解C#引用不是对象本身,但指向对象是一个非常重要的概念。

#1:您没有通过值 对于初学者来说,当你使用指针时,你知道指针只包含一个地址,就是这样。变量本身几乎是空的,这就是为什么传递参数这么好。除了性能提升之外,您还在使用实际对象,因此您所做的任何更改都不是临时的

#2:多态性/接口 当您具有作为接口类型的引用并且它指向对象时,即使该对象可能具有更多功能,您也只能调用该接口的方法。对象也可以以不同方式实现相同的方法。

如果你很好地理解了这些概念,那么我认为你没有因为没有使用指针而错过太多。 C ++通常被用作学习编程的语言,因为有时候弄脏你的手很好。此外,使用较低级别的方面使您欣赏现代语言的舒适。我从C ++开始,现在是一名C#程序员,我觉得使用原始指针可以帮助我更好地了解幕后发生的事情。

我不认为每个人都必须从指针开始,但重要的是他们理解为什么使用引用而不是值类型,理解它的最好方法是查看它的祖先,指针。