如何在C#中实现反射?

时间:2013-02-27 06:28:34

标签: c# .net

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

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

4 个答案:

答案 0 :(得分:15)

.NET Framework 2.0的ACTUAL源可在互联网上找到(用于教育目的):http://www.microsoft.com/en-us/download/details.aspx?id=4917

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

  

\ sscli20 \ CLR \ SRC \ BCL \ SYSTEM \反射

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

更新:抱歉,我认为这是一个死胡同。 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();

UPDATE(AGAIN):我深入挖掘并在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

答案 1 :(得分:7)

反射的最重要部分是作为CLI本身的一部分实现的。因此,您可以查看MS CLI reference source (aka "Rotor")mono source。但是:它主要是C / C ++。公共API实现详细信息(MethodInfoType等)可能是C#。

答案 2 :(得分:5)

它可能不会直接回答你的问题。但是,这里有一个关于托管代码如何了解类型的一些概述。

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

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

    编译器可以看到这是一个有两个成员的内部类。成员1是具有私有setter的int类型的属性。成员2是名为Met2的受保护方法,类型为boolean,采用浮点输入(输入名称为'input')。所以,它拥有所有这些信息。

  2. 它将此信息存储在程序集中。有几张桌子。例如,类(类型)都留在一个表中,方法存在于另一个表中。想想在SQL表格中,尽管它们肯定不是。

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

  4. PS:这种机制是使.NET程序集更安全的关键。您无法替换指向方法的指针。它将打破集合的签名。

    JIT编译在很大程度上依赖于这些表

答案 3 :(得分:2)

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

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