【问题标题】:clearing doubts about value and reference types清除对值和引用类型的疑虑
【发布时间】:2012-09-09 18:51:01
【问题描述】:

值类型变量直接包含实际数据,引用类型变量包含对实际数据的引用。

我认为这是:

l.h.s 是值类型,r.h.s 是引用类型

在左侧,如果我将i 复制到j 中,则会用相同的原始数据 (45) 填充新的内存位置。

在右侧,如果我将k 复制到l,则新的内存位置将填充对对象的引用;这个引用指向内存中的实际对象。

现在,我对这种引用类型复制感到困惑。这里有一点不同:

这里,r.h.s 上的副本使l 指向与k 相同的位置。

我的问题是1。 “哪个更真实?” 还是比我想象的更多?

此外,值类型可以在堆上分配,这取决于 jitter 认为它适合的方式,然后 2。我们可以强制在堆栈上分配引用类型吗?

抱歉,图片编辑马虎。

【问题讨论】:

  • 相关帖子here

标签: c#


【解决方案1】:

第一张图比较好,lk是不同的变量,在内存中占据不同的位置。

值类型可以在堆上分配,这取决于 jitter 认为它适合的方式

实际上它更多地取决于上下文和使用值的方式。值类型字段总是在堆上分配,装箱和闭包是其他原因。

但是,当lref 参数时,第二张图片适用:

MyClass k = new ...;
M(ref k);

void M(ref MyClass l) { /* Here l is an alias for k */ }

那么 2. 我们可以强制在堆栈上分配引用类型吗?

有类似 stackalloc 的东西,但它是 C# 程序员“看不见”的优化。
最简单且最有用的答案是:不。

【讨论】:

    【解决方案2】:

    两者都不是,两者都是。问题是您在谈论 C# 语言本身未指定的实现细节。

    事实上,您可以针对只有堆栈的机器进行编程,或者您可能有可用的寄存器。归根结底,it is all just an implementation detail. 最接近现实的模型取决于您运行的机器架构。

    【讨论】:

    • 如果我们真的想表达抽象概念,那么我们会画两个内存块。一种用于短期记忆,一种用于长期记忆。谈论堆栈和堆仍然非常依赖于架构。我的观点是,试图确定“它是如何工作的”的准确表示完全取决于实现。泛化并不真正适用于 C# 的所有不同实现。 Windows、Mono、Micro 等...
    • 我将第二张图片解释为:如果有人,l = k;之后说k = new SomeReferenceType();,那么因为只有一个引用那张照片,l 也会因此而改变。 (而且我们知道这不是 C# 规范所说的那样,第二张图片并不“好”。)
    【解决方案3】:
    1. 我们可以强制在堆栈上分配引用类型吗?

    我们不能以这种方式强制执行任何操作,例如,当您在方法中初始化变量时,引用就在堆栈上。 引用类型,其实就是用new关键字初始化的对象,包括里面的值类型都是在堆上分配的。

    虽然这是一个可以写一本书的主题,但这一切都归结为:

    在 .NET 中有两种类型的行为。价值行为和参考行为。两者的区别在于它们的概念。 Value 类型代表值本身,实际数据和 references 是内存位置。内存位置是在堆上创建的实际对象实例中的地址。它们代表了一种指向虚拟地址空间中实际对象的链接。

    I have written a blog post that goes somewhat into detail 并尝试从概念上解释它是如何在较低级别上工作的。但是我在那里的解释主要基于 x86 架构,并不是所有东西都是如何实现的。 C# 和 .NET 框架以及 JIT 可以如何改变,但希望它有助于使其更清晰一点。

    【讨论】:

      【解决方案4】:

      画得真好!我认为第一张图片“更真实”。它们引用同一个对象,但为了存储引用,它们还需要变量。引用或指针也是变量,这意味着它们有自己的内存位置。 2. 我不这么认为。 (我不确定)

      【讨论】:

        【解决方案5】:
        1. 第一个是最正确的。参考是一个数字,复制时复制地址。在 c 中,您实际上可以将引用放入一个整数中,在 c# 中,您可以多观察一下。

        2. 如果你声明一个结构而不是一个类,它很可能在堆栈中(据我所知)

        【讨论】:

          【解决方案6】:

          第一张图是最正确的,因为你认为

          l = k;
          

          如果你有这种情况

          class MyClass
          {
            internal string k;
          
            internal void Test()
            {
              OtherClass.Method(ref k);
            }
          }
          
          class OtherClass
          {
            internal static Method(ref string l)
            {
              // do a lot of stuff using l
            }
          }
          

          那么在这种情况下我会说第二张图片更正确,因为这个参数有ref关键字。这意味着如果有人将k 引用更改为指向另一个字符串对象,OtherClass.Method 正在运行时,那么突然l 变量也将指向新对象.

          但这只是因为ref关键字才成立。

          【讨论】:

            【解决方案7】:

            肯定是第一张图片...否则使用“by-ref”参数的调用会影响两个引用,即 Xxx(ref k);可能“重定向” k 和 l。

            关于 stack 等,您可能想阅读 Eric Lippert (The Stack Is An Implementation Detail)

            【讨论】:

            • :) 我很高兴看到其他人引用那篇文章。
            • Thx... 我认为这个(The Truth About Value Types,Eric Lippert 也是)很好地总结了存储分配是如何不受值类型(仅)调节的。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-11-20
            • 1970-01-01
            • 1970-01-01
            • 2011-01-20
            • 2014-05-23
            • 2022-11-25
            • 2012-07-20
            相关资源
            最近更新 更多