web-dev-qa-db-fra.com

Appel de la méthode générique avec un argument de type connu uniquement au moment de l'exécution

Modifier:

Bien sûr, mon vrai code ne ressemble pas exactement à ça. J'ai essayé d'écrire du code semi-pseudo pour le rendre plus clair sur ce que je voulais faire.

On dirait que ça a tout gâché.

Donc, ce que je voudrais réellement faire est la suivante:

Method<Interface1>();
Method<Interface2>();
Method<Interface3>();
...

Eh bien ... je pensais que je pourrais peut-être en faire une boucle en utilisant la réflexion. La question est donc: comment puis-je le faire. J'ai très connaissance superficielle de la réflexion. Donc, des exemples de code seraient formidables.

Le scénario ressemble à ceci:

public void Method<T>() where T : class
{}
public void AnotherMethod()
{
    Assembly assembly = Assembly.GetExecutingAssembly();

    var interfaces = from i in Assembly.GetTypes()
    where i.Namespace == "MyNamespace.Interface" // only interfaces stored here
    select i;

    foreach(var i in interfaces)
    {
        Method<i>(); // Get compile error here!
    }




Message original:

Salut!

J'essaie de parcourir toutes les interfaces d'un espace de noms et de les envoyer en tant qu'arguments à une méthode générique comme celle-ci:

public void Method<T>() where T : class
{}
public void AnotherMethod()
{
    Assembly assembly = Assembly.GetExecutingAssembly();

    var interfaces = from i in Assembly.GetTypes()
    where i.Namespace == "MyNamespace.Interface" // only interfaces stored here
    select i;

    foreach(var interface in interfaces)
    {
        Method<interface>(); // Get compile error here!
    }
}

L'erreur que j'obtiens est "Nom de type attendu, mais nom de variable local trouvé". Si j'essaye

...
    foreach(var interface in interfaces)
    {
        Method<interface.MakeGenericType()>(); // Still get compile error here!
    }
}

J'obtiens "Impossible d'appliquer l'opérateur '<' aux opérandes de type 'groupe de méthodes' et 'System.Type'". Avez-vous une idée sur la façon de contourner ce problème?

121
Presidenten

EDIT: OK, le temps d'un programme court mais complet. La réponse de base est comme avant:

  • Trouvez la méthode générique "ouverte" avec Type.GetMethod
  • Faites-le générique en utilisant MakeGenericMethod
  • Invoquer avec Invoke

Voici un exemple de code. Notez que j'ai changé l'expression de requête en notation par points: il n'y a aucun intérêt à utiliser une expression de requête si vous venez de recevoir une clause where.

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

namespace Interfaces
{
    interface IFoo {}
    interface IBar {}
    interface IBaz {}
}

public class Test
{
    public static void CallMe<T>()
    {
        Console.WriteLine("typeof(T): {0}", typeof(T));
    }

    static void Main()
    {
        MethodInfo method = typeof(Test).GetMethod("CallMe");

        var types = typeof(Test).Assembly.GetTypes()
                                .Where(t => t.Namespace == "Interfaces");

        foreach (Type type in types)
        {
            MethodInfo genericMethod = method.MakeGenericMethod(type);
            genericMethod.Invoke(null, null); // No target, no arguments
        }
    }
}

réponse originale

Laissons de côté les problèmes évidents d'appeler une variable "interface" pour commencer.

Vous devez l'appeler par réflexion. Le but des génériques est de mettre plus de vérification de type à la compilation . Vous ne savez pas quel est le type au moment de la compilation - vous devez donc utiliser des génériques.

Obtenez la méthode générique et appelez-la MakeGenericMethod, puis appelez-la.

Votre type d'interface est-il réellement générique? Je demande parce que vous appelez MakeGenericType dessus, mais ne transmettez aucun argument de type ... Essayez-vous d'appeler

Method<MyNamespace.Interface<string>>(); // (Or whatever instead of string)

ou

Method<MyNamespace.Interface>();

Si c'est le dernier cas, vous avez uniquement besoin d'un appel à MakeGenericMethod - et non à MakeGenericType.

143
Jon Skeet