20

我对在哪里Type.GetType()实现感到好奇,所以我看了一下程序集并注意到Type.GetType()调用base.GetType(),因为Type继承自我MemberInfo看了一下,它被定义为_MemberInfo.GetType()which 返回this.GetType()。由于我找不到显示 C# 如何获取类型信息的实际代码,我想知道:

CLR 如何在运行时从对象中获取 Type 和 MemberInfo?

4

4 回答 4

16

.NET Framework 2.0 的实际源代码可在 Internet 上获得(用于教育目的):http: //www.microsoft.com/en-us/download/details.aspx?id=4917

这是 C# 语言实现。您可以使用 7zip 解压缩它。您将在此处(相对)找到反射命名空间:

.\sscli20\clr\src\bcl\system\reflection

我正在挖掘您所询问的具体实现,但这是一个好的开始。

更新:对不起,但我认为这是一个死胡同。 Type.GetType()调用来自 System.Object 的基本实现。如果您检查该代码文件 ( .\sscli20\clr\src\bcl\system\object.cs),您会发现方法是extern(参见下面的代码)。进一步检查可以发现实施,但它不在 BCL 中。我怀疑它会在某个地方出现在 C++ 代码中。

// Returns a Type object which represent this object instance.
// 
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern Type GetType();

更新(再次):我深入挖掘并在 CLR 虚拟机本身的实现中找到了答案。(它在 C++ 中)。

第一个难题在这里:

\sscli20\clr\src\vm\ecall.cpp

在这里,我们看到将外部调用映射到 C++ 函数的代码。

FCFuncStart(gObjectFuncs)
    FCIntrinsic("GetType", ObjectNative::GetClass, CORINFO_INTRINSIC_Object_GetType)
    FCFuncElement("InternalGetHashCode", ObjectNative::GetHashCode)
    FCFuncElement("InternalEquals", ObjectNative::Equals)
    FCFuncElement("MemberwiseClone", ObjectNative::Clone)
FCFuncEnd()

现在,我们需要去寻找ObjectNative::GetClass……这里是:

\sscli20\clr\src\vm\comobject.cpp

这里是实现GetType

    FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis)
{
    CONTRACTL
    {
        THROWS;
        SO_TOLERANT;
        DISABLED(GC_TRIGGERS); // FCallCheck calls ForbidenGC now
        INJECT_FAULT(FCThrow(kOutOfMemoryException););
        SO_TOLERANT;
        MODE_COOPERATIVE;
    }
    CONTRACTL_END;

    OBJECTREF   objRef   = ObjectToOBJECTREF(pThis);
    OBJECTREF   refType  = NULL;
    TypeHandle  typeHandle = TypeHandle();

    if (objRef == NULL) 
        FCThrow(kNullReferenceException);

    typeHandle = objRef->GetTypeHandle();
    if (typeHandle.IsUnsharedMT())
        refType = typeHandle.AsMethodTable()->GetManagedClassObjectIfExists();
    else
        refType = typeHandle.GetManagedClassObjectIfExists();

    if (refType != NULL)
        return OBJECTREFToObject(refType);

    HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_RETURNOBJ, objRef, refType);

    if (!objRef->IsThunking())
        refType = typeHandle.GetManagedClassObject();
    else
        refType = CRemotingServices::GetClass(objRef);
    HELPER_METHOD_FRAME_END();

    return OBJECTREFToObject(refType);
}
FCIMPLEND

最后一件事,GetTypeHandle可以在这里找到与其他一些支持功能的实现:

\sscli20\clr\src\vm\object.cpp

于 2013-03-02T04:33:21.533 回答
7

反射的最重要部分是作为 CLI 本身的一部分实现的。因此,您可以查看MS CLI 参考源(又名“Rotor”)单声道源。但是:它将主要是 C/C++。公共 API 实现细节(MethodInfoType)可能是 C#。

于 2013-02-27T07:47:02.227 回答
5

它可能不会直接回答你的问题。但是,这里简要介绍了托管代码如何了解有关类型的所有内容。

  1. 每当您编译代码时,编译器都会分析/解析源文件并收集它遇到的信息。例如看看下面的课程。

    class A
    {
      public int Prop1 {get; private set;}
      protected bool Met2(float input) {return true;}
    }
    

    编译器可以看到这是一个有两个成员的内部类。成员一是具有私有设置器的 int 类型的属性。成员 2 是一个受保护的方法,名称为 Met2,类型为布尔值,采用浮点输入(输入名称为“输入”)。因此,它拥有所有这些信息。

  2. 它将这些信息存储在程序集中。有几张桌子。例如,类(类型)都留在一个表中,方法存在于另一个表中。考虑一下 SQL 表,尽管它们绝对不是。

  3. 当用户(开发人员)想要了解有关类型的信息时,它会调用 GetType 方法。此方法依赖于对象隐藏字段 - 类型对象指针。这个对象基本上是一个指向类表的指针。每个类表都有一个指向方法表中第一个方法的指针。每个方法记录都有一个指向参数表中第一个参数的指针。

PS:这种机制是使 .NET 程序集更安全的关键。您不能替换指向方法的指针。它会破坏组件的签名。

JIT 编译也严重依赖这些表

于 2013-03-03T03:57:09.700 回答
2

正如@GlennFerrieLive 指出的那样,对 GetType 的调用是一个InternalCall,这意味着实现在 CLR 本身中,而不是在任何 BCL 中。

我的理解是内部CLR方法从this指针中获取运行时类型信息,基本上相当于类型的名称。然后,它从所有已加载程序集(可能在当前应用程序域中)中存在的元数据中查找完整的类型信息,这使得反射变得相当昂贵。元数据区域基本上是程序集中存在的所有类型和成员的数据库,它构建了该数据的实例TypeMethod|Property|FieldInfo从该数据构建实例。

于 2013-03-02T05:32:52.817 回答