“原始人"是怎么来的?非递归定义的类型? [英] How are the "primitive" types defined non-recursively?

查看:22
本文介绍了“原始人"是怎么来的?非递归定义的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

//'T' 类型的结构体成员 'T.m_field' 导致结构体布局循环struct T { T m_field;}

我的理解是上述类型的实例永远无法实例化*——任何尝试这样做都会导致实例化/分配的无限循环(我猜这会导致堆栈溢出?**)——或者,另一种看待它的方式可能是定义本身没有意义;也许这是一个自我挫败的实体,有点像这个陈述是错误的."

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

BindingFlags privateInstance = BindingFlags.NonPublic |BindingFlags.Instance;//给我所有 int 类型的私有实例字段.FieldInfo[] int32Fields = typeof(int).GetFields(privateInstance);foreach(int32Fields 中的 FieldInfo 字段){Console.WriteLine("{0} ({1})", field.Name, field.FieldType);}

...你会得到以下输出:

<前>m_value (System.Int32)

我们似乎被骗"到这里***.显然,我明白像 intdouble 等基本类型必须以某种特殊的方式定义在 C# 的深处(你不能在内部定义每个可能的单元)一个系统就该系统而言......你能吗? - 不同的话题,无论如何!);我只是想知道这里发生了什么.

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

黑魔法?

<小时>

*单独说明:这是值类型(实例化")的正确词吗?我觉得它带有类参考"的内涵;但也许那只是我.另外,我觉得我可能之前已经问过这个问题——如果是这样,我忘记了人们的回答.

**Both Martin v.Löwis埃里克·利珀特已经指出,这对于这个问题既不完全准确,也不恰当.查看他们的回答以了解更多信息.

***好吧,我意识到实际上没有人在撒谎.我并不是要暗示我认为这是;我一直怀疑这在某种程度上过于简单化了.了解之后(我认为)thecoop 的回答,对我来说更有意义.

解决方案

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

在 BCL 中的每个原始结构([mscorlib]System.Int32[mscorlib]System.Single 等)中都有一个该原生类型的字段,并且因为结构与其组成字段的大小完全相同,所以每个原始结构与其内存中的本机类型具有相同的位模式,因此可以被 CLR、C# 编译器或使用这些类型的库解释为.

在 C# 中,intdouble 等是 mscorlib 结构的同义词,每个结构都具有 CLR 本地识别的类型的原始字段.

(这里有一个额外的复杂性,因为 CLR 规范指定任何具有短格式"的类型(本机 CLR 类型)始终必须编码为该短格式 (int32),而不是 valuetype [mscorlib]System.Int32.所以 C# 编译器也知道原始类型,但我不确定在C# 编译器和 CLR,例如,原始结构上的方法调用)

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

Since a struct in C# consists of the bits of its members, you cannot have a value type T which includes any T fields:

// Struct member 'T.m_field' of type 'T' causes a cycle in the struct layout
struct T { T m_field; }

My understanding is that an instance of the above type could never be instantiated*—any attempt to do so would result in an infinite loop of instantiation/allocation (which I guess would cause a stack overflow?**)—or, alternately, another way of looking at it might be that the definition itself just doesn't make sense; perhaps it's a self-defeating entity, sort of like "This statement is false."

Curiously, though, if you run this code:

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);
}

...you will get the following output:

m_value (System.Int32)

It seems we are being "lied" to here***. Obviously I understand that the primitive types like int, double, etc. must be defined in some special way deep down in the bowels of C# (you cannot define every possible unit within a system in terms of that system... can you?—different topic, regardless!); I'm just interested to know what's going on here.

How does the System.Int32 type (for example) actually account for the storage of a 32-bit integer? More generally, how can a value type (as a definition of a kind of value) include a field whose type is itself? It just seems like turtles all the way down.

Black magic?


*On a separate note: is this the right word for a value type ("instantiated")? I feel like it carries "reference-like" connotations; but maybe that's just me. Also, I feel like I may have asked this question before—if so, I forget what people answered.

**Both Martin v. Löwis and Eric Lippert have pointed out that this is neither entirely accurate nor an appropriate perspective on the issue. See their answers for more info.

***OK, I realize nobody's actually lying. I didn't mean to imply that I thought this was false; my suspicion had been that it was somehow an oversimplification. After coming to understand (I think) thecoop's answer, it makes a lot more sense to me.

解决方案

As far as I know, within a field signature that is stored in an assembly, there are certain hardcoded byte patterns representing the 'core' primitive types - the signed/unsigned integers, and floats (as well as strings, which are reference types and a special case). The CLR knows natively how to deal with those. Check out Partition II, section 23.2.12 of the CLR spec for the bit patterns of the signatures.

Within each primitive struct ([mscorlib]System.Int32, [mscorlib]System.Single etc) in the BCL is a single field of that native type, and because a struct is exactly the same size as its constituent fields, each primitive struct is the same bit pattern as its native type in memory, and so can be interpreted as either, by the CLR, C# compiler, or libraries using those types.

From C#, int, double etc are synonyms of the mscorlib structs, which each have their primitive field of a type that is natively recognised by the CLR.

(There's an extra complication here, in that the CLR spec specifies that any types that have a 'short form' (the native CLR types) always have to be encoded as that short form (int32), rather than valuetype [mscorlib]System.Int32. So the C# compiler knows about the primitive types as well, but I'm not sure of the exact semantics and special-casing that goes on in the C# compiler and CLR for, say, method calls on primitive structs)

So, due to Godel's Incompleteness Theorem, there has to be something 'outside' the system by which it can be defined. This is the Magic that lets the CLR interpret 4 bytes as a native int32 or an instance of [mscorlib]System.Int32, which is aliased from C#.

这篇关于“原始人"是怎么来的?非递归定义的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆