Type, RuntimeType and RuntimeTypeHandle

 Vijay 在其 BLog 上的一篇文章 A .NET Riddle 中提出了一个有趣的问题:
以下为引用:

    What is the only Type in .NET which has only a non-public constructor and the method body of which throws up a NotImplemented Exception. If so how is the type instance created then ?


    答案就是 System.RuntimeType 类型。Vijay 在接下来的一篇文章 .NET Riddle Redux - The Answer 中解释了为什么如此,并且介绍了 Rotor 中 RuntimeType 类型获得的方法。
    Rotor 中 System.RuntimeType 类型的构造函数(bclsystemRuntimeType.cs:59)代码如下:
以下为引用:

internal sealed class RuntimeType : Type, ISerializable, ICloneable
{
  // Prevent from begin created
  internal RuntimeType()
  {
    throw new NotSupportedException(Environment.GetResourceString(ResId.NotSupported_Constructor));
  }
}


    正如前面那个谜语中所说,有一个内部构造函数但函数体直接抛出异常,因此不能直接构造。唯一能够获得此类型实例的方式是通过Object.GetType()等函数,由 CLR 创建。

    下面我们简单介绍一下,Type、RuntimeType 和 RuntimeTypeHandle 三个类型之间的关系。

    简单的说:Type 是一个表示类型的抽象类;RuntimeType 是 Type 针对载入类型信息的具体实现;RuntimeTypeHandle 则是类型唯一的抽象句柄。
以下为引用:

namespace System
{
  public struct RuntimeTypeHandle : ISerializable
  {
    private IntPtr m_ptr;

    //...
  }

  public abstract class Type : MemberInfo, IReflect
  {
    //...

    public abstract RuntimeTypeHandle TypeHandle
    {
      get;
    }

    public static RuntimeTypeHandle GetTypeHandle(Object o)
    {
      return RuntimeType.InternalGetTypeHandleFromObject(o);
    }

    public static Type GetTypeFromHandle(RuntimeTypeHandle handle)
    {
      return RuntimeType.GetTypeFromHandleImpl(handle);
    }

    //...
  }

  internal sealed class RuntimeType : Type, ISerializable, ICloneable
  {
    //...

    public override RuntimeTypeHandle TypeHandle
    { get { return InternalGetClassHandle(); } }

    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    private extern RuntimeTypeHandle InternalGetClassHandle();

    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    internal extern static RuntimeTypeHandle InternalGetTypeHandleFromObject(Object o);

    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    public static extern Type GetTypeFromHandleImpl(RuntimeTypeHandle handle);

    //...
  }
}


    Type 使用最为广泛,提供了完整的类型信息获取的接口,可以通过 C# 的 typeof 关键字或者 Object.GetType() 函数从类型和对象直接获取。但 Type 只是一个抽象类,需要通过子类实现其部分功能。除了常见的 RuntimeType 子类外,还有 System.Reflection 名字空间下的 TypeDelegator 和 System.Reflection.Emit 名字空间下的 EnumBuilder、TypeBuilder 和 SymbolType 从其直接继承。
    RuntimeType 是 System 名字空间的内部类,用于实现对普通类型的运行时信息提供代码。Type中很多抽象函数如 Type.GetMethodImpl 都是在 RuntimeType 中实现的。与 TypeBuilder 等不同,RuntimeType 是类型的运行时表现。
    RuntimeTypeHandle 是运行时类型的抽象句柄,结构内部实际上是一个 Unamanged 指针。可以使用 Type.GetTypeFromHandle() 函数、Type.TypeHandle 属性和 Type.GetTypeHandle() 函数,完成在类型句柄、类型和对象之间的双向转换。例如:
以下为引用:

MyClass1 cls = new MyClass1();

Debug.Assert(cls.GetType().TypeHandle.Equals(Type.GetTypeHandle(cls)));
Debug.Assert(Type.GetTypeFromHandle(cls.GetType().TypeHandle) == typeof(MyClass1));


    从 Rotor 的实现代码上来看,Type 类型的实际构造基本上都是由 RuntimeType 类型通过 COMClass 类(vmComClass.cpp:260)完成的;而 RuntimeTypeHandle 则是类型方法表 MethodTable 类(vmClass.h:293)的指针的简单包装。
    获取 RuntimeTypeHandle 的函数 Type.GetTypeHandle() 实际上调用的是 RuntimeType.InternalGetTypeHandleFromObject 函数(bclsystemRuntimeType.cs:592);而此函数的内部实现被绑定(vmECall.cpp:515)到 COMClass::GetTHFromObject 函数(vmComClass.cpp:255)上;最终只是简单地返回对象的 MethodTable 指针。伪代码如下
以下为引用:

void * COMClass::GetTHFromObject(Object* obj)
{
  if(obj==NULL)
    FCThrowArgumentNull(L"obj");

  return obj->GetMethodTable();
}


    因此在 Unmanaged C++ 中实际上可以直接通过 GetTypeHandle 返回的 RuntimeTypeHandle.Data 转换为地址指针,访问 MethodTable 类的数据。例如 MethodTable 第二个 DWORD 中保存着此对象的基本大小,可以在 VS.NET 调试环境的内存窗口中查看到。
    
    直接从类型获取 RuntimeTypeHandle 的 Type.InternalGetClassHandle 函数(bclsystemRuntimeType.cs:589),内部实现被绑定(vmECall.cpp:528)到 COMClass::GetClassHandle 函数(vmComClass.cs:1434)上,直接从类型的内部包装类 ReflectClass 类(vmReflectorWrap.h:212)中获取初始化时提供的类型句柄(MethodTable指针)。伪代码如下
以下为引用:

void * COMClass::GetClassHandle(ReflectClassBaseObject* refThisUNSAFE)
{  
  REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF) refThisUNSAFE;

  ReflectClass* pRC = (ReflectClass*) refThis->GetData();  

  TypeHandle ret = pRC->GetTypeHandle();
  
  return ret.AsPtr();
}


    从 RuntimeTypeHandle 反向查询 Type 的 Type.GetTypeFromHandle 函数,实现上调用 RuntimeType.GetTypeFromHandleImpl 函数(bclsystemRuntimeType.cs:596),并被绑定(vmECall.cpp:529)到 COMClass::GetClassFromHandle 函数(vmComClass.cpp:1451)上。
    GetClassFromHandle 函数首先试图从 RuntimeTypeHandle 实例指向的方法表中,调用 MethodTable::GetExistingExposedClassObject 函数(vmClass.h:488)获取已经建立的类型信息包装类;如果第一次使用此类型,则调用 TypeHandle::CreateClassObj 函数(vmClass.cpp:12486)创建类型信息包装类实例。伪代码如下:
以下为引用:

Object * COMClass::GetClassFromHandle(LPVOID handle)
{
  if(handle == 0)
    FCThrowArgumentEx(kArgumentException, NULL, L"InvalidOperation_HandleIsNotInitialized");

  TypeHandle typeHnd(handle);

  if(!typeHnd.IsTypeDesc())
  {
    MethodTable *pMT = typeHnd.GetMethodTable();

    if (pMT)
    {
      OBJECTREF o = pMT->GetExistingExposedClassObject(); // 获取已经建立的类型信息包装类
      if (o != NULL)
      {
        return (OBJECTREFToObject(o));
      }
    }
  }

  return typeHnd.CreateClassObj(); // 创建类型信息包装类实例
}

posted @ 2004-05-17 20:42  dudu  阅读(3650)  评论(2编辑  收藏  举报