【问题标题】:How is Reflection implemented in C#?反射是如何在 C# 中实现的?
【发布时间】:2013-02-12 21:00:40
【问题描述】:

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

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

【问题讨论】:

  • @gdoron 不要那样。也许他只是想知道是否有人已经知道。
  • @OscarMederos,知道吗?他到底想知道什么?
  • @gdoron 我的最后一条评论是因为您希望我们改为挖掘吗?。就这样。无论他在问什么(在这种情况下根本不清楚)。有时很多人只是因为他们的工作而想出了正确的答案(Eric Lippert 等)
  • @gdoron 我不介意深入挖掘,但我只是对如何以及在哪里有点困惑。
  • 您的问题之一是 C# 没有实现反射。 .NET 可以。

标签: c# .net


【解决方案1】:

.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

【讨论】:

  • 哇,我去看看。
  • 您实际上可以编译它并替换您 PC 上现有的 .NET 实现,但不支持以 Fx 为目标的应用程序。
  • 是的,我使用 DotPeek 并得出了相同的结论。
  • 从未听说过。会检查出来。你想达到什么目的?这只是“脑糖”吗?
  • C++ 代码看起来很难理解,但我想我明白了要点。它取消引用指向对象的指针并获取其类型句柄。然后它在方法表中查找类型信息(取决于 IsUnsharedMT,它返回对象方法表是位于默认域还是共享域(存储在共享域中的大对象,如数组、指针、函数指针或 byref) . 然后它获取该类型的 objectref 并取消引用它以返回。但是,我不知道 IsThunking 或 CONTRACTL 是关于什么的。
【解决方案2】:

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

【讨论】:

  • 我正在查看 Mono 源代码,可能只有我自己,但导航起来很麻烦。
  • @Romoku 是的;我从来没有说过这将是微不足道的
  • 你认为是否值得为这个问题悬赏以获得更具体的答案?
  • @Romoku,你特别想知道什么?
  • 确实,还不清楚您还想要什么。内脏都在 CLR 中,而那段代码并不容易。哎呀,我在 C#/VB IDE 上工作,每当我必须查看运行时时,我都会问自己是否可以避免它!
【解决方案3】:

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

  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 编译也严重依赖这些表

【讨论】:

  • 表格的类比让我清醒了很多,谢谢。
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-26
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2015-10-21
    • 2015-05-01
    • 2014-12-30
    • 1970-01-01
    相关资源
    最近更新 更多