zhouqijie

类的封送



封送类对象

类和结构体有很多相似之处,因而仍然可以用前面方法控制封送行为,例如StructLayoutFieldOffsetMarshalAs等特性都可以用在类上,以达到一样的效果。

要注意的是,由于类(引用类型)和结构体(值类型)在托管代码中存在本质区别,这就导致平台调用时二者行为会有所差异。直接传递类对象给非托管函数时,可以认为是将引用(指针)传递给了非托管函数。

然而,虽然非托管函数收到了指向类对象的指针,但它对实例的修改却不一定能返回给调用方,需要指定为[In,Out],否则CLR会默认为[In]类型以提升性能。



字段都是blittable的应用类型

上面提到,由于引用类型作为参数传递时,默认设置为In属性,因此无法将非托管代码对数据的修改返回给调用方。但是由一种特殊的引用类型的默认封送行为会有所区别。如果引用类型的所有字段都是blittable类型,那么在封送参数时,即使不指定Out特性,也能得到非托管代码更新后的结果。

前面还提过,除了blittable类型本身,CLR也会把下面两种类型当作blittable类型处理:一是只包含blittable类型的一维数组。二是只包含blittable类型字段的类。

CLR为了优化平台调用性能,在封送这两种类型的数据时,封送拆收器会把它们的实例Pin在托管内存中,并将指向该内存的指针传递给非托管函数,这样就无需再将托管数据复制到非托管内存中了。并且无需[In,Out]也能得到非托管代码修改过的数据。

CRE:但是为什么结构体引用封送到非托管代码后和托管代码中的内存地址一样,而托管的类对象封送到非托管代码后内存地址却不一样呢?



(END)