Question

Je me rends compte que, d'une manière générale, il y a des implications sur les performances de l'utilisation de la réflexion. (Je suis moi-même pas un fan de réflexion du tout, en fait, ce qui est une question purement théorique.)

Supposons qu'il existe une classe qui ressemble à ceci:

public class MyClass {
    public string GetName() {
        return "My Name";
    }
}

Ours avec moi ici. Je sais que si j'ai une instance de MyClass appelé x, je peux appeler x.GetName(). De plus, je pourrais définir une variable de Func<string> à x.GetName.

Maintenant, voici ma question. Disons que je ne pas connaître la classe ci-dessus est appelé MyClass; J'ai un objet, x, mais je ne sais pas ce qu'il est. Je pouvais vérifier si cet objet a une méthode GetName en faisant ceci:

MethodInfo getName = x.GetType().GetMethod("GetName");

On suppose que getName n'est pas nul. Ensuite, je ne pourrais pas en outre vérifier si getName.ReturnType == typeof(string) et getName.GetParameters().Length == 0, et à ce moment-là, ça ne irait pas tout à fait certain que la méthode représentée par mon objet getName pourrait définitivement être jeté à un Func<string>, en quelque sorte?

Je me rends compte qu'il ya un MethodInfo.Invoke, et je me rends compte aussi que je pouvais toujours créer un Func<string> comme:

Func<string> getNameFunc = () => getName.Invoke(x, null);

Je suppose que ce que je demande est de savoir s'il y a un moyen d'aller un objet MethodInfo la méthode réelle qu'il représente, encourir le coût de la réflexion de la performance dans la processus , mais après ce point pouvant appeler la méthode directement (via, par exemple, un Func<string> ou quelque chose de similaire) sans une pénalité de performance.

Ce que je veux envisager pourrait ressembler à quelque chose comme ceci:

// obviously this would throw an exception if GetActualInstanceMethod returned
// something that couldn't be cast to a Func<string>
Func<string> getNameFunc = (Func<string>)getName.GetActualInstanceMethod(x);

(Je sais bien que n'existe pas. Je me demande s'il y a quelque chose comme il)

Était-ce utile?

La solution

Ce genre de remplace ma réponse précédente parce que cela, même si elle est un peu plus long chemin - vous donne un appel de méthode rapide et, contrairement à certains des autres réponses, vous permet de passer par différentes instances (au cas où vous allez rencontrer plusieurs instances du même type). Si vous ne voulez pas, vérifiez ma mise à jour en bas (ou de regarder la réponse de Ben M).

Voici une méthode d'essai qui fait ce que vous voulez:

public class TestType
{
  public string GetName() { return "hello world!"; }
}

[TestMethod]
public void TestMethod2()
{
  object o = new TestType();

  var input = Expression.Parameter(typeof(object), "input");
  var method = o.GetType().GetMethod("GetName", 
    System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
  //you should check for null *and* make sure the return type is string here.
  Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string)));

  //now build a dynamic bit of code that does this:
  //(object o) => ((TestType)o).GetName();
  Func<object, string> result = Expression.Lambda<Func<object, string>>(
    Expression.Call(Expression.Convert(input, o.GetType()), method), input).Compile();

  string str = result(o);
  Assert.AreEqual("hello world!", str);
}

Une fois que vous construisez le délégué une fois - vous pouvez le cache dans un dictionnaire:

Dictionary<Type, Func<object, string>> _methods;

Tout ce que vous faites est alors l'ajouter au dictionnaire, en utilisant le type d'objet entrant (à partir GetType ()) comme la clé. À l'avenir, vous devez d'abord vérifier pour voir si vous avez un délégué prêt-cuit au four dans le dictionnaire (et l'invoquons le cas échéant), sinon vous construisez d'abord, ajoutez dans, puis l'invoquons.

Par ailleurs, ceci est une version très très simplifiée du genre de chose que le DLR fait pour ce mécanisme de dispatch dynamique (en C #, qui est lorsque vous utilisez le mot-clé « dynamique »).

Et enfin

Si, comme quelques personnes l'ont mentionné, vous voulez simplement de faire cuire un Func directement lié à l'objet que vous recevez alors vous faites ceci:

[TestMethod]
public void TestMethod3()
{
  object o = new TestType();

  var method = o.GetType().GetMethod("GetName", 
    System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

  Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string)));

  //this time, we bake Expression.Constant(o) in.
  Func<string> result = Expression.Lambda<Func<string>>(
   Expression.Call(Expression.Constant(o), method)).Compile();

  string str = result(); //no parameter this time.
  Assert.AreEqual("hello world!", str);
}

Note, cependant, qu'une fois l'arbre d'expression est jeté loin, vous devez vous assurer que les séjours o dans la portée, sinon vous pourriez obtenir des résultats désagréables. La façon la plus facile serait de se tenir à une référence locale (dans une instance de classe, peut-être) pour la durée de vie de votre délégué. (Suppression de la suite des commentaires de Ben M)

Autres conseils

Oui, cela est possible:

Func<string> func = (Func<string>)
                     Delegate.CreateDelegate(typeof(Func<string>), getName);

Voici ma réponse, en construisant un arbre d'expression. Contrairement aux autres réponses, le résultat (getNameFunc) est une fonction qui est lié à l'instance d'origine. - sans avoir à passer en tant que paramètre

class Program
{
    static void Main(string[] args)
    {
        var p = new Program();
        var getNameFunc = GetStringReturningFunc(p, "GetName");
        var name = getNameFunc();
        Debug.Assert(name == p.GetName());
    }

    public string GetName()
    {
        return "Bob";
    }

    static Func<string> GetStringReturningFunc(object x, string methodName)
    {
        var methodInfo = x.GetType().GetMethod(methodName);

        if (methodInfo == null ||
            methodInfo.ReturnType != typeof(string) ||
            methodInfo.GetParameters().Length != 0)
        {
            throw new ArgumentException();
        }

        var xRef = Expression.Constant(x);
        var callRef = Expression.Call(xRef, methodInfo);
        var lambda = (Expression<Func<string>>)Expression.Lambda(callRef);

        return lambda.Compile();
    }
}

La meilleure façon de le faire est par Delegate.CreateDelegate:

Func<string> getNameFunc = (Func<string>) Delegate.CreateDelegate(
                                           typeof(Func<string>), x, getName);

Notez que cette getNameFunc lie à x, donc pour chaque x vous aurez besoin de créer une nouvelle instance de délégué. Cette option est beaucoup moins compliqué que les exemples basés Expression. Cependant, les exemples basées sur des expressions, il est possible de créer une Func<MyClass, string> getNameFuncForAny fois, que vous pouvez réutiliser pour chaque instance d'un MyClass.

Pour créer un tel getNameFuncForAny, vous auriez besoin d'une méthode comme

public Func<MyClass, string> GetInstanceMethod(MethodInfo method)
{
    ParameterExpression x = Expression.Parameter(typeof(MyClass), "it");
    return Expression.Lambda<Func<MyClass, string>>(
        Expression.Call(x, method), x).Compile();
}

que vous pouvez utiliser comme ceci:

Func<MyClass, string> getNameFuncForAny = GetInstanceMethod(getName);

MyClass x1 = new MyClass();
MyClass x2 = new MyClass();

string result1 = getNameFuncForAny(x1);
string result2 = getNameFuncForAny(x2);

Si vous ne voulez pas être lié à Func<MyClass, string>, vous pouvez définir

public TDelegate GetParameterlessInstanceMethod<TDelegate>(MethodInfo method)
{
    ParameterExpression x = Expression.Parameter(method.ReflectedType, "it");
    return Expression.Lambda<TDelegate>(
        Expression.Call(x, method), x).Compile();
}

Vous pouvez construire une expression arbre représentant un lambda appelant cette méthode, puis Compile() pour que d'autres appels seront tout aussi rapide que les appels standards compilés.

Sinon, je l'ai écrit cette méthode un bon moment il y a basé sur un article MSDN, qui génère une enveloppe en utilisant iL appeler une façon MethodInfo plus rapidement qu'avec MethodInfo.DynamicInvoke car une fois que le code est généré, il n'y a presque pas de frais généraux sur un appel normal.

Un du haut de mon approche de la tête serait d'utiliser dynamique. Vous pourriez alors si quelque chose comme ceci:

if( /* This method can be a Func<string> */)
{
    dynamic methodCall = myObject;
    string response = methodCall.GetName();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top