Creation dynamique d'objet, InvalidCastException [Résolu]

cs_Warny
Messages postés
478
Date d'inscription
mercredi 7 août 2002
Dernière intervention
10 juin 2015
- 18 févr. 2007 à 22:04 - Dernière réponse : EmacLi
Messages postés
165
Date d'inscription
jeudi 3 novembre 2005
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
Afficher la suite 

Votre réponse

4 réponses

Meilleure réponse
cs_Warny
Messages postés
478
Date d'inscription
mercredi 7 août 2002
Dernière intervention
10 juin 2015
- 19 févr. 2007 à 17:30
3
Merci
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

Merci cs_Warny 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 94 internautes ce mois-ci

Commenter la réponse de cs_Warny
cs_coq
Messages postés
6366
Date d'inscription
samedi 1 juin 2002
Dernière intervention
2 août 2014
- 18 févr. 2007 à 22:59
0
Merci
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
*/
Commenter la réponse de cs_coq
cs_Warny
Messages postés
478
Date d'inscription
mercredi 7 août 2002
Dernière intervention
10 juin 2015
- 19 févr. 2007 à 08:10
0
Merci
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
Commenter la réponse de cs_Warny
EmacLi
Messages postés
165
Date d'inscription
jeudi 3 novembre 2005
Dernière intervention
1 février 2013
- 20 mai 2009 à 21:57
0
Merci
Un grand merci Warny ! :)
Commenter la réponse de EmacLi

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.