如何非递归地定义"原始"类型?

Dan*_*Tao 19 .net c# primitive struct value-type

由于structin C#由其成员的位组成,因此您不能拥有T包含任何T字段的值类型:

// Struct member 'T.m_field' of type 'T' causes a cycle in the struct layout
struct T { T m_field; }
Run Code Online (Sandbox Code Playgroud)

我的理解是上述类型的实例永远不能被实例化* - 任何尝试这样做会导致实例化/分配的无限循环(我猜这会导致堆栈溢出?**) - 或者,另外,另一个看待它的方式可能是定义本身没有意义; 也许这是一个弄巧成拙的实体,有点像"这句话是假的".

但奇怪的是,如果你运行这段代码:

BindingFlags privateInstance = BindingFlags.NonPublic | BindingFlags.Instance;

// Give me all the private instance fields of the int type.
FieldInfo[] int32Fields = typeof(int).GetFields(privateInstance);

foreach (FieldInfo field in int32Fields)
{
    Console.WriteLine("{0} ({1})", field.Name, field.FieldType);
}
Run Code Online (Sandbox Code Playgroud)

...您将获得以下输出:

m_value (System.Int32)

看来我们正在"骗"到这里***.显然,我的理解是原始类型,如int,double等必须在C#的肠子深跌一些特殊的方法(你不能在系统方面的系统中定义每一个可能的单位来定义......可以吗? -不同的话题, 而不管!); 我只是想知道这里发生了什么.

System.Int32类型(例如)如何实际考虑存储32位整数?更一般地说,值类型(作为一种值的定义)如何包含其类型本身的字段?它看起来像乌龟一直在下降.

黑魔法?


*单独注意:这是值类型("实例化")的正确单词吗?我觉得它带有"类似参考"的内涵; 但也许那就是我.此外,我觉得我之前可能会问过这个问题 - 如果是这样,我会忘记人们回答的问题.

**Martinv.LöwisEric Lippert都指出,这既不完全准确也不恰当.有关详细信息,请参阅其答案.

***好吧,我发现没人在撒谎.我并不是故意暗示我认为这是假的 ; 我的怀疑是它在某种程度上过于简单化了.在了解了(我认为)thecoop的答案之后,它对我来说更有意义.

the*_*oop 11

据我所知,在存储在程序集中的字段签名中,存在某些硬编码字节模式,表示"核心"基本类型 - 有符号/无符号整数,浮点数(以及字符串,它们是引用类型和一个特例).CLR本身就知道如何处理这些问题.查看CLR规范的第II部分第23.2.12节,了解签名的位模式.

各基元中结构([mscorlib]System.Int32,[mscorlib]System.Single在BCL等)是天然类型的单个场,并且因为一个结构是完全相同的大小作为其构成的字段相同,每个基元结构是作为存储器中的其天然型相同的比特模式,并且所以可以通过CLR,C#编译器或使用这些类型的库来解释.

从C#, int,double等都是mscorlib程序结构,其中各自具有它们是本身由CLR识别的类型的原始字段的同义词.

(这里有一个额外的复杂性,因为CLR规范指定任何具有'short form'的类型(本机CLR类型)总是必须编码为short form(int32),而不是valuetype [mscorlib]System.Int32.所以C#编译器知道原始类型为好,但我不知道的是,在C#编译器和CLR,比如说那张精确语义和特殊套管,方法调用原始结构)

因此,由于Godel的不完备性定理,必须有一个"外部"系统可以定义它.这就是让CLR将4个字节解释为本机int32或实例的魔术[mscorlib]System.Int32,它是C#的别名.


Eri*_*ert 7

我的理解是,上述类型的实例永远不会被实例化,任何尝试这样做都会导致实例化/分配的无限循环(我想这会导致堆栈溢出?) - 或者,另外一种看待它的方式可能是定义本身没有意义;

这不是表征情况的最佳方式.更好的方法是查看每个结构的大小必须明确定义.确定T大小的尝试进入无限循环,因此T的大小没有明确定义.因此,它不是一个合法的结构,因为每个结构必须具有明确定义的大小.

看来我们在这里撒谎了

没有谎言.int是包含int类型字段的结构.int具有已知大小; 根据定义,它是四个字节.因此,它是一个合法的结构,因为它的所有字段的大小都是已知的.

System.Int32类型(例如)如何实际存储32位整数值

类型没有任何事情.类型只是一个抽象概念.执行存储的是CLR,它通过在堆上,堆栈上或寄存器中分配四个字节的空间来实现.如果不是四个字节的内存,你怎么想存储一个四字节的整数呢?

用typeof(int)引用的System.Type对象如何呈现自身,好像这个值本身就是一个类型为System.Int32的日常实例字段?

那只是一个对象,用任何其他对象编写代码.没什么特别的.您可以在其上调用方法,它返回更多对象,就像世界上的其他所有对象一样.为什么你认为它有什么特别的东西?

  • 你做得很好,听起来非常简单; 但是,对我来说,你的答案不能真正澄清问题.也许(可能?)我只是密集.你说:"int是一个包含int类型字段的结构"; 对我来说,这听起来很圆,就像说:"X是一个包含X的盒子." 您可以在该声明的末尾添加"......并且X也有一个已定义的大小",但它仍然感觉循环和令人困惑(对我而言).我意识到一个类型是一个概念(我自己的选择不好); 令我困惑的是,在`int`的情况下,似乎这个概念被用来定义自己...... (2认同)

Mar*_*wis 5

三个评论,除了thecoop的回答:

  1. 你断言递归结构本身不起作用并不完全正确.它更像是一句"这句话是真的":如果是这样的话就是如此.有一个类型T是合理的,其中唯一的成员是T类型:例如,这样的实例可能消耗0个字节(因为它的唯一成员消耗0个字节).如果您有第二个成员(这是不允许它们的原因),递归值类型只会停止工作.

  2. 看看Mono对Int32的定义.如您所见:它实际上一个包含自身的类型(因为int只是C#中Int32的别名).涉及肯定有"黑魔法"(即特殊外壳),正如评论所解释的:运行时将按名称查找字段,并且只是期望它在那里 - 我还假设C#编译器将特殊情况下存在在那里.

  3. 在PE程序集中,类型信息通过"类型签名blob"表示.这些是类型声明的序列,例如用于方法签名,但也用于字段.这种签名中可用的基元类型列表在CLR规范的第22.1.15节中定义; 允许值的副本位于CorElementType枚举中.显然,反射API将这些基元类型映射到它们对应的System.XYZ值类型.