C# Int32: m_value

6
阅读了一些关于C#中Int32结构体的内容后,我意识到intInt32是同义词。在Int32结构体源代码中,有一个internal int m_value字段。
如果我的知识是正确的,我们赋给int变量的值存储在m_value中(我是对的吗?)。但是我的疑问是,在我们执行int i = 7;时,值是如何存储在m_value中的。
我没有在Int32结构体的源代码中看到任何隐式操作符,因此值无法存储在m_value中。
有人可以帮我解决这个问题吗?

1
投票支持重新开放。这些是不同的问题。 - Soner Gönül
@SonerGönül:我不知道之前提出的重复问题,但这似乎回答了OP的问题(实际上是:当我分配一个int变量时,如何初始化System.Int32):http://stackoverflow.com/questions/25449498/instantiation-and-initialization-of-value-types-vs-reference-types _“int是一种内置类型。编译器知道如何为它生成IL。int的类型被“烘焙”到编译器为赋值生成的CIL中:.locals init ([0] int32 a) ldc.i4.s 5 stloc.0"_ - Tim Schmelter
@TimSchmelter 这就是这个 - James Thorpe
@TimSchmelter 你可以在编辑历史中看到它 - 在第2次和第3次编辑之间。 - default
@TimSchmelter 那是另一个被投票为重复的问题。是的,你链接的那个问题更合适。 - Soner Gönül
3个回答

5

int 在 C# 中代表着与 CIL 中的 int32 相同的东西,它是一个 4 字节的原始数据类型,通常被视为有符号数(尽管 CIL 可以在没有强制转换的情况下对其进行无符号操作)。

它是我们可以用来创建更复杂的结构和类的最低级构建块之一。

但是作为这样一个构建块,它本身没有定义任何方法。

System.Int32 看起来很像一个包装了 int/int32 的结构体,并提供了一些方法。

让我们把它看作是这样的;想象一下在没有将 int 别名化为 System.Int32 的世界中,它会是什么样子:

在这种假设的情况下,我们只能在将其视为特殊的“装箱 int”类型时使用 System.Int32 提供的方法,在需要时从 int 创建一个 System.Int32,并在需要时再次提取 int

因此,如果没有别名进行 (3).CompareTo(2),我们将不得不执行以下操作:

new System.Int32{m_value = 3}.CompareTo(2)

但要考虑到,int的内存表示是4个字节,System.Int32的内存表示也是相同的4个字节。如果我们没有一个强类型系统来禁止将一种类型视为另一种类型,那么我们随时都可以将一种类型视为另一种类型。

现在,C#不允许我们这样做。例如,我们不能执行以下操作:

public struct MyInt32
{
  private int _value;
}
/* … */
MyInt32 = 3;

我们需要添加一个cast方法来调用,否则C#将拒绝像这样工作。
但是,CIL没有这样的规则。它可以随时将一个类型视为另一个兼容布局的类型。因此,(3).CompareTo(2) 的IL代码如下:
ldc.i4.3 // Push the 32-bit integer 3 on the stack.
ldc.i4.2 // Push the 32-bit integer 2 on the stack.
call instance int32 [mscorlib]System.Int32::CompareTo(int32)

最后的调用假设3是一个System.Int32,并将其调用。这违反了C#类型安全的规则,但这些规则不是CIL的规则。 C#编译器也不必遵循其强制执行的所有规则。
所以没有必要把任何东西放入m_value中,我们只需说“哦,那四个字节,它们是System.Int32m_value字段”,于是就神奇地完成了。(如果您了解C或C ++,请考虑一下如果您有两个具有等效成员的结构体,并将指向其中一种类型的指针转换为void *,然后再转换回另一种指针会发生什么。这是一种不好的做法,并且我IRC未定义而不是保证,但更低级别的代码允许进行这些操作)。
这就是别名如何工作的方式; .Net语言的编译器特别处理需要调用基元方法以执行此类C#代码不允许的类型强制转换的情况。同样,它特别处理了值类型不能持有自己类型的字段的事实,并允许System.Int32具有int字段,尽管通常不允许struct S { public S value; }

0

这就是语言的工作方式,伙计!

在该语句中

int i = 7;

你正在创建一个 Int32 变量(即 int i 部分)。然而,你正在将它赋值为 7。在 C#(以及许多其他语言)中,7 被称为字面量。这意味着 7 已经是 Int32 的实例了!就像这样:
假设你有一个类
public class ClassA {
    public int i;
}

还有一个 ClassA 的实例:

ClassA obj = new ClassA ();
obj.i = 1;

然后你这样做:

ClassA a = obj;

你说:“我没有看到 ClassA 中有任何隐式操作符,使得值可以存储在 i 中。”

但是 obj 已经是一个合法的对象!就像 7 一样。


你已经引入了一个 new ClassA(),所以它不是类似的。要类似,它必须是 ClassA obj = 1 并且 没有定义任何隐式运算符。这在 C# 中行不通,但 C# 在进行别名时并不需要遵守 C# 所强制执行的规则。 - Jon Hanna

0

阅读核心框架源代码时,很难跟上正在发生的事情。

“当我们给出int i = 7;时,值如何存储在m_value中?”

首先,当C#编译器看到int时,它只是假装你说了System.Int32(*)。同样,当它看到7时,它会说:“啊哈!那是一个整数字面量。我将把它存储在System.Int32中”。然后它创建变量i(正确类型的变量),并用它创建的值初始化它。

(*)这意味着源http://referencesource.microsoft.com/#mscorlib/system/int32.cs,225942ed7b7a3252有:

public struct Int32 : *various bases*
{
    internal Int32 m_value;

...这有点令人困惑(而且通常不合法)。


2
接口不是基类。 - Bauss

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