在设计CLI语言时必须考虑的第二个设计层面是要将什么程度的底层CLI实现模型结合到该语言中。这个语言解决什么样的问题?要解决这些问题必须要什么样的工具? 此外,该语言很可能吸引哪一类程序员?
下面,我们利用发生在托管堆中的值类型问题。在许多情况下,值类型可以在托管堆中找到自己:
通过隐式的框入/框出操作(boxing)——当值类型的某个实例被赋值给一对象时,或者通过某个未被改写的值类型调用一个虚拟方法时;
当值类型被当作为引用类类型的成员时;
当值类型被当作CLI数组元素存储时;
是否允许程序员处理这种值类型地址是设计CLI语言时必须要解决的问题。
存在的问题?
位于托管堆中的任何对象在垃圾回收器进行清扫收缩的过程中都有可能遭遇重新分配,指向这些对象的任何指针必须被追踪并在运行时得到更新,而程序员 无法自己手动追踪它们,因此,如果你被允许用某个可能在托管堆中的值类型的地址,那么除了本机指针外,还需要一个追踪形态的指针。
到底该怎样去权衡呢?一方面,需要考虑简洁和安全。直接引入对一个或一组追踪指针的支持会使语言变得更复杂。如果不提供这种支持,由于所需的复杂程度降低,从而可以找到的程序员人群就会增加。此外,允许程序员访问这些生命期短暂的值类型,则增加了程序员出错的可能性。她经意或不经意地对内存做一些危险动作。不支持追踪指针,可以潜在地创建较安全的运行时环境。
另一方面,必须考虑效率和灵活性。每次将值类型赋值给相同的对象,该值都会发生新的框入/框出操作。允许访问这种经过框入/框出操作的值类型 ,就允许在内存中进行更新操作,这样便可能提供重要的性能改进。没有某种形式的追踪指针,你将无法用指针算法遍历CLI数组,这意味着CLI数组将不能 融入STL(标准模板库)中的迭代器模式,也无法与泛型算法协同工作。允许访问框入/框出值类型将会大大提高设计的灵活性。
在C++/CLI 中,微软选择提供一系列在托管堆中处理值类型的寻址模式:
int ival = 1024;
int^ boxedi = ival;
array<int>^ ia = gcnew array<int>{1,1,2,3,5,8};
interior_ptr<int> begin = &ia[0];
value struct smallInt { int m_ival; ... } si;
pin_ptr<int> ppi = &si.m_ival;
典型的 C++/CLI 开发人员是一个经验丰富的系统程序员,其任务是提供底层架构以及作为基础的核心应用,以此为基础来构建未来。她必须解决可伸缩性和性能相关的问题,并且必须从系统一级来看待底层 CLI。某种 CLI 语言的细节标准反映了其程序员的面貌。
复杂性本身并不是对质量的否定,人类生命比单核细胞复杂得多,这当然不是一件坏事,然而,当单一概念的表达变得复杂化以后,这常常被认为是一件坏事。在C++/CLI中,CLI开发团队已经 尝试提供一种优雅的方式来表达一个复杂的主体。
附加功能
第三个设计层面是特定语言层功能要超过被CLI直接支持的功能,这样就需要建立一种语言层支持与CLI底层实现模型之间的映射。