对象类 基本类型 栈和堆

7

首先,如果听起来太愚蠢,我深表歉意。但我总是在问以下问题。

由于对象类是 .Net 中所有类的最终基类,类是引用类型,引用类型存储在堆上,那么任何东西如何进入堆栈。 我知道原始类型存储在堆栈上,这就让我感到困惑了。

以 int32 为例,如果它是一个类,其基类是 object,那么它是一个引用类型,应该存储在堆上。它如何进入堆栈。

如果不是一个类,那我们怎么做呢?

int i = 1;
object obj = i;  

由于我无法像这样做:

int i = 1;
MyClass myObj = i;

编辑

根据我得到的答案,int是结构体而不是类。仍然有一个困惑,那就是一个值类型的基类是引用类型(object),它是一个值类型而不是引用类型。

另一个提出的观点是,“属于类的值类型不会存储在堆栈上”,我猜一切都在一个类中,即使在控制台程序中,Main方法也在类中。对我来说,这意味着所有的变量都属于一个类?那它们如何进入堆栈?


1
值类型继承自类型 System.Object,但这并不意味着它继承自类。类型和类是不同的东西。 - Rodrick Chapman
@RodrickChapman System.Object是一个类。在这种情况下,类型和类有什么不同? - ZedBee
1
“类”和“结构体”是您定义类型的方式,但您不会从类或结构体继承,而是从类型继承。 - Rodrick Chapman
5个回答

15

If a value type is not a reference type, then how can we store it on the heap?

int i = 1;
object obj = i;  

好问题。会生成一个特殊的类。可以把它想象成:

class BoxedInt 
{
    int value;
    public BoxedInt(int v) { this.value = v; }
    public int Unbox() { return this.value; }
}

那么你的程序片段是:

int i = 1;
object obj = new BoxedInt(i);

然后

int j = (int)obj;

等同于

int j = ((BoxedInt)obj).Unbox();

另外一个提出来的问题是,“属于类的值类型不会被存储在堆栈中”。

更好的说法是:引用类型的字段是长时间存在的变量,因此它们存储在堆上。

我想所有的东西都在类里面,即使Main方法在控制台程序中也是如此。

是的,所有的代码都在类或结构体中。

这对我来说意味着所有的变量都属于一个类?

不,局部变量不是类的成员。

我住在一间黄色的房子里。里面的所有物品都是黄色的吗?

那么它们如何进入堆栈?

普通方法中的局部变量生命周期短暂,所以它们被放置在堆栈上。

不要再考虑堆栈与所存储数据类型之间有关系。事实上,堆栈与数据类型完全无关。应该思考变量。堆栈是短期变量的存储位置,而长期变量则存储在堆中。变量的类型是无关紧要的。


Eric:当你说原始类型的概念是“无意义”的时候,我很好奇你的意思是什么。我承认这个区别与这个问题无关,但你是指更普遍的情况吗?C#语言规范和.NET Framework文档都提到了它们,我很好奇是否我错过了你所要表达的更重要的观点。 - Cole Campbell
@ColeCampbell:C#规范在没有定义的情况下使用了一次这个术语,所以在这个背景下它并不是一个有用的术语。“毫无意义”可能有些过于强烈;我更愿意问:什么是“此类型是否为原始类型?”谓词的合理使用案例呢?在什么情况下,你会在实际程序中使用这样的谓词来做出决策呢? - Eric Lippert
我曾经想说,这种区别有时对于我们偶尔深入不安全代码的黑暗世界的人来说很重要。然而,如果我要精确地说,那里面重要的问题不是类型是否为“原始类型”,而是它们是否为“可平坦化类型”,虽然两者之间有很大的重叠,但它们并不完全相同。所以我理解你的观点。谢谢你的澄清。 - Cole Campbell
@ColeCampbell:确实;struct S { int x; int y; }显然不是原始的,无论这意味着什么,但它绝对是可平坦化的。 (或者按照C#规范所说,“未托管”,在我看来有点用词不当。) - Eric Lippert
@EricLippert,您能否在您的回答中提供您在评论中提到的文章的链接?谢谢。 - ZedBee
显示剩余3条评论

1

Int32 不是一个类。它是一个结构体。而结构体被编译器特别处理。

object obj = i;  

这被称为装箱。因为所有的类都隐式继承自object,所以你可以把任何东西赋值给object。你应该查看文档,同时互联网上有成千上万的关于装箱和拆箱的文章。
MSDN:

装箱是将值类型转换为类型对象或由该值类型实现的任何接口类型的过程。当CLR对值类型进行装箱时,它将值包装在System.Object中并将其存储在托管堆上。

以下是一些可能有用的参考资料。
  1. C# 参考 | 结构体
  2. C# 参考 | 类型
  3. 值类型和引用类型
  4. 装箱和拆箱
  5. 关于值类型的真相 - Eric Lippert

- 相关问题

  1. 引用类型和值类型的场景

  2. C#中引用类型和值类型的区别是什么?


1

简短回答:

  • System.Object类型不是“类”,但System.Object类型的实例是引用类型。
  • 当类型B继承自类型A时,唯一需要注意的是B会获得所有Apublicprotected的非静态方法、属性和事件。
  • 类是引用类型,总是分配在堆上。
  • 结构体是值类型,可能分配在栈、堆或CPU寄存器中。

基本上,值类型可以有两种不同的内存表示形式(装箱和非装箱),而引用类型只有一种内存表示形式。


更长的回答:

首先,您绝对可以做到这一点:

int i = 1;
MyClass myObj = i;

只需将MyClass定义为:
class MyClass
{
    int i;
    
    public MyClass(int i)
    {
            this.i = i;
    }
    
    public static implicit operator MyClass(int i)
    {
        return new MyClass(i);
    }
}

(Note well that in this example, the field, i, of the class MyClass is heap-allocated even though it is a value type)
其次,值类型并不一定像以下情况那样存储在堆栈中:
  • 属于class的值类型字段
  • 封闭本地值类型变量(即被闭包捕获的变量)
现在,当类型B继承自类型A时,这意味着只有B获得了A的所有属性方法事件。它并没有规定如何分配B的实例。

A 获取 B 的公共部分?还是 B 获取 A 的公共部分..?? - ZedBee
现在重点是 - 属于类的值类型不会存储在堆栈上,我猜一切都在类内部,甚至控制台程序中的 Main 方法也是如此。对我来说,这意味着所有变量都属于某个类?那么它们如何进入堆栈? - ZedBee
前两个句子是不正确的。System.Object 是一个类。当 B 继承自 A 时,B 获取了所有 A 的成员,无论它们是公共的、私有的、受保护的还是内部的,也无论它们是静态的还是实例的。BA 那里没有继承构造函数和析构函数,因为它们是不可继承的。你混淆了成员是否“可继承”和是否“可访问”;这是两个非常不同的概念。 - Eric Lippert
例如考虑以下代码:class A { private int x; class B : A { int M() { return this.x; } } }。仅仅因为 x 是私有的并不意味着它没有被继承 - Eric Lippert

1

Int32是一个值类型,因此它将被分配到堆栈中。关键字int指的是结构体System.Int32


.net中的类型分为值类型引用类型

值类型要么是在堆栈上分配的,要么是内联分配在结构中。

引用类型是在堆上分配的。

值类型包括两个主要类别:

  • 结构体{bool,数值,自定义}
  • 枚举

引用类型和值类型都派生自最终基类Object

在需要值类型像对象一样运作时,会在堆上分配一个使值类型看起来像引用对象的包装器,并将值类型的值复制到其中。包装器被标记,以便系统知道它包含一个值类型。这个过程称为装箱,反向过程称为拆箱。

请参见此处


0

原始数据类型是值类型,它们的大小在编译时定义,因此编译器将它们推送到堆栈上以实现更快的访问,而引用类型只是指向存储在堆上的对象起始地址的指针,其内存不是在编译时分配的(而是在运行时分配)。

你不能这样做

 int i  = null; // Because it's value type and need some value 
 (Default is 0) here int is alias to Int32 which is structure and
  structure is value type and same is for other primitives 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接