Création dynamique d'objets

Description

Ce code sert à lire dans un répertoire l'ensemble des DLL et à les ajouter si elles correspondent à une interface ou dérivent d'un type donné.
J'utilise pour cela la reflexion.
Ceci peut permettre de créer des plugins.
La solution est composée de 3 projets, un executable qui contient le code de recherche des DLL, une DLL qui contient de type de référence (référencé dans l'exe) et une DLL qui contient les types dérivés (inconnue de l'exe).
Une propriété statique de la classe de base peut servir de clef, sinon, c'est le nom de la classe qui aura cette fonction.

Source / Exemple :


class Loader<BaseClass> 
{
    private Dictionary<string, Type> creatable;
    private Type BaseType;
    private string KeyElement;
    private string defaultElement;

    #region constructeurs

    /// <summary>
    /// Créé un chargeur de classe
    /// </summary>
    public Loader() : this("") { ;}

    /// <summary>
    /// Créé un chargeur de classes
    /// </summary>
    /// <param name="KeyElement">Element servant de clef</param>
    public Loader(string KeyElement)
    {
        this.BaseType = typeof(BaseClass);
        this.KeyElement = KeyElement;
        creatable = new Dictionary<string,Type>();
    }

    #endregion

    #region gestion des types
    /// <summary>
    /// Ajoute toutes les classes contenues dans toutes les assemblies d'un répertoire
    /// </summary>
    /// <param name="Path">Chemin Contenant les assemblies à charger</param>
    public void AddFolder(string Path) {
        //Charge les fichiers contenus dans le répertoire
        DirectoryInfo directory = new DirectoryInfo(Path);
        foreach (FileInfo file in directory.GetFiles("*.dll"))
        {
            AddAssembly(file.FullName);
        }
    }

    /// <summary>
    /// Ajoute toutes les classes contenues dans une assembly
    /// </summary>
    /// <param name="Path"></param>
    public void AddAssembly(string Path)
    {
        //Charge l'assembly
        Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(Path)); ;
        //Récupère tous les type contenus
        Type[] types = assembly.GetTypes();
        foreach (Type type in types)
        {
            AddType(type);
        }
    }

    /// <summary>
    /// Ajoute un type
    /// </summary>
    /// <param name="type">Type à ajouter</param>
    private void AddType(Type type)
    {
        //Vérifie que le type dérive bien de la classe de base
        if (!IsBasedOn(type,BaseType)) return;
        //Récupère la valeur clef (nom de la classe si le nom est null)
        string key;
        if (string.IsNullOrEmpty(KeyElement)) {
            key = type.FullName;
        } else {
            key = type.InvokeMember(KeyElement, BindingFlags.InvokeMethod | BindingFlags.FlattenHierarchy | BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.Public, null, null, null).ToString();
        }
        //inscrit la classe dans la liste
        creatable[key] = type;
    }

    /// <summary>
    /// Ajoute un type
    /// </summary>
    /// <param name="Path">Chemin de l'assembly contenant la classe</param>
    /// <param name="ClassName">Nom de la classe</param>
    private void AddType(string Path, string ClassName)
    {
        //Charge l'assembly
        Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(Path));
        //charge le type particulier
        Type type = assembly.GetType(ClassName);
        //Ajoute le type
        AddType(type);
    }

    /// <summary>
    /// Retire un type de la liste
    /// </summary>
    /// <param name="Id">Clef du type à enlever</param>
    public void RemoveType(string Id)
    {
        creatable.Remove(Id);
    }   
    #endregion

    #region divers
    /// <summary>
    /// Défini une classe par défaut si la classe recherchée n'existe pas
    /// </summary>
    public string DefaultElement
    {
        get { return defaultElement; }
        set { defaultElement = value; }
    }

    /// <summary>
    /// Vérifie si une classe est basée ou dérivée d'un type particulier
    /// </summary>
    /// <param name="TestType">Type à tester</param>
    /// <param name="BaseType">Référence</param>
    /// <returns></returns>
    private bool IsBasedOn(Type TestType, Type BaseType) {
        if (TestType.IsSubclassOf(BaseType)) return true;
        if (TestType.FullName == BaseType.FullName) return true;
        if (Type.Equals(TestType, typeof(System.Object))) return false;
        return IsBasedOn(TestType.BaseType, BaseType);
    }

    #endregion

    #region creation de types
    /// <summary>
    /// Créé une instance de l'élément demandé, s'il n'existe pas, créé une instance de l'élément par défaut.
    /// </summary>
    /// <param name="Id">Identifiant de l'élément à créer</param>
    /// <param name="parameters">Paramètres du constructeur</param>
    /// <returns>Objet de la classe de base</returns>
    public BaseClass Create(string Id, params object[] parameters)
    {
        object Obj;
        try {
            Obj = Activator.CreateInstance(creatable[Id], parameters);
        } catch {
            try {
                Obj = Activator.CreateInstance(creatable[defaultElement], parameters);
            } catch {
                throw new System.Reflection.ReflectionTypeLoadException(null, null, "Impossible de créer l'objet, la clef n'existe pas");
            }
        }
        return (BaseClass)Obj;
    }

    #endregion
}

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.