Creation dynamique d'objet, InvalidCastException

Résolu
cs_Warny Messages postés 473 Date d'inscription mercredi 7 août 2002 Statut Membre Dernière intervention 10 juin 2015 - 18 févr. 2007 à 22:04
EmacLi Messages postés 165 Date d'inscription jeudi 3 novembre 2005 Statut Membre Dernière intervention 1 février 2013 - 20 mai 2009 à 21:57
Salut,

Je cherche à créer un chargeur de classe. Celui-ci doit me permettre de trouver les classes dans une assembly ou un ensemble d'assembly qui dérive d'une classe particulière. Ca, ça marche bien. Ensuite j'essaye d'instancier (grâce à une reflection) ces objets et de les caster dans mon type de base. Et là j'obtient une InvalidCastException.

En fait je sèche. Dans ma classe, les procédures importantes sont AddType (qui ajoute un type à la liste des types créables) et Create qui créé une instance d'un type. J'ai une procédure IsBasedOn qui renvoi true si un type dérive d'un autre (par implementation ou interface) et que j'utilise pour valider l'utilisation ou non d'une classe. L'erreur a lieu dans create alors que j'essaye de caster mon objet tout frais sorti du moule.

Merci d'avance de votre aide,
Olivier

Voici les bouts utiles de mon code :

class

Loader {

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>
///Element servant de clef

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>
///Chemin Contenant les assemblies à charger

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>
///

public
void AddAssembly(
string Path) {

//Charge l'assembly
Assembly assembly =
Assembly.LoadFrom(Path);

//Récupère tous les type contenus
Type[] types = assembly.GetTypes();

foreach (
Type type
in types) {AddType(type);

}

}

///<summary>
/// Ajoute un type
///</summary>
///Type à ajouter

private
void AddType(
Type type) {

//Vérifie que le type dérive bien de la classe de base
if (!IsBasedOn(type,BaseType))
return; // <----- Avec cette procédure, je suis sûr que les types dérivent de BaseClass

//Récupère la valeur clef (nom de la classe si le nom est null)
string value;

if (KeyElement.Length > 0) {value = type.InvokeMember(KeyElement,

BindingFlags.InvokeMethod |
BindingFlags.FlattenHierarchy |
BindingFlags.GetProperty |
BindingFlags.Static |
BindingFlags.Public,
null,
null,
null).ToString();}

else {value = type.FullName;

}

//inscrit la classe dans la liste

if (creatable.ContainsKey(value)) {creatable[value] = type;

}

else {creatable.Add(value, type);

}

}

///<summary>
/// Ajoute un type
///</summary>
///Chemin de l'assembly contenant la classe

///Nom de la classe

private
void AddType(
string Path,
string ClassName) {

//Charge l'assembly
Assembly assembly =
Assembly.LoadFrom(Path);

//charge le type particulier
Type type = assembly.GetType(ClassName);

//Ajoute le type
AddType(type);}

///<summary>
/// Retire un type de la liste
///</summary>
///Clef du type à enlever

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>
///Type à tester

///Référence

///<returns></returns>
private
bool IsBasedOn(
Type TestType,
Type BaseType) {

if (TestType.IsSubclassOf(BaseType))
return
true;

if (TestType.FullName == BaseType.FullName)
return
true;

if (TestType.FullName ==
"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>
///Identifiant de l'élément à créer

///Paramètres du constructeur

///<returns>Objet de la classe de base</returns>
public BaseClass Create(
string Id,
params
object[] parameters) {

object Obj;

try {Obj = creatable[Id].InvokeMember(

null,
BindingFlags.CreateInstance |
BindingFlags.Public |
BindingFlags.FlattenHierarchy |
BindingFlags.Instance,
null,
null, parameters);}

catch {Obj = creatable[defaultElement].InvokeMember(

null,
BindingFlags.CreateInstance |
BindingFlags.Public |
BindingFlags.FlattenHierarchy |
BindingFlags.Instance,
null,
null, parameters);}

return (BaseClass)Obj; // <---- l'erreur a lieu ici, le cast est refusé alors que le type dérive bien}

#endregion

}

Console.Out.WriteLine("Warny")

Il y a 10 types de personnes
Ceux qui comptent en binaire... et les autres

4 réponses

cs_Warny Messages postés 473 Date d'inscription mercredi 7 août 2002 Statut Membre Dernière intervention 10 juin 2015
19 févr. 2007 à 17:30
J'ai trouvé. Le problème vient d'un chargement partiel d'assembly voilà la solution :

A la place de
Assembly assembly = Assembly.LoadFrom(Path);

Il faut mettre
Assembly assembly =
Assembly.Load(
AssemblyName.GetAssemblyName(Path)); ;

Qui permet au framework de faire la correspondance dans le cache d'assembly entre celle qu'on charge et celles déjà chargées.

Console.Out.WriteLine("Warny")

Il y a 10 types de personnes
Ceux qui comptent en binaire... et les autres
3
cs_coq Messages postés 6351 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 99
18 févr. 2007 à 22:59
Salut,

Tu devrais vérifier tes retours avant de les utiliser. Après à voir mais je ne suis pas sur que FlattenHierarchy aie sa place ici.
Sinon jette un oeil à Activator.CreateInstance, ça devrait t'intéresser.

/*
coq
MVP Visual C#
CoqBlog
*/
0
cs_Warny Messages postés 473 Date d'inscription mercredi 7 août 2002 Statut Membre Dernière intervention 10 juin 2015
19 févr. 2007 à 08:10
L'Activator.CreateInstance simplifie nettement la lecture du code mais n'arrange pas mon problème.
Comment vérifier mon retour ? J'ai essayé de rajouter un As BaseClass, mais le compilateur le refuse.

J'ai fait un test où toutes mes entrées sont valides (toutes les classes lues dérivent de BaseClass). Même quand j'essaye d'instancier baseClass, j'ai la même erreur (voici la copie du message, c'est un peu frustrant)

L'exception System.InvalidCastException n'a pas été gérée
  Message="Impossible d'effectuer un cast d'un objet de type 'BaseBibliotheque.Parent' en type 'BaseBibliotheque.Parent'."
  Source="TestDynamique"
  StackTrace:
       à AutoCreation.Loader`1.Create(String Id, Object[] parameters) dans c:\projets\TestDynamique\TestDynamique\Loader.cs:ligne 163
       à AutoCreation.Program.Main(String[] args) dans c:\projets\TestDynamique\TestDynamique\Program.cs:ligne 13
       à System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
       à System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       à Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       à System.Threading.ThreadHelper.ThreadStart()

Console.Out.WriteLine("Warny")

Il y a 10 types de personnes
Ceux qui comptent en binaire... et les autres
0
EmacLi Messages postés 165 Date d'inscription jeudi 3 novembre 2005 Statut Membre Dernière intervention 1 février 2013 1
20 mai 2009 à 21:57
Un grand merci Warny ! :)
0