4

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

我认为这是:

lhs 是值类型,rhs 是引用类型

在此处输入图像描述

在左侧,如果我复制ij,一个新的内存位置将填充相同的原始数据 (45)。

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

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

在此处输入图像描述

在这里,rhs 上的副本l指向与 相同的位置k

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

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

抱歉草率的图像编辑。

4

7 回答 7

2

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

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

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

但是,第二张图片适用于 whenl是一个ref参数:

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

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

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

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

于 2012-09-09T18:54:02.923 回答
2

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

实际上,您可能正在对只有堆栈的机器进行编程,或者您可能有可用的寄存器。归根结底,这只是一个实现细节。最接近现实的模型取决于您运行的机器架构。

于 2012-09-09T19:01:06.927 回答
2
  1. 我们可以强制在堆栈上分配引用类型吗?

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

尽管这是一个可以写一本书的主题,但归根结底是:

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

我写了一篇博客文章,其中有些详细,并试图从概念上解释它是如何在较低级别上工作的。但是我在那里的解释主要基于 x86 架构,并不是所有东西都是如何实现的。C# 和 .NET 框架以及 JIT 可以如何改变,但希望它有助于使其更清晰一点。

于 2015-04-23T14:12:52.877 回答
1

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

于 2012-09-09T18:55:18.617 回答
0
  1. 第一个是最正确的。参考是一个数字,复制时复制地址。在 c 中,您实际上可以将引用放入整数中,而在 c# 中,您可以多看一眼。

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

于 2012-09-09T18:59:15.370 回答
0

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

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.Methodl

但这仅因为ref关键字而成立。

于 2012-09-09T19:07:55.077 回答
0

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

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

于 2012-09-09T19:08:41.820 回答