20 votes

Comment Reflection est-il implémenté en C# ?

J'étais curieux de savoir où Type.GetType() est implémenté, alors j'ai jeté un coup d'œil à l'assemblage et j'ai remarqué que Type.GetType() appelle base.GetType() et puisque Type hérite de MemberInfo J'ai jeté un coup d'œil et il est défini comme suit _MemberInfo.GetType() qui renvoie this.GetType() . Comme je ne peux pas trouver le code réel qui montre comment le C# peut obtenir des informations sur les types, j'aimerais savoir :

Comment le CLR obtient-il le Type et le MemberInfo des objets au Runtime ?

16voto

GlennFerrieLive Points 4524

La source RÉELLE de .NET Framework 2.0 est disponible sur Internet (à des fins éducatives) ici : http://www.microsoft.com/en-us/download/details.aspx?id=4917

Il s'agit de l'implémentation du langage C#. Vous pouvez utiliser 7zip pour le décompresser. Vous trouverez l'espace de nom de réflexion ici (relativement) :

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

Je cherche l'implémentation spécifique que vous demandez, mais c'est un bon début.

UPDATE : Désolé, mais je pense que c'est une impasse. Type.GetType() appelle l'implémentation de base qui provient de System.Object. Si vous inspectez ce fichier de code ( .\sscli20\clr\src\bcl\system\object.cs ), vous trouverez que la méthode est extern (voir le code ci-dessous). Une inspection plus poussée pourrait permettre de découvrir l'implémentation, mais elle n'est pas dans la BCL. Je soupçonne qu'elle se trouve quelque part dans le code C++.

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

MISE À JOUR (ENCORE) : J'ai creusé plus profondément et j'ai trouvé la réponse dans l'implémentation de la machine virtuelle CLR elle-même. (C'est en C++).

La première pièce du puzzle est ici :

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

Nous voyons ici le code qui fait correspondre l'appel externe à une fonction C++.

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

Maintenant, nous devons aller trouver ObjectNative::GetClass ... qui est ici :

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

et voici l'implémentation de 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

Une dernière chose, la mise en œuvre de GetTypeHandle ainsi que d'autres fonctions de soutien se trouvent ici :

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

7voto

Marc Gravell Points 482669

Les parties les plus importantes de la réflexion sont mises en œuvre dans le cadre du CLI lui-même. En tant que tel, vous pouvez regarder soit le Source de référence MS CLI (alias "Rotor") ou le source mono . Mais : il s'agira principalement de C/C++. Les détails de l'implémentation de l'API publique ( MethodInfo , Type etc.) peut être C#.

5voto

evhen14 Points 1155

Il se peut que cela ne réponde pas directement à votre question. Cependant, voici un petit aperçu de la façon dont le code géré sait tout sur les types.

  1. Chaque fois que vous compilez du code, le compilateur analyse/parse les fichiers sources et collecte les informations qu'il rencontre. Par exemple, regardez la classe ci-dessous.

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

    Le compilateur peut voir qu'il s'agit d'une classe interne avec deux membres. Le membre 1 est une propriété de type int avec un setter privé. Le membre 2 est une méthode protégée avec le nom Met2 et le type boolean qui prend l'entrée float (le nom d'entrée est 'input'). Elle possède donc toutes ces informations.

  2. Il stocke ces informations dans l'assemblage. Il y a plusieurs tableaux. Par exemple les classes (types) partent toutes dans une table, les méthodes vivent dans une autre table. Pensez en turms des tables SQL, bien qu'elles ne le soient définitivement pas.

  3. Lorsqu'un utilisateur (développeur) souhaite obtenir des informations sur un type, il appelle la méthode GetType. Cette méthode s'appuie sur un champ caché de l'objet - le pointeur d'objet de type. Cet objet est en fait un pointeur vers une table de classes. Chaque table de classe contient un pointeur vers la première méthode de la table des méthodes. Chaque enregistrement de méthode aura un pointeur vers le premier paramètre dans la table des paramètres.

PS : ce mécanisme est essentiel pour rendre les assemblages .NET plus sûrs. Vous ne pouvez pas remplacer les pointeurs par des méthodes. Cela briserait la signature de l'assemblage.

La compilation JIT s'appuie aussi fortement sur ces tableaux.

2voto

Dai Points 24530

Comme le souligne @GlennFerrieLive, l'appel à GetType est une InternalCall ce qui signifie que l'implémentation se trouve dans le CLR lui-même et non dans la BCL.

D'après ce que j'ai compris, la méthode interne du CLR prend les informations sur le type d'exécution à partir de l'écran d'accueil de l'utilisateur. this qui correspond essentiellement au nom du type. Il recherche ensuite les informations complètes sur le type à partir des métadonnées présentes dans tous les assemblages chargés (vraisemblablement, dans le domaine d'application actuel), ce qui rend la réflexion plutôt coûteuse. La zone de métadonnées est essentiellement une base de données de tous les types et membres présents dans l'assemblage et il construit une instance de Type o Method|Property|FieldInfo à partir de ces données.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X