2012-01-11 59 views
7

引用类型和值类型之间的差异通常会让初学者感到困惑,因为不了解值类型的变量实际上是什么。我们知道:引用类型 - 我们可以看到实际的引用吗?

  • 值类型存储实际
  • 引用类型只有参考存储对象

是否可以检查每一个类型的变量,要么看该值,还是实际参考本身?参考是否存储为某种编码值?我知道引用可以通过值传递,所以我假设如此。

我认为这将有助于新人的理解,并且非常有趣的探索。

+1

你只会看到一个地址,一个看似随机的数字。你为什么想看到这个? – Dykam 2012-01-11 12:24:43

+3

巩固变量内部不是他们设定的对象的想法,而是一个地址。我们如何访问这个? – 2012-01-11 12:25:32

+1

http://stackoverflow.com/questions/1978232/how-can-i-display-the-actual-value-of-a-reference-in-c-sharp – 2012-01-11 12:33:18

回答

10

是否可以检查每种变量以查看值或实际引用本身?

只是为了澄清,引用类型变量的的参考。参考是价值。

引用是一种值,就像int是一种值。与int不同,参考值只能是复制取消引用;你不能直接在C#中观察它的值,因为它的值是垃圾收集器的实现细节。

是否将参考存储为某种编码值?

是的,正好。在实践中,一个引用是一个32或64位整数(取决于你是在一个32位还是64位的进程中),这个指针指向垃圾收集器已知的一些结构的指针,与被引用对象的数据相关联。

如果您想直接查看引用,那么执行此操作的工具就是调试器。将C#代码加载到调试器中,编译它,运行它,找到一个断点,并查看堆栈和寄存器的状态。有一点聪明,你应该能够找出哪些堆栈位置和寄存器对应于哪些局部变量。与值类型的局部变量相对应的位置将包含这些值;那些引用类型将包含看起来像指针的值。如果您检查内存窗口中的这些指针,您将查看由垃圾回收器维护的用于描述对象内容的结构。

+3

我认为重要的是要注意,虽然现有的.net实现可能会将地址(对象信息记录的地址)存储在引用变量中,但不能保证将来的版本可以这样做。例如,有可能参考变量的某些位是选择多个堆中的一个的索引,而其他位是该堆中的索引。虽然这样的系统对于现有的处理器可能效率低下,但围绕这种模型设计未来的处理器可能允许更高效的高速缓存利用。现有的.net代码不应该关心这些细节。 – supercat 2012-01-11 17:06:55

+0

@supercat:绝对。实际上,引用可以被实现为完全不透明的句柄,它们只是索引到垃圾收集器拥有的某个表中才有意义;没有理由为什么参考文献中的任何位需要具有特定的含义。碰巧,在今天的实现中,引用*的每一位都是有意义的,因为它可以被解释为一个指针。但是这随时都可能发生变化。 – 2012-01-11 17:12:21

+0

出于好奇,虽然现有的CPU可能不会让这些事情变得非常有效,但我想知道在未来的CPU中,对于数据量为8(可能是16)个字节或更少的对象有一个小对象堆是否值得,对象表中的每个插槽将保存实际的对象数据,而不是指向它的指针。在现有的CPU上,每个堆访问都需要额外的指令来确定哪个堆是给定的引用所属的,但是如果有一个“获取对象地址”指令,它在引用中用一点来选择单向或双向间接... – supercat 2012-01-11 17:30:36

1

你可以很容易地用固定的物体做到这一点;

GCHandle gch=GCHandle.Alloc(data, GCHandleType.Pinned); 
IntPtr AddressInMemory=gch.AddrOfPinnedObject(); 
+0

与[几乎所有类一样,这对[不可擦]类型(http://msdn.microsoft.com/zh-cn/library/75dwhxf7.aspx)不起作用。 – svick 2012-01-11 12:39:08

+0

我站好了。看起来像我唯一需要的是与原始人在一起。 – 2012-01-11 12:45:33

1

你可以用unsafe代码做到这一点:

unsafe 
    static void Main(string[] args) 
    { 
     string s = "Hello"; 

     fixed (char* pc = s) 
     {     
      IntPtr p = (IntPtr)pc; 
      Console.WriteLine(p); // here is your meaningless address 
     }    
    } 
+0

这使得s的固定副本,所以你为什么不直接pin s? – 2012-01-11 12:38:24

+0

我们不会用's'来做任何事情。 – 2012-01-11 12:39:27

+0

@EugenRieck您是否有该陈述的来源?我看不到在这里复制。 – CodesInChaos 2012-01-11 12:41:39

5

这可能是一个乔恩斯基特,但我可能有一个不同的角度:

不要担心如何这些东西在内存中表示。除非您已经阅读了整个语言规范 - 无论如何,谁还这样做? - 你并不需要知道。真。不要费心记忆哪些数据存储在哪里 - 很可能这是特定于实现的。

相反,用语义来思考,例如,传递给函数的值类型是复制,而引用类型是引用。像这样的东西。

你并不真正想知道一个类型的声明实际上是什么。相信我。你想知道的是它是如何表现的

+2

+1这里是Eric Lippert说的(http://blogs.msdn.com/b/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx)同样的东西。地址是实现细节,语言规范在指定引用时没有提及地址。了解他们的行为方式,而不是他们的工作方式。 – MarkJ 2012-01-11 12:51:31

+2

虽然我同意你的发言,但我觉得这不是问题的关键。我想他想知道如何去做 - 而不是你的意见,为什么它不重要。了解复杂功能的运作方式可以使您成为更好的工程师,即使您没有找到即时使用该知识的工具。 – 2012-01-11 13:16:10

+0

虽然我都非常感谢并且同意你的回答,但并没有解决我的问题。我只想访问地址**只是为了证明它在那里**没有其他原因。我同意@ AdamG.Carstensen。 – 2012-01-11 13:40:19

相关问题