欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表
最新评论

CLR外部环境-类型的布局(二)

创建时间:2013年05月22日 22:01  阅读次数:(4934)
分享到:
当使用explict布局选项时,必须谨慎地维护CLR的平台无关(platform-agnostic)特性,此点十分重要。尤其是在代码部署到目标机器之前,对象引用的大小、托管指针以及非托管指针都不确定并且是未知的。为此,在使用explicit布局时,需要谨慎对待包含对象引用或者指针的类型,确保每个字段都自充足的空间。如果在编译对explicit布局没有成功,则将导致运行时类型初始化错误(特别是System-TypeLoadException)。另一方面,对于没有对象引用的字段允许重叠(overlapping)是合法的。这是一种在基于CLR程序中实现C风格union(联合)的方式。下面的c#类型说明了这种技术:
using System.Runtime.InteropServices;
[structLayOut(LayoutKind.Explicit)]
public struct Number{
private enum NumType{Double,int64}
[FieldOffset(0)]double dblVal;
[FieldOffset(0)]long longVal; //与dblVal相同的位置
[FieldOffset(8)]NumType type;

public void Set(double val){
type=NumType.Double;
dblVal=val;
}

public void Set(long val){
type=NumType.Int64;
longVal=val;
}

pblic double GetDouble(){
if(type==NumType.Double) return dblVal;
else return (double)longVal;
}

public long GetInt64(){
if(type==NumType.Int64) return longVal;
else return (long)dblVal;
}
}

注意,Number的实例具有充分的空间,用于System.Double或者System.Int64,但不能是两者都有。

与类型和布局有关的还有最后一个需要强调的主题。如上所述,explicit元数据特性完全牺牲了CLR所提供的虚拟化布局。然而,甚至标记为explicit的类型也含有丰富的元数据,并且CLR知道类型的字段、方法和其他成员。这是CLR公共类型系统中类型的特点。然而,为了使编译器支持非CLR公共类型系统部分的构件,例如,多重继承或者模板(template),CLR提供了一种机制,用于指明类型是模糊的(opaque)。模糊类型(Opaque type)就是不可预知的。用于模糊类型的元数据只包括实例在内存中的大小,而没有元数据标明类型的字段或方法。此外,模糊类型总是值类型,易于从所需支持CLR运行时类型的基础架构或者垃圾回收器实现。

C#和VB.NET不支持模糊类型。相反,c++默认就支持模糊类型。当你使用/CLR开关,所有的类和结构就被作为模糊类型发射,CLR这样做以维护严格的c++语义  允许任何c++程序被重新编译为CLR模块并运行。为了标明类型不是模糊的(有时也标为托管类型),C++类型定义必须使用_gc或者__value修饰符,这取决于是否期望是引用类型还是值类型。当你使用其中的某个修饰符时,结果类型不再是单纯的C++语义。准确地说,有关构造顺序、终结以及其他CLR技术都将应用到这个类型上。

为了便于理解C++中模糊类型与非模糊类型之间的差别,考察下面的C++类型定义:
template<typename ‘I’ >
class Hob{
T t;
public: void f(){t++ ;}
public : Bob(){t=0}
public :~Bob(){}
};

class Sleve:public Bob<int >,public Bob<double >{
float x;
public: void g(){
Bob<int >::f();
Bob<double >::f();
}
public:Steve(){}
public:-Steve(){}
};

因为Bob和steve都没有标记为_ge或者_value修饰符,c++编译器作为模糊类型编译这些类型,并且它们具有完全的c++语义[确定的析构(destruction)和从基类到派生类的构造】。然而,由于这些类型是模糊类型Bob或者Steve没有字段显示在元数据中。事实上,Bob和Steve的方法将显示为以模块为作用域的方法,使用散乱的名。此外,由于CLR不支持泛型(generic),所以c++编译器必须显式地实例化类型Bob<int >和Bob<double >,以在元数据中作为不同的类型。

当处理对象引用时,CLR不知道模糊类型的字段就会产生问题。因为CLR必须知道每个对象引用的位置,才能实现垃圾回收,而模糊类型没有作为字段的对象引用。为了避免这种限制,CLR提供了一种类型System.Runtime.InteropSevices.GCHandle,支持将对象引用处理为
整型。为了使用GCHandle,你必须首先显式地调用静态GCHandle.Alloc方法分配它。这个方法创建一个根的列象引用,并且将句柄存储到新GcHandle实例的引用中,如图10.1所示。任何程序都能够实现GCHandle和IntPtr(安全地存储在模糊类型中)之间的转换。分配的GCHandle(及其根的对象引用)将一直存在,直到程序显式地调用GCHandle.Free。

下面的模糊c++类,演示了如何使用GCHandle,将对象引用作为字段进行缓存。
#using<mscorlib.dll >
using namespace System;
using namespace System::Runtime::InteropServices;

class OpagueObjrefUser{
void *pvCache;
public:
void SetIt(Object *obj){
GCHandle handle=GCHandle::Alloc(obj);
pvCache=((IntPtr)handle).ToPointer();
}
int GetHash(){
GCHandle handle=GCHandle::op_Explicit(pvCache);
Object *obj=handle.Target;
return obj- >GetHashCode();
}

void FreeIt(){
GCHandle handle=GCHandle::op_Explicit(pvCache);
handle.Free();
}
}

具有COM背景的读者可能注意到GCHandle类型和COM全局接口表之间的相似性。为此,Visual C++的include目录下包含一个文件goroot.h,它定义了一个智能(smart)指针,简化了模糊类型中GCHandle的使用。给定该头文件,你就可以重新改写前面的示例:
#using<mscorlib.dll >
using namespace System;
using namespace System::Runtime::InteropServices;

class OpagueObjrefUser{
gcroot<System::Object* > cache;
public:
void SetIt(Object *obj){
cache=obj; //overloaded operator=
}
int GetHash(){
return cache- >GetHashCode(); //operator - >
}

void FreeIt(){
cache=0; //or let destructor do it…
}
};

熟悉智能指针的C++程序员,将会有似曾相识的感觉。

工作中的GCHandle
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 67+95=? 的结果(结果是:162)
结  果: