所有独立的堆对象都继承自Object
; 这是有道理的,因为所有独立堆对象必须具有某些共同的方面,例如识别其类型的方法。否则,如果垃圾收集器对未知类型的堆对象进行了引用,则它将无法知道与该对象关联的内存blob中的哪些位应被视为对其他堆对象的引用。
此外,在类型系统内,使用相同的机制定义结构的成员和类的成员很方便。值类型存储位置(变量,参数,字段,数组插槽等)的行为与类类型存储位置的行为有很大不同,但是这种行为差异是在源代码编译器和执行引擎(包括JIT编译器),而不是在类型系统中表示。
这样的结果是,定义值类型实际上定义了两种类型-存储位置类型和堆对象类型。前者可以隐式转换为后者,后者可以通过类型转换转换为前者。两种类型的转换都通过将所有公共字段和私有字段从相关类型的一个实例复制到另一实例来工作。此外,可以使用通用约束直接在值类型存储位置上调用接口成员,而无需先复制它。
所有这些都很重要,因为对值类型堆对象的引用的行为类似于类引用,而不是值类型。例如,考虑以下代码:
字符串testEnumerator <T>(T it)其中T:IEnumerator <string>
{
var it2 = it;
it.MoveNext();
it2.MoveNext();
返回它。
}
公共无效测试()
{
var theList = new List <string>();
theList.Add(“ Fred”);
theList.Add(“ George”);
theList.Add(“ Percy”);
theList.Add(“ Molly”);
theList.Add(“ Ron”);
var enum1 = theList.GetEnumerator();
IEnumerator <string> enum2 =枚举1;
Debug.Print(testEnumerator(enum1));
Debug.Print(testEnumerator(enum1));
Debug.Print(testEnumerator(enum2));
Debug.Print(testEnumerator(enum2));
}
如果将testEnumerator()
方法传递给值类型的存储位置,it
则将收到一个实例,该实例的公共和私有字段是从传入的值中复制的。本地变量it2
将保存另一个实例,其所有字段均从复制it
。调用MoveNext
上it2
不会影响it
。
如果将上面的代码传递给类类型的存储位置,则传入的值,it
和it2
将全部引用同一个对象,因此MoveNext()
对它们中的任何一个进行调用都将对它们全部有效地对其进行调用。
请注意,强制List<String>.Enumerator
转换IEnumerator<String>
有效地将其从值类型转换为类类型。堆对象的类型是,List<String>.Enumerator
但是其行为与同名的值类型有很大不同。
object
.NET框架中有一个根源,部分原因是它为所有对象提供了一些基本功能,例如ToString()
andGetHashCode()