20

I got curious as to where Type.GetType() is implemented, so I took a peek at the assembly and noticed Type.GetType() calls base.GetType() and since Type inherits from MemberInfo I took a look and it is defined as _MemberInfo.GetType() which returns this.GetType(). Since I cannot find the actual code that shows how C# can get type information I would like to know:

How does the CLR get Type and MemberInfo from objects at Runtime?

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
Dustin Kingen
  • 20,677
  • 7
  • 52
  • 92

4 Answers4

16

The ACTUAL source for .NET Framework 2.0 is available on the internet (for educational purposes) here: http://www.microsoft.com/en-us/download/details.aspx?id=4917

This is the C# Language implementation. You can use 7zip to unpack it. You will find the reflection namespace here (relatively):

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

I am digging for the specific implementation you are asking about, but this is a good start.

UPDATE: Sorry, but I think its a dead end. Type.GetType() calls to the base implementation which comes from System.Object. If you inspect that codefile (.\sscli20\clr\src\bcl\system\object.cs) you will find the method is extern (see code below). Further inspect could uncover the implementation, but its not in the BCL. I suspect it will be in C++ code somewhere.

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

UPDATE (AGAIN): I dug deeper and found the answer in the implementation of the CLR virtual machine itself. (Its in C++).

The first piece of puzzle is here:

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

Here we see the code that maps the external call to an C++ function.

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

Now, we need to go find ObjectNative::GetClass ... which is here:

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

and here is the implementation of 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

One last thing, the implementation of GetTypeHandle along with some other supporting functions can be found in here:

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

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
  • Aww neat I'll take a look. – Dustin Kingen Mar 02 '13 at 04:33
  • You can actually compile it and replace the existing .NET implementation on your PC, but and apps that target that Fx are unsupported. – Glenn Ferrie Mar 02 '13 at 04:38
  • Yeah I was using DotPeek and reached the same conclusion. – Dustin Kingen Mar 02 '13 at 04:45
  • never heard of that. will check it out. what are you trying to accomplish? is this just "brain candy"? – Glenn Ferrie Mar 02 '13 at 04:47
  • Well it started when I was answering a question on SO and I just kept going deeper and deeper into the mscorlib until I reached a deadend. I would say it is mostly brain candy, but I enjoy that kind of stuff and it is good to know the internals to a certain degree since I keep reading how Reflection is often slow, so if I knew more of how the runtime handles reflection calls I could control or cope with the slowness to a degree. – Dustin Kingen Mar 02 '13 at 04:51
  • 2
    Man that C++ code looks discouraging to understand, but I think I get the gist. It dereferences the pointer to the object and gets its typehandle. It then looks up the type information in the method table (depending on IsUnsharedMT which returns whether the objects methodtable is located on the default or shared domain (large objects such as array, pointer, function pointer, or byref as stored in the shared domain). It then gets an objectref to the type and dereferences it to be returned. However, I don't know what the IsThunking or CONTRACTL is about. – Dustin Kingen Mar 03 '13 at 05:45
  • Take a look at [.NET Framework Internals: How the CLR Creates Runtime objects](http://msdn.microsoft.com/en-us/magazine/cc163791.aspx) for more information on the method tables. – Dustin Kingen Mar 03 '13 at 05:46
  • Using Reflection is always going to be a performance hit. period. if you characterize the scenario you are trying to implement we can discuss alternate methods to accomplish your objective without using Reflection – Glenn Ferrie Mar 04 '13 at 01:24
  • @Romoku so is my answer accepted or are you still missing something? – Glenn Ferrie Mar 04 '13 at 01:26
  • Assuming no one comes up with a better answer in the next couple days then yes. – Dustin Kingen Mar 04 '13 at 01:35
  • I expected [Jon Skeet](http://stackoverflow.com/users/22656/jon-skeet) to answer this question. – Mr. Polywhirl Dec 09 '13 at 02:17
  • It's important to remember that sscli is old and was never used in MS production code. i.e. it's not the source for .NET 2.0, just an example of how another implementation might look--or could be based upon. – Peter Ritchie Apr 14 '14 at 02:24
  • Can we get a laymen answer for those who're not fluent in C++ ? Romoku's comment helps, but is still very much lacking. Pretty please? – Maverick Meerkat Apr 30 '18 at 13:46
7

The most significant parts of reflection are implemented as part of the CLI itself. As such, you could look at either the MS CLI reference source (aka "Rotor"), or the mono source. But: it will mostly be C/C++. The public API implementation details (MethodInfo, Type etc) may be C#.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I'm looking through the Mono source and it might just be me, but it's pretty cumbersome to navigate. – Dustin Kingen Feb 27 '13 at 23:35
  • @Romoku yep; I never said it would be trivial – Marc Gravell Feb 27 '13 at 23:38
  • Do you think it would be worth putting up a bounty for this question to get a more concrete answer? – Dustin Kingen Feb 27 '13 at 23:39
  • @Romoku, what would you like to know in particular? – Andrew Savinykh Mar 02 '13 at 04:22
  • Indeed, it's not clear what more you want here. The inner guts are all in the CLR, and that code is not easy. Heck, I work on the C#/VB IDE and any time I have to go looking through the runtime I ask myself if I can avoid it! – Jason Malinowski Mar 02 '13 at 04:28
  • @zespri It doesn't have to be anything too deep .I'll probably edit my question to be more concise. I would like to know in general how the CLR can get type and member information from an object at runtime. – Dustin Kingen Mar 02 '13 at 04:29
  • @JasonMalinowski That sounds interesting. Does Visual Studio have its own reflection api into the runtime or is it mostly kosher? – Dustin Kingen Mar 02 '13 at 04:48
  • Nope, we use the same APIs you get to. Some parts like the compilers avoid reflection and read the metadata from files directly for performance reasons, but that's no different than what anybody else would do with a copy of the CLI spec and some free time on their hands. :-) – Jason Malinowski Mar 02 '13 at 06:05
5

It might not answer you question directly. However, here is a little outline of how managed code knows everything about types.

  1. Whenever you compile code the compiler analyzes/parses the source files and collects information it encounters. For example take a look at class below.

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

    The compiler can see that this is an internal class with two members. Member one is a property of type int with private setter. Member 2 is a protected method with name Met2 and type boolean that takes float input (input name is 'input'). So, it has all this information.

  2. It stores this information in the assembly. There are a couple of tables. For example classes (types) all leave in one table, methods live in another table. Think in turms of SQL tables, though they are definitely are not.

  3. When a user (developer) wants to know information about a type it calls GetType method. This method relies on objects hidden field - type object pointer. This object is basically a pointer to a class table. Each class table will have a pointer to the first method in methods table. Each method record will have a pointer to the first parameter in the parameters table.

PS: this mechamism is key to making .NET assemblies more secure. You cannot replace pointers to methods. It will break the signature of the assebmly.

JIT compilation relies heavily on this tables as well

svick
  • 236,525
  • 50
  • 385
  • 514
evhen14
  • 1,839
  • 12
  • 16
2

As @GlennFerrieLive points out, the call to GetType is an InternalCall which means the implementation is within the CLR itself and not in any of the BCL.

My understanding is that the internal CLR method takes the runtime type information from the this pointer, which basically amounts to the name of the type. It then look up the complete type information from the metadata present in all loaded assemblies (presumably, in the current appdomain), which is what makes reflection rather expensive. The metadata area is basically a database of all the types and members present in the assembly and it constructs an instance of Type or Method|Property|FieldInfo from this data.

Dai
  • 141,631
  • 28
  • 261
  • 374