web-dev-qa-db-fra.com

Réflexion C # - charge l'assembly et appelle une méthode si elle existe

Je veux charger un assembly (son nom est stocké dans une chaîne), utiliser la réflexion pour vérifier s'il a une méthode appelée "CustomType MyMethod (byte [] a, int b)" et l'appeler ou lever une exception sinon. Je suppose que je devrais faire quelque chose comme ça, mais j'apprécierais que quelqu'un puisse offrir le même conseil sur la meilleure façon de le faire:

Assembly asm = Assembly.Load("myAssembly"); /* 1. does it matter if write myAssembly or myAssembly.dll? */

Type t = asm.GetType("myAssembly.ClassName");

// specify parameters
byte[] a = GetParamA();
int b = GetParamB();

object[] params = new object[2];
params[0] = a;
params[1] = b;

/* 2. invoke method MyMethod() which returns object "CustomType" - how do I check if it exists? */
/* 3. what's the meaning of 4th parameter (t in this case); MSDN says this is "the Object on which to invoke the specified member", but isn't this already accounted for by using t.InvokeMember()? */
CustomType result = t.InvokeMember("MyMethod", BindingFlags.InvokeMethod, null, t, params);

Est-ce suffisant, ou existe-t-il des moyens meilleurs/plus rapides/plus courts? Qu'en est-il des constructeurs, étant donné que ces méthodes ne sont pas statiques - peuvent-elles simplement être ignorées?

Lorsque vous appelez des méthodes void (), est-il correct d'écrire simplement t.InvokeMember (...) ou devez-vous toujours faire Object obj = t.InvokeMember (...)?

Merci d'avance.


EDIT J'ai fourni un exemple de travail comme réponse séparée ci-dessous.

28
w128

utiliser la réflexion pour vérifier si elle a une méthode appelée "CustomType MyMethod (byte [] a, int b)" et l'appeler ou lever une exception sinon

Votre code actuel ne remplit pas cette condition. Mais vous pouvez assez facilement avec quelque chose comme ça:

var methodInfo = t.GetMethod("MyMethod", new Type[] { typeof(byte[]), typeof(int) });
if (methodInfo == null) // the method doesn't exist
{
    // throw some exception
}

var o = Activator.CreateInstance(t);

var result = methodInfo.Invoke(o, params);

Est-ce suffisant, ou existe-t-il des moyens meilleurs/plus rapides/plus courts?

En ce qui me concerne, c'est la meilleure façon et il n'y a vraiment rien de plus rapide.

Qu'en est-il des constructeurs, étant donné que ces méthodes ne sont pas statiques - peuvent-elles simplement être ignorées?

Vous devrez toujours créer une instance de t comme indiqué dans mon exemple. Cela utilisera le constructeur par défaut sans arguments. Si vous avez besoin de passer des arguments, vous pouvez le voir juste documentation MSDN et le modifier comme tel.

12
Mike Perrenoud

Comme cela semble être une question populaire, voici l'exemple de code source complet sur la façon de le faire.

Supposons que nous ayons un exemple d'assembly, MyAssembly.dll, avec une classe MyClass. Nous souhaitons le charger dynamiquement et invoquer ses méthodes. MyAssembly code:

namespace MyAssembly
{
    public class MyClass
    {
        public int X { get; set; }
        public int Y { get; set; }

        public MyClass(int initialX, int initialY)
        {
            X = initialX;
            Y = initialY;
        }

        public int MyMethod(int count, string text)
        {
            Console.WriteLine("This is a normal method.");
            Console.WriteLine("Count: {0}", count);
            Console.WriteLine("Text: {0}", text);

            return this.X + this.Y;
        }

        public static void StaticMethod(int count, float radius)
        {
            Console.WriteLine("This is a static method call.");
            Console.WriteLine("Count: {0}", count);
            Console.WriteLine("Radius: {0}", radius);
        }
    }
}

Tout d'abord, nous aimerions créer une instance de la classe en utilisant le constructeur MyClass(int initialX, int initialY), puis appeler la méthode public int MyMethod(int count, string text). Voici comment vous le faites à partir d'un autre projet (par exemple, une application console):

static void Main(string[] args)
{
    //
    // 1. Load Assembly "MyAssembly.dll" from file path. Specify that we will be using class MyAssembly.MyClass
    //
    Assembly asm = Assembly.LoadFrom(@"C:\Path\MyAssembly.dll");
    Type t = asm.GetType("MyAssembly.MyClass");

    //
    // 2. We will be invoking a method: 'public int MyMethod(int count, string text)'
    //
    var methodInfo = t.GetMethod("MyMethod", new Type[] { typeof(int), typeof(string) });
    if (methodInfo == null)
    {
        // never throw generic Exception - replace this with some other exception type
        throw new Exception("No such method exists.");
    }

    //
    // 3. Define parameters for class constructor 'MyClass(int initialX, int initialY)'
    //
    object[] constructorParameters = new object[2];
    constructorParameters[0] = 999; // First parameter.
    constructorParameters[1] = 2;   // Second parameter.

    //
    // 4. Create instance of MyClass.
    //
    var o = Activator.CreateInstance(t, constructorParameters);

    //
    // 5. Specify parameters for the method we will be invoking: 'int MyMethod(int count, string text)'
    //
    object[] parameters = new object[2];
    parameters[0] = 124;            // 'count' parameter
    parameters[1] = "Some text.";   // 'text' parameter

    //
    // 6. Invoke method 'int MyMethod(int count, string text)'
    //
    var r = methodInfo.Invoke(o, parameters);
    Console.WriteLine(r);
}

L'appel de la méthode statique public static void StaticMethod(int count, float radius) ressemble à ceci:

var methodInfoStatic = t.GetMethod("StaticMethod");
if (methodInfoStatic == null)
{
    // never throw generic Exception - replace this with some other exception type
    throw new Exception("No such static method exists.");
}

// Specify parameters for static method: 'public static void MyMethod(int count, float radius)'
object[] staticParameters = new object[2];
staticParameters[0] = 10;
staticParameters[1] = 3.14159f;

// Invoke static method
methodInfoStatic.Invoke(o, staticParameters);
23
w128
Assembly assembly = Assembly.LoadFile("myAssembly");
        Type type = Assembly.GetType("myAssembly.ClassName");
        if (type != null)
        {
            MethodInfo methodInfo = type.GetMethod("MyMethod");
            if (methodInfo != null)
            {
                object result = null;
                ParameterInfo[] parameters = methodInfo.GetParameters();
                object classInstance = Activator.CreateInstance(type, null);
                if (parameters.Length == 0)
                {
                    //This works fine
                    result = methodInfo.Invoke(classInstance, null);
                }
                else
                {
                    object[] parametersArray = new object[] { "Hello" };

                    //The invoke does NOT work it throws "Object does not match target type"             
                    result = methodInfo.Invoke(classInstance, parametersArray);
                }
            }
        }
6
Talha

Vous pouvez utiliser un type dynamique qui sera résolu lors de l'exécution.

Type type = Type.GetType(className, true);
dynamic instance = Activator.CreateInstance(type);
var response = instance.YourMethod();
2