Puis-je charger un assembly .NET au moment de l'exécution et instancier un type en ne connaissant que le nom?

178

Est-il possible d'instancier un objet au moment de l'exécution si je n'ai que le nom de la DLL et le nom de la classe, sans ajouter de référence à l'assembly dans le projet? La classe implémente une interface, donc une fois que j'instancie la classe, je la caste vers l'interface.

Nom de l'assemblage:

library.dll

Nom du type:

Société.Projet.Classe


EDIT: Je n'ai pas le chemin absolu de la DLL, donc Assembly.LoadFilene fonctionnera pas. La DLL peut être à la racine de l'application, system32 ou même chargée dans le GAC.

MegaByte
la source

Réponses:

221

Oui. Vous devez utiliser Assembly.LoadFrompour charger l'assembly en mémoire, puis vous pouvez l'utiliser Activator.CreateInstancepour créer une instance de votre type préféré. Vous devrez d'abord rechercher le type en utilisant la réflexion. Voici un exemple simple:

Assembly assembly = Assembly.LoadFrom("MyNice.dll");

Type type = assembly.GetType("MyType");

object instanceOfMyType = Activator.CreateInstance(type);

Mettre à jour

Lorsque vous avez le nom du fichier d'assembly et le nom du type, vous pouvez utiliser Activator.CreateInstance(assemblyName, typeName)pour demander à la résolution de type .NET de résoudre cela en un type. Vous pouvez encapsuler cela avec un try / catch afin qu'en cas d'échec, vous puissiez effectuer une recherche dans les répertoires où vous pouvez spécifiquement stocker des assemblys supplémentaires qui, autrement, pourraient ne pas être recherchés. Cela utiliserait la méthode précédente à ce stade.

Jeff Yates
la source
2
Je n'ai pas le chemin absolu de la dll, donc assemlby.LoadFile ect. ne fonctionnera pas, d'autres idées?
MegaByte
@rp Toujours heureux d'aider (et seulement un an de retard pour le dire!)
Jeff Yates
2
@MegaByte: LoadFrom est différent de LoadFile. Cela résoudra vos dépendances et devrait résoudre le nom de la DLL à partir des chemins connus (GAC, répertoire exe, etc.) Voir MSDN pour plus d'informations.
Jeff Yates
1
Encore une chose ... (moi encore) Euh, vous ne pouvez pas simplement avoir "MyType" comme nom de type, il doit être suivi de NAMESPACE. Donc ce serait plus précis:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi
1
@Cipi: Techniquement, un type est son nom complet d'espacement de noms (le concept d'espace de noms est une commodité du langage). Vous pouvez avoir un type sans espace de noms dans le CLR - je viens de fournir un exemple trop simplifié.
Jeff Yates
36

Considérez les limites des différentes Load*méthodes. À partir de la documentation MSDN ...

LoadFile ne charge pas les fichiers dans le contexte LoadFrom et ne résout pas les dépendances à l'aide du chemin de chargement, comme le fait la méthode LoadFrom.

Plus d'informations sur les contextes de chargement peuvent être trouvées dans la LoadFromdocumentation.

Anthony Mastrean
la source
19

Activator.CreateInstance devrait fonctionner.

IFace object = (IFace)Activator.CreateInstance( "AssemblyName",
                                                "TypeName" )
                               .Unwrap();

Remarque: le nom du type doit être le type pleinement qualifié.

Exemple:

var aray = (IList)Activator.CreateInstance("mscorlib","System.Collections.ArrayList").Unwrap();
aray.Add(10);

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
Tvanfosson
la source
1
Juste une note à ce sujet: TypeNamedoit être pleinement qualifié. J'ai dû appeler ça comme ça: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") Et ça renvoie un ObjectHandle. Pour accéder à votre interface, vous devez le faireObjectHandle.UnWrap()
Anthony Sottile
7

J'ai trouvé cette question et certaines réponses très utiles, mais j'ai eu des problèmes de chemin, donc cette réponse couvrirait le chargement de la bibliothèque en trouvant le chemin du répertoire bin.

Première solution:

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFrom(assemblyPath);
Type T = assembly.GetType("Company.Project.Classname");
Company.Project.Classname instance = (Company.Project.Classname) Activator.CreateInstance(T);

Deuxième solution

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFile(assemblyPath);
(Company.Project.Classname) instance = (Company.Project.Classname) assembly.CreateInstance("Company.Project.Classname");

Vous pouvez utiliser le même principe pour les interfaces (vous créeriez une classe mais la transtyperiez en interface), comme:

(Company.Project.Interfacename) instance = (Company.Project.Interfacename) assembly.CreateInstance("Company.Project.Classname");

Cet exemple est pour une application Web mais similaire pourrait être utilisé pour l'application de bureau, seul le chemin est résolu de manière différente, par exemple

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
la source
5

C'est facile.

Exemple de MSDN:

public static void Main()
{
    // Use the file name to load the assembly into the current
    // application domain.
    Assembly a = Assembly.Load("example");
    // Get the type to use.
    Type myType = a.GetType("Example");
    // Get the method to call.
    MethodInfo myMethod = myType.GetMethod("MethodA");
    // Create an instance.
    object obj = Activator.CreateInstance(myType);
    // Execute the method.
    myMethod.Invoke(obj, null);
}

Voici un lien de référence

https://msdn.microsoft.com/en-us/library/25y1ya39.aspx

user3722131
la source
C'est une façon horrible de prendre en charge le chargement dynamique du code. MS a toujours aimé nous forcer à entrer beaucoup trop de détails.
Plus clair le
3

À partir de Framework v4.5, vous pouvez utiliser Activator.CreateInstanceFrom () pour instancier facilement des classes dans des assemblys. L'exemple suivant montre comment l'utiliser et comment appeler une méthode en passant des paramètres et en obtenant la valeur de retour.

    // Assuming moduleFileName contains full or valid relative path to assembly    
    var moduleInstance = Activator.CreateInstanceFrom(moduleFileName, "MyNamespace.MyClass");
    MethodInfo mi = moduleInstance.Unwrap().GetType().GetMethod("MyMethod");
    // Assuming the method returns a boolean and accepts a single string parameter
    bool rc = Convert.ToBoolean(mi.Invoke(moduleInstance.Unwrap(), new object[] { "MyParamValue" } ));
Afiorillo
la source
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
la source
2

Vous pouvez charger un assembly à l'aide des méthodes * Assembly.Load **. En utilisant Activator.CreateInstance, vous pouvez créer de nouvelles instances du type souhaité. Gardez à l'esprit que vous devez utiliser le nom de type complet de la classe que vous souhaitez charger (par exemple Namespace.SubNamespace.ClassName ). En utilisant la méthode InvokeMember de la classe Type , vous pouvez appeler des méthodes sur le type.

De plus, tenez compte du fait qu'une fois chargé, un assembly ne peut pas être déchargé tant que tout l'AppDomain n'est pas déchargé (il s'agit essentiellement d'une fuite de mémoire).

Dario Solera
la source
2

En fonction de la façon dont ce type de fonctionnalité est intrinsèque à votre projet, vous voudrez peut-être envisager quelque chose comme MEF qui se chargera du chargement et de la liaison des composants pour vous.

Kent Boogaart
la source
2
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");

Type type = assembly.GetType("MyType");

dynamic instanceOfMyType = Activator.CreateInstance(type);

Ainsi, de cette façon, vous pouvez utiliser des fonctions non avec get methodinfo, puis l'invoquer. Vous ferez comme ceci instanceOfMyType.MethodName (); Mais vous ne pouvez pas utiliser Intellisense car les types dynamiques sont tapés au moment de l'exécution, pas au moment de la compilation.

David Mkheyan
la source
1

Oui, vous souhaiterez utiliser la méthode statique Load sur la classe Assembly, puis appeler puis appeler la méthode CreateInstance sur l'instance Assembly qui vous est retournée par l'appel à Load.

En outre, vous pouvez appeler l'une des autres méthodes statiques commençant par "Load" sur la classe Assembly, selon vos besoins.

casperOne
la source
0

Vous pouvez faire ces choses de cette façon:

using System.Reflection;

Assembly MyDALL = Assembly.Load("DALL"); //DALL name of your assembly
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // name of your class
 object  obj = Activator.CreateInstance(MyLoadClass);
Pankaj
la source