web-dev-qa-db-fra.com

Réflexion C # et recherche de toutes les références

Avec un fichier DLL, j'aimerais pouvoir trouver tous les appels à une méthode dans ce fichier DLL. Comment puis-je faire ceci?

En gros, comment puis-je faire par programme ce que Visual Studio fait déjà?

Je ne veux pas utiliser un outil comme .NET Reflector pour le faire, mais la réflexion est bonne et probablement nécessaire.

35
user420667

Pour savoir où une méthode MyClass.Foo() est utilisée, vous devez analyser toutes les classes de tous les assemblys comportant une référence à Assembly contenant MyClass. J'ai écrit une simple preuve de concept montrant à quoi ce code peut ressembler. Dans mon exemple, j'ai utilisé cette bibliothèque (c'est juste un seul fichier .cs ) écrit par Jb Evain:

J'ai écrit un petit cours de test à analyser:

public class TestClass
{
    public void Test()
    {
        Console.WriteLine("Test");
        Console.Write(10);
        DateTime date = DateTime.Now;
        Console.WriteLine(date);
    }
}

Et j’ai écrit ce code pour imprimer toutes les méthodes utilisées dans TestClass.Test():

MethodBase methodBase = typeof(TestClass).GetMethod("Test");
var instructions = MethodBodyReader.GetInstructions(methodBase);

foreach (Instruction instruction in instructions)
{
    MethodInfo methodInfo = instruction.Operand as MethodInfo;

    if(methodInfo != null)
    {
        Type type = methodInfo.DeclaringType;
        ParameterInfo[] parameters = methodInfo.GetParameters();

        Console.WriteLine("{0}.{1}({2});",
            type.FullName,
            methodInfo.Name,
            String.Join(", ", parameters.Select(p => p.ParameterType.FullName + " " + p.Name).ToArray())
        );
    }
}

Cela m'a donné la sortie suivante:

System.Console.WriteLine(System.String value);
System.Console.Write(System.Int32 value);
System.DateTime.get_Now();
System.Console.WriteLine(System.Object value);

Cet exemple est évidemment loin d'être complet, car il ne gère pas les paramètres ref et out, ni les arguments génériques. Je suis sûr que j'ai oublié d'autres détails. Cela montre simplement que cela peut être fait.

46
Elian Ebbing

La réflexion seule ne suffit pas pour trouver toutes les références à une méthode dans un assemblage donné. Reflection vous donne un tableau d'octets pour le corps d'une méthode particulière ( MethodInfo.GetMethodBody.GetILAsByteArray ) et vous devez l'analyser vous-même pour rechercher des références à d'autres méthodes. Il existe plusieurs bibliothèques " CIL reader" accessibles au public (je ne les ai pas utilisées - j'espère que quelqu'un en publiera plus).

Ajouter FxCop option - Selon votre scénario, vous pourrez peut-être réutiliser la logique d’analyse CIL fournie par FxCop (analyse de code Visual Studio) et ajouter vos règles personnalisées si son exécution dans le cadre de l’analyse de code vous convient.

3
Alexei Levenkov

Vous pouvez consulter l'article de MSDN Magazine Détermination des références d'assembly et de méthodes .NET.

2
Darin Dimitrov

J'envisagerais de refléter les assemblys de Visual Studio et de voir si vous pouvez le trouver dans la base de code de reverse engineering. Je crois que VS navigue en fait dans le code plutôt que de refléter. Comme Michael l'a fait remarquer, la réflexion est excellente pour déterminer les bits d'une assemblée, mais pas les consommateurs de ces bits. Je n'ai cependant pas réexaminé mes réflexions pour confirmer mes soupçons.

1
Gregory A Beamer

C’est un exemple en supposant que vous souhaitiez rechercher tous les appels de l’Assemblée actuelle. J'ai codé ceci en essayant d'obtenir une valeur de paramètre afin de définir des contraintes pour une méthode avec des valeurs par défaut. Mais je n'ai pas réussi à obtenir les valeurs des paramètres, je n'ai que des types et des valeurs par défaut.

var currentAssembly = Assembly.GetExecutingAssembly();

foreach (var method in currentAssembly.GetTypes().Where(type => type.IsClass)
                                      .SelectMany(cl => cl.GetMethods())
                                      .OfType<MethodBase>()
                                      .SelectMany(mb => mb.GetInstructions())
                                      .Select(inst => inst.Operand).OfType<MethodInfo>()
                                      .Where(mi => mi.Name == "<YourMethodName>"))
{
    //here are your calls.
}

J'espère que ça aide.