C# ValueType.GetType()如何确定结构的类型?

C# ValueType.GetType()如何确定结构的类型?,c#,.net,types,C#,.net,Types,对于引用类型,对象的内存布局为 | Type Object pointer| | Sync Block | | Instance fields...| 对于值类型,对象布局似乎是 | Instance fields...| 对于引用类型,GetType意味着从“类型对象指针”中查找对象。给定引用类型对象的所有对象都指向同一类型对象(该类型对象还具有方法表) 对于值类型,此指针不可用。那么GetType()是如何工作的呢 我通过谷歌查了一下,发现了这个片段。。这有点模糊。有

对于引用类型,对象的内存布局为

| Type Object pointer|
|    Sync Block      |
|  Instance fields...|
对于值类型,对象布局似乎是

|  Instance fields...|
对于引用类型,GetType意味着从“类型对象指针”中查找对象。给定引用类型对象的所有对象都指向同一类型对象(该类型对象还具有方法表)

对于值类型,此指针不可用。那么GetType()是如何工作的呢

我通过谷歌查了一下,发现了这个片段。。这有点模糊。有人能详细说明一下吗

解决方案是,在 存储的值只能存储 特定类型的值。这是 由验证者保证。

在值类型上调用
GetType()
,将框中的值类型。通过将值类型移动到堆上,您现在有了一个引用类型,它现在有一个指向该对象类型的指针

如果希望避免装箱,可以调用它,它返回一个枚举,指示值类型的类型,而不装箱

下面是一个示例,显示发生的装箱:

C#:

class Program
{
    static void Main()
    {
        34.GetType();
    }
}
.method private hidebysig static void Main() cil managed
{
        .entrypoint
        .maxstack 8
        L_0000: ldc.i4.s 0x22
        L_0002: box int32
        L_0007: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
        L_000c: pop 
        L_000d: ret 
}
IL用于
Main()

class Program
{
    static void Main()
    {
        34.GetType();
    }
}
.method private hidebysig static void Main() cil managed
{
        .entrypoint
        .maxstack 8
        L_0000: ldc.i4.s 0x22
        L_0002: box int32
        L_0007: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
        L_000c: pop 
        L_000d: ret 
}

编辑:要显示编译器正在执行的操作,让我们更改文本的类型,如下所示:

class Program
{
    static void Main()
    {
        34L.GetType();
    }
}
.method private hidebysig static void Main() cil managed
{
        .entrypoint
        .maxstack 8
        L_0000: ldc.i4.s 0x22
        L_0002: conv.i8 
        L_0003: box int64
        L_0008: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
        L_000d: pop 
        L_000e: ret 
}
通过在文本后添加
“L”
,我告诉编译器我希望将该文本转换为
System.Int64
。编译器看到了这一点,当它发出
指令时,它看起来是这样的:

class Program
{
    static void Main()
    {
        34L.GetType();
    }
}
.method private hidebysig static void Main() cil managed
{
        .entrypoint
        .maxstack 8
        L_0000: ldc.i4.s 0x22
        L_0002: conv.i8 
        L_0003: box int64
        L_0008: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
        L_000d: pop 
        L_000e: ret 
}
正如您所看到的,编译器已经完成了确定要发出的正确指令的艰苦工作,之后由CLR来执行它们。

也许Andrew H.认为这是显而易见的,并努力让我理解+1。我的电灯时刻来自Jon Skeet。。再一次(这次是通过我碰巧正在读的他的书,……以及答案所在的确切区域

  • C#是一个静态类型。每个变量都有一个类型,并且在编译时已知
  • 无法继承值类型。因此,VT对象不需要携带额外的类型信息(与Ref-type对象相反,每个对象都有一个对象类型头,因为变量类型和值/对象类型可能不同。)
考虑下面的代码段。虽然变量类型是BaseRefType,但它指向一个更特殊类型的对象。对于值类型,由于继承是非法的,所以变量类型是对象的类型

BaseRefType r = new DerivedRefType(); 
ValueType v = new ValueType();
我丢失的是子弹1号。

但它如何知道如何正确设置装箱对象的类型对象指针?所有值类型都会像字节流。例如,一个1 int的structA后跟另一个2 int的structB会像3 int。struct变量指向第一个实例字段的开头。那么它如何确定类型信息呢n?请参阅我发布的示例-注意,“box”指令给定的是要装箱的类型(由编译器对隐式装箱的支持提供)。此类型用于设置正确的对象类型指针。假设int a=34,现在a只指向内存中的一个整数。没有其他内容。现在CLR如何知道当我输入框a时,我需要传入[int32]作为box的参数。我希望我说得很清楚..CLR如何将特定内存块映射到值类型对象?@Gishu:CLR不这样做-编译器这样做。当编译器将int装箱时,它将指令作为“box int32”放入。这在编译时总是已知的,因为值类型无法继承。CLR只处理“box int32”在堆栈中的对象上。您已经回答了标题中发布的问题-尽管这是一个涉及装箱的特定案例..我的基本问题更多的是,如果对象实例本身不包含任何类型标识符,如何确定值类型的类型和方法表。稍后找到我的答案..发布在此线程中。thanks.+1顺便说一句,这是一个非常好的问题。我不能说我是这里确切细节方面的专家,但我建议您看看IL规范(ECMA-335),特别是“受限”操作代码。我相信,这就是允许在不装箱的情况下调用虚拟方法的原因。(编译调用GetHashCode()和GetType()的代码).话虽如此,您认为它是两个指针的想法也是错误的。r的值是对对象的引用,而对象本身有对其实际类型的引用。v的值只是变量的值,但编译器和运行时知道确切的类型(由于缺少值类型继承)是的,对于第二次GetHashCode方法调用,我确实在IL中看到了一条受约束的指令。那么,变量的类型存储在哪里?为了让编译器和运行时确定;给定一个任意变量,它只指向托管堆上的RT对象或VT对象的开头。那么,您找到问题的答案了吗变量的类型存储在哪里?@waheed:编译器知道变量的类型(存储在用于存放标识符/符号的任何内部数据结构中)。RefType对象必须携带其类型的原因是'Base b=new-Derived()“。即使var是Base类型,对象也是diff类型派生的。对于值类型,情况并非如此,因为不允许继承。ValType v将始终是ValType的对象。无需将该信息与每个对象一起携带。它在编译时是“一成不变”的。使用
ValueType可能有点混淆。”在示例中,因为
System.ValueType
实际上是一个引用类型。