web-dev-qa-db-fra.com

Obtenir la méthodeInfo à partir d'une méthode référence C #

Nous pouvons utiliser un mot-clé C # typeof lorsque nous voulons obtenir une instance de type pour un type spécifié. Mais que puis-je utiliser si je veux obtenir MethodInfo d'une méthode par sa référence?

Par exemple, j'ai une simple application de console. Il contient la méthode Program.Main. Je veux obtenir MethodInfo en utilisant quelque chose comme methodinfoof(Program.Main). J'ai ce problème car les noms de méthode peuvent changer, donc je ne peux pas simplement utiliser Type.GetMethodInfo(string MethodName) pour cela.

J'ai environ 10 000 méthodes pour lesquelles je voudrais obtenir MethodInfo, l'ajout d'attributs personnalisés ou autre chose à mes méthodes n'est pas une solution.

22
Bogdan Verbenets

Légère adaptation d'une réponse précédemment affichée, mais ce panneau de blog semble atteindre ce que vous demandez; http://blog.functionalfun.net/2009/10/geting-methodinfo-f-generic-method.html

L'utilisation des échantillons serait la suivante;

var methodInfo = SymbolExtensions.GetMethodInfo(() => Program.Main());

La réponse originale était à cette question; https://stackoverflow.com/a/9132588/5827

16

Vous pouvez utiliser des arbres d'expression pour des méthodes non statiques. Voici un exemple.

using System.Linq.Expressions;
using System.Reflection;

public static class MethodInfoHelper
{
    public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
    {
        var member = expression.Body as MethodCallExpression;

        if (member != null)
            return member.Method;

        throw new ArgumentException("Expression is not a method", "expression");
    }
}

Vous l'utiliseriez comme ceci:

        MethodInfo mi = MethodInfoHelper.GetMethodInfo<Program>(x => x.Test());
        Console.WriteLine(mi.Name);

Test () est une fonction de membre déclarée dans la classe de programme.

Utilisez MemberExpression et MemberInfo _ Au lieu de cela, si vous souhaitez prendre en charge les getters et les Setters de la propriété.

12
Dmitry S.

Classe de test

public class  Foo
{
    public void DoFoo()
    {
        Trace.WriteLine("DoFoo");
    }

    public static void DoStaticFoo()
    {
        Trace.WriteLine("DoStaticFoo");
    }
}

Et vous pouvez faire quelque chose comme ça

MethodInfo GetMethodInfo(Action a)
{
    return a.Method;
}

var foo = new Foo();
MethodInfo mi = GetMethodInfo(foo.DoFoo);
MethodInfo miStatic = GetMethodInfo(Foo.DoStaticFoo);

//do whatever you need with method info

Mise à jour
[.____] par @greg commentaire Si vous avez des paramètres aux méthodes, vous pouvez utiliser Action<T>, Action<T1, T2>, Action<T1, T2, T3>, ou Func<T1>, le désagrément est que vous aurez toujours besoin d'écrire les surcharges pour GetMethodInfo.

6
oleksii

Je sais que c'est un très vieux post, mais je vais juste jetter cela là-bas pour quelqu'un qui pourrait toujours chercher une solution simple à cela. Cela semble juste comme personne ne pensait que la solution la plus simple:

typeof(Program).GetMethods();

Renvoie une matrice avec la méthodeInfo de toutes les méthodes de la classe de programme, quels que soient les attributs ou pour avoir des paramètres ou non.

Vous pouvez itérer l'OVE si vous le souhaitez, par exemple, pour répertorier les noms de toutes vos méthodes de 10.000+.

Vous pouvez également faire typeof(Program).GetMethod(nameof(Program.Main)); de cette façon si le nom de la méthode change refactoring de Visual Studio renommera ici aussi.

Remarque: le mot-clé "Nomof" n'était pas disponible il y a 5 ans lorsque la question a été postée.

5
Matheus Rocha

Permettez-moi d'ajouter des explications au problème à la main ici. Nous recherchons une méthode GetMethodInfo(SomeMethodSymbol) qui renvoie des informations sur la méthode donnée. Ce n'est pas striprwayward car les méthodes peuvent être surchargées dans C #. Donc, fondamentalement, vous devez fournir des indications supplémentaires dans l'appel pour rendre le compilateur (et d'autres analyseurs de code tels que IntelliSense) comprennent quelle méthode dont vous parlez.

Dis par exemple, je cherche des informations sur le Math.Abs méthode. Je dois ensuite spécifier la version surchargée de la méthode que je recherche exactement:

// int
MethodInfo info1 = ((Func<int, int>)Math.Abs).Method;

// or double ?
MethodInfo info2 = ((Func<double, double>)Math.Abs).Method;

Même s'il n'y a qu'une surcharge existante, comme pour le Math.Exp Méthode, je dois toujours fournir des signaux de frappe, car la méthode pourrait être surchargée à l'avenir et que le code ne compilerait plus.

Aides directes

Compte tenu des remarques ci-dessus, nous pouvons fournir l'ensemble des méthodes d'assistance suivant pour atténuer quelque peu la tâche fastidieuse de la mise en oeuvre de chaque méthode pour atteindre ses informations:

public static class GetMethodInfoUtil
{
    // No cast necessary
    public static MethodInfo GetMethodInfo(Action action) => action.Method;
    public static MethodInfo GetMethodInfo<T>(Action<T> action) => action.Method;
    public static MethodInfo GetMethodInfo<T,U>(Action<T,U> action) => action.Method;
    public static MethodInfo GetMethodInfo<TResult>(Func<TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, TResult>(Func<T, TResult> fun) => fun.Method;
    public static MethodInfo GetMethodInfo<T, U, TResult>(Func<T, U, TResult> fun) => fun.Method;

    // Cast necessary
    public static MethodInfo GetMethodInfo(Delegate del) => del.Method;
}

Vous utiliseriez ensuite ces aides comme ceci:

var methodInfos = new[] {

    // Static methods
    GetMethodInfo<int, int>(Math.Abs),
    GetMethodInfo<double, double>(Math.Abs),
    GetMethodInfo<long, long, long>(Math.Max),

    // Static void methods
    GetMethodInfo(Console.Clear),
    GetMethodInfo<string[]>(Main),

    // With explicit cast if too many arguments
    GetMethodInfo((Action<string, object, object>)Console.WriteLine),

    // Instance methods
    GetMethodInfo<string, bool>("".StartsWith),
    GetMethodInfo(new List<int>().Clear),
};

Notez que les informations de type doivent encore être fournies à l'exception de la méthode statique vide ne prenant aucun argument comme Console.Clear. De plus, par exemple des méthodes, une instance réelle doit être utilisée pour obtenir la méthode appropriée, qui utilise plus de ressources.

Aides indirectes

Maintenant, pour certains cas de coin, les aides ci-dessus ne fonctionneront pas. Dites que la méthode utilise des paramètres out par exemple. Dans ces cas particuliers, une méthode d'extraction des informations des expressions de Lambda devient utile et nous revenons à la solution fournie par d'autres affiches (inspiration du code de ici ):

public static class GetIndirectMethodInfoUtil
{
    // Get MethodInfo from Lambda expressions
    public static MethodInfo GetIndirectMethodInfo(Expression<Action> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T>(Expression<Action<T>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);
    public static MethodInfo GetIndirectMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression) 
        => GetIndirectMethodInfo((LambdaExpression)expression);

    // Used by the above
    private static MethodInfo GetIndirectMethodInfo(LambdaExpression expression)
    {
        if (!(expression.Body is MethodCallExpression methodCall))
        {
            throw new ArgumentException(
                $"Invalid Expression ({expression.Body}). Expression should consist of a method call only.");
        }
        return methodCall.Method;
    }
}

Vous utiliseriez ceux-là comme celui-ci:

int dummyInt;
var moreMethodInfos = new[]
{
    // Extracted from lambdas
    GetIndirectMethodInfo(() => "".StartsWith("")),
    GetIndirectMethodInfo((string s) => s.StartsWith(s)),
    GetIndirectMethodInfo(() => int.TryParse("", out dummyInt)),
};

Notez que les informations de type sont toujours fournies indirectement du type d'argument. Notez également qu'un argument factice a été ajouté juste pour permettre d'utiliser un paramètre out.

Programme de démonstration complet: https://dotnetfiddle.net/cks075 .

2
Frederic

Peut-être pas le moyen idéal, mais cela pourrait aider:

var callback = typeof(BlogController).GetMethod(nameof(BlogController.GetBlogs));
2
Mladen B.

J'ai créé un modèle T4 qui crée les fonctions d'assistance nécessaires pour vous aider avec cela. Il crée une liste de fonctions pour obtenir des objets Methodinfo à partir de Func <> ou d'Action <> Méthodes.

Copiez le code suivant dans un fichier nommé GetMethodInfo.tt:

<#@ template language="C#" #>
<#@ output extension=".cs" encoding="utf-8" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Text" #>
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Tools
{
    public static class GetMethodInfo
    {
<# int max = 12;
for(int i = 0; i <= max; i++) 
{
    var builder = new StringBuilder();

    for(int j = 0; j <= i; j++) 
    {
        builder.Append("T");
        builder.Append(j);
        if(j != i) 
        {
            builder.Append(", ");
        }
    }

    var T = builder.ToString();
#>
        public static MethodInfo ForFunc<T, <#= T #>>(Expression<Func<T, <#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

            if (member != null)
                return member.Method;

            throw new ArgumentException("Expression is not a method", "expression");
        }

        public static MethodInfo ForAction<<#= T #>>(Expression<Action<<#= T #>>> expression)
        {
            var member = expression.Body as MethodCallExpression;

            if (member != null)
                return member.Method;

            throw new ArgumentException("Expression is not a method", "expression");
        }

<# } #>
    }
}

Notes :

  • s'il vous plaît assurez-vous que le Build Action du modèle .tt est défini sur Aucun
  • vous pouvez créer plus ou moins de fonctions en définissant la variable maximale dans le paramètre approprié.
1
MovGP0