Type, RuntimeType and RuntimeTypeHandle
Vijay 在其 BLog 上的一篇文章 A .NET Riddle 中提出了一个有趣的问题:
答案就是 System.RuntimeType 类型。Vijay 在接下来的一篇文章 .NET Riddle Redux - The Answer 中解释了为什么如此,并且介绍了 Rotor 中 RuntimeType 类型获得的方法。
Rotor 中 System.RuntimeType 类型的构造函数(bclsystemRuntimeType.cs:59)代码如下:
正如前面那个谜语中所说,有一个内部构造函数但函数体直接抛出异常,因此不能直接构造。唯一能够获得此类型实例的方式是通过Object.GetType()等函数,由 CLR 创建。
下面我们简单介绍一下,Type、RuntimeType 和 RuntimeTypeHandle 三个类型之间的关系。
简单的说:Type 是一个表示类型的抽象类;RuntimeType 是 Type 针对载入类型信息的具体实现;RuntimeTypeHandle 则是类型唯一的抽象句柄。
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() 函数,完成在类型句柄、类型和对象之间的双向转换。例如:
从 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 指针。伪代码如下
因此在 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指针)。伪代码如下
从 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)创建类型信息包装类实例。伪代码如下:
以下为引用:
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(); // 创建类型信息包装类实例
}