【问题标题】:how do Value Types in .NET actually work?.NET 中的值类型实际上是如何工作的?
【发布时间】:2012-10-16 19:08:08
【问题描述】:

我在理解 .NET 中的值类型表示时遇到了问题。 每个 Value Type 都派生自 System.ValueType 类,那么这是否意味着 Value Type 是一个类?

例如,如果我写:

int x = 5;

这意味着我创建了一个 System.Int32 类的实例并将其写入变量 x??

【问题讨论】:

标签: c#


【解决方案1】:

System.ValueType 类实际上只是一个“元”类,其内部处理方式与普通类不同。结构和原始类型隐式继承 System.ValueType,但该类型在运行时实际上并不存在,它只是程序集标记应将类视为值类型结构并采用按值传递语义的一种方式。

与其他答案相反,值类型并不总是在堆栈上分配。当它们是类中的字段时,它们就像实例化该对象时的其他类数据一样位于堆上。在迭代器或闭包中使用时,局部变量也可以提升到隐式类中。

【讨论】:

    【解决方案2】:

    见Eric Lippert's article。

    简而言之,值类型是按值复制的。值类型就像引用类型一样是类,值类型的实例是对象。 5是一个对象,是System.Int32的一个实例(简称int)。

    【讨论】:

      【解决方案3】:

      现在,有很多话要说。说真的,网上关于这个话题的内容大约有数百页,其中大部分内容以惊人的速度烧毁了我的大脑。

      我真正记得的是,int 和 Int32 是同一个东西,它们都像一个结构体,而不是一个类。 “int”是 Int32 的快捷方式。 (如果您将鼠标放在一个 int 上,工具提示会显示为“struct System.Int32”。 这意味着:

      public class BoxedInt
      {
          public int x;
      
          public BoxedInt(int i) { x = i; }
      }
      
      public Test()
      {
          BoxedInt bi = new BoxedInt(10);
          Boxed(bi);
          Console.WriteLine(bi.x); // Returns 11, as any reference type would.
      
          int vi = 10;
          Valued(vi);
          Console.WriteLine(vi); // Returns 10, because it acts like a struct. (Which it is)
      }
      
      public void Boxed(BoxedInt i)
      {
          i.x++;
      }
      
      public void Valued(int i)
      {
          i++;
      }
      

      【讨论】:

      • public void Valued(ref int i) 将返回 11,因为 ... ?
      • 盒装返回 11,有值返回 10。
      • 一个装箱的值,或者如果您愿意,包含在类中的值就像在一个盒子里。如果我告诉您该框在哪里并且您更改了它的内容,我可以选中该框并查看您所做的更改。为此,该类充当一个盒子,只要代码中的某处引用它,它就会存在。至于正常值,我可以告诉你那个值,但我不能给你。如果你决定它应该改变,除非你明确告诉我,否则我无法知道。为此,“i”仅存在于 Valued 方法的边界内,并在方法完成后立即销毁。
      • 所以,( ref int i) 将返回 11,因为?
      • 因为“ref”关键字允许您通过引用传递它...就像在引用类型(类)中装箱一样。我在示例中的一个类中手动将其装箱以显示这一点。
      【解决方案4】:

      值类型不是类。这些是语言中的内置类型,由 .NET CTS(通用类型系统)处理。 CTS 是 CLR 内部的一个组件,负责处理 bool、int、double、float 等值类型。 当您创建一个值类型的对象时,它会在堆栈上创建。当您将它传递给函数或从函数返回时,会在 ram 中为它创建一个新对象。但是在引用类型的情况下,只传递对象的引用并且不会在内存中创建新对象。 有一些用户定义的值类型不在语言规范中,而是由用户定义的。此类值类型继承自 System.ValueType。 这种值类型的示例是复数。我们可以使用 System.ValueType 创建我们自己的用户定义的复数类型。

      【讨论】:

      • 我不会投反对票,因为这是您在 SO 上的第一天,但​​请阅读其他答案的 cmets
      • 我将投反对票,因为这是错误的。请参阅 blogs.msdn.com/b/ericlippert/archive/2009/04/27/… 以了解对值类型的全面处理。
      • @ThomSmith:虽然堆栈是一个实现细节,但基本点是正确的:值类型存储位置(包括编译器临时文件)不保存类对象。 C# 规范可能声称 Int32 字段的内容继承自 System.Object,但 CLI 规范(描述托管代码世界的实际工作方式)清楚地表明 System.Int32 描述了两种类型:堆对象派生自System.Object 的类型和不是对象的存储位置类型。 C# 规范可能会假装不这样,但这并不代表它是真的。
      • C# 规范不会“假装”其他情况——它定义其他情况,这确实是真的。在 C# 中,Int32 是一个对象。 CLI 规范中的实现细节是 CLI 实现细节。
      猜你喜欢
      • 2012-04-06
      • 2016-07-24
      • 1970-01-01
      • 2021-01-21
      • 2011-09-27
      • 2021-12-16
      • 2013-03-14
      • 2021-03-23
      • 2011-02-11
      相关资源
      最近更新 更多