Come viene implementato Reflection in C#?

Come viene implementato Reflection in C#?

La fonte ACTUAL per .NET Framework 2.0 è disponibile su Internet (per scopi didattici) qui:http://www.microsoft.com/en-us/download/details.aspx?id=4917

Questa è l'implementazione del linguaggio C#. Puoi usare 7zip per decomprimerlo. Troverai lo spazio dei nomi di riflessione qui (relativamente):

Sto cercando l'implementazione specifica di cui stai chiedendo, ma questo è un buon inizio.

AGGIORNAMENTO: Scusa, ma penso che sia un vicolo cieco. Type.GetType() chiama l'implementazione di base che proviene da System.Object. Se controlli quel file di codice (.\sscli20\clr\src\bcl\system\object.cs ) troverai che il metodo è extern (vedi codice sotto). Un'ulteriore ispezione potrebbe scoprire l'implementazione, ma non è nel BCL. Sospetto che sarà nel codice C++ da qualche parte.

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

AGGIORNAMENTO (ANCORA): Ho scavato più a fondo e ho trovato la risposta nell'implementazione della macchina virtuale CLR stessa. (È in C++).

Il primo pezzo del puzzle è qui:

Qui vediamo il codice che associa la chiamata esterna a una funzione C++.

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

Ora dobbiamo cercare ObjectNative::GetClass ... che è qui:

ed ecco l'implementazione di 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

Un'ultima cosa, l'implementazione di GetTypeHandle insieme ad alcune altre funzioni di supporto possono essere trovate qui:


Le parti più significative della riflessione sono implementate nell'ambito della CLI stessa. Pertanto, puoi guardare la sorgente di riferimento della CLI MS (aka "Rotor") o la sorgente mono. Ma:sarà principalmente C/C++. I dettagli di implementazione dell'API pubblica (MethodInfo , Type ecc.) può essere C#.


Potrebbe non rispondere direttamente alla tua domanda. Tuttavia, ecco un piccolo schema di come il codice gestito sa tutto sui tipi.

  1. Ogni volta che si compila il codice, il compilatore analizza/analizza i file di origine e raccoglie le informazioni che incontra. Ad esempio, dai un'occhiata alla classe qui sotto.

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

    Il compilatore può vedere che questa è una classe interna con due membri. Il membro uno è una proprietà di tipo int con setter privato. Il membro 2 è un metodo protetto con nome Met2 e tipo boolean che accetta l'input float (il nome dell'input è 'input'). Quindi, ha tutte queste informazioni.

  2. Memorizza queste informazioni nell'assieme. Ci sono un paio di tavoli. Ad esempio le classi (tipi) lasciano tutte in una tabella, i metodi vivono in un'altra tabella. Pensa a turno alle tabelle SQL, anche se sicuramente non lo sono.

  3. Quando un utente (sviluppatore) vuole conoscere le informazioni su un tipo, chiama il metodo GetType. Questo metodo si basa sul campo nascosto degli oggetti - tipo puntatore oggetto. Questo oggetto è fondamentalmente un puntatore a una tabella di classe. Ogni tabella di classe avrà un puntatore al primo metodo nella tabella dei metodi. Ciascun record di metodo avrà un puntatore al primo parametro nella tabella dei parametri.

PS:questo meccanismo è la chiave per rendere più sicuri gli assiemi .NET. Non è possibile sostituire i puntatori ai metodi. Rompere la firma dell'assemblea.

Anche la compilazione JIT fa molto affidamento su queste tabelle