IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Casts et surcharge de fonction


Sujet :

C#

  1. #1
    Invité
    Invité(e)
    Par défaut Casts et surcharge de fonction
    Bonjour tout le monde,

    J'utilise actuellement un équivalent de factory qui instancie différents types d'objets selon des conditions précises, et les retourne en les (up?)castant vers une classe dont tous ces objets héritent.
    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Class BaseObject {}
    Class SubObject1 : BaseObject {}
    Class SubObject2 : BaseObject {}
    Ce que fait la factory:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    public static BaseObject SpawnObject(String str)
    {
      if (str == "toto")
      {
         SubObject1 tutu = new SubObject1();
         return (BaseObject)tutu;
      }
      if (str == "titi")
      {
         SubObject1 tutu = new SubObject2();
         return (BaseObject)tutu;
      }
    }
    Ce que j'aimerais faire dans l'idéal est que la fonction appelant la factory (et recevant donc un BaseObject) puisse appeler une autre fonction "générique" en passant cet objet, et que la fonction appelée puisse déduire facilement ce qui est à faire (d'après les tests que j'ai fait, la surcharge ne semble pas possible car le type passé est le type retenu pour la surcharge).
    Y'aurait-il un moyen simple de faire ce que je souhaite ? Genre comme en PHP ou on peut faire ça sans souci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <?php $toto = 'uneClasse'; $tutu = new $toto(); ?>
    J'ai bien pensé à ajouter un membre String contenant le type de la classe récupéré par la reflection, mais je trouve ça malhabile.

    Merci d'avance

  2. #2
    Membre éprouvé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2011
    Messages
    487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2011
    Messages : 487
    Points : 945
    Points
    945
    Par défaut
    Bonjour,

    J'avoue ne pas avoir tout saisi (et surtout pas l'exemple en php ), les titi toto et tutu m'embrouillent :p

    Est ce que tu ne chercherais pas quelque chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if(toto is SubObject1)
    	tata = toto as SubObject1;

  3. #3
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    Je suis pas sûr d'avoir compris. En gros, après avoir appelé ta factory et récupéré une référence de "BaseObject", tu voudrais appeler une autre méthode qui prendrait ce BaseObject en paramète et se comporterait différemment selon son type réel ?
    Un truc comme ça ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void Bidule(BaseObject obj)
    {
      if (obj is SubObject2)
      {
        ((SubObject2)obj).Machin = Truc;
      }
      else if (obj is SubObject1)
      {
         ...
      }
    ...
    }
    Que doit faire cette méthode ? Si elle sert à initialiser l'objet, autant que ce soit la méthode factory qui le fasse. Elle peut aussi être une méthode membre abstraite de BaseObject et surchargée dans les classes filles, ce qui évite le pseudo-switch moche sur le type ci-dessus.

    Mais en tous cas, tu pourrais détailler ce que cette fameuse fonction est sensée faire, avec un exemple concret ?

    D'ailleurs, il n'est pas utile de forcer le cast. Tu peux simplement écrire "return tutu", parce qu'un upcast est implicite.

  4. #4
    Invité
    Invité(e)
    Par défaut
    L'exemple PHP permettait de voir un peu ce que je voulais faire (création de classe en dynamique à partir d'un string; en gros dans l'exemple PHP lit le contenu de la variable et l'utilise pour instancier la classe 'uneClasse').

    Sinon effectivement je pourrais faire un gros switch/case ou if/else if, mais j'aimerais éviter car c'est lourd et je suis sûr qu'on peut mieux faire

    Pour expliciter mieux mon exemple de code, j'ai trois groupes de classes. Le premier, c'est la factory qui instancie tous les objets à partir d'un string et retourne une référence dessus. Le second groupe représente les objets que je manipule (la classe générique mère, et les classes filles qui en dérivent et sont renvoyées par la factory avec l'upcasting). Enfin le troisième groupe est une seule classe qui joue un peu le rôle d'inventaire en manageant tous les objets qu'on lui passe.
    En gros, le main() passe un string à la Factory qui lui renvoie un objet upcasté (le main() ne connait pas son type et s'en fiche), puis le main envoie cet objet crée à la classe d'inventaire qui fait ce qu'elle a à faire en connaissant, elle, le type de l'objet passé (et le stock dans un des dictionnaires servant d'inventory).

    Du coup Guulh je ne vois pas trop comment je pourrais mettre en place ton histoire de surcharge des classes filles...

  5. #5
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    Si c'est à partir du nom complet de la classe que tu veux l'instancier, autant utiliser la réflexion (Activator.CreateInstance ) plutôt qu'un switch manuel.

    Je parlais de méthodes membres parce que ton besoin était flou, que je ne savais pas quelle était la nature du traitement que tu veux effectuer sur tes objets, et que le moyen le plus simple d'avoir un comportement qui dépend du type, c'est d'avoir une méthode membre virtuelle et de se reposer sur le polymorphisme

    Dans ce que tu décris: ton troisième groupe de classe, il a vraiment besoin de connaîte le vrai type des objets créés ? Si c'est juste pour le mettre dans un repository, récupérer le type réel via GetType ne te suffit pas ? Ou bien alors tu as explicitement un dico de SubObject1, SubObject2, ... ? Auquel cas ce ne serait pas très générique, par rapport à la (trop grande ?) souplesse que tu t'autorises/t'imposes en passant par des chaînes pour créer des types.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Merci pour l'activator, je ne connaissais pas ! Je vais regarder ça de plus près...

    Sinon effectivement j'utilises des listes de classes bien précises, ce qui n'est pas très générique, mais je travail sur un nombre d'objets fini (pour information, mes listes contiennent des classes d'objets DHCP... À moins que MS, ISC et les créateurs de RFC ne bougent tout ça, ce n'est pas près de changer).
    Pour être vraiment précis, la classe de stockage gère une dizaine de listes contenant les scopes et leurs paramètres, les ranges et leurs paramètres, etc...D'où le fait qu'elle prenne un objet quelconque en entrée, déduise sa classe et trouve ou le placer dans ces listes.

  7. #7
    Invité
    Invité(e)
    Par défaut
    J'ai regardé un peu comment marche l'Activator, et soit je n'ai pas compris comment l'utiliser, soit je doute qu'il m'aide.
    Ce que je vois à l'heure actuelle, ce sont deux moyens dont un hypothétique d'arriver à mes fins (si vous voyez autre chose, je suis preneur !).
    D'une, je fais un switch/case sur une propriété (reflection, String en dur, ou GetType) pour savoir dans quel dictionnaire ajouter mon objet (du coup, le code est à mettre au niveau de la classe de stockage, et suppose que la reflection ou la fonction GetType me renvoie bien la classe réelle et non la classe d'upcast).
    De deux, je trouve un moyen dans le main() de virer l'upcast et de faire comprendre au compilo que je veux utiliser la classe réelle (fille) de l'objet et donc pouvoir passer par une surcharge de la fonction d'ajout de ma classe de stockage... (comment ça, je me prends la tête ? )

    Enfin en tout cas, merci beaucoup pour le coup de main

  8. #8
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    Cool, si ça a pu t'être utile

    Une précision quand même : C# est un langage typé, et bien que l'on puisse faire via réflexion un certain nombre de choses "innées" dans des langages dynamiques tels que PHP, ce n'est que rarement la solution la meilleure. En l'occurence, tu n'es pas en train de développer une librairie générique, comme par exemple une lib d'injection de dépendance qui se chargerait de créer des objets et de les exposer, à partir s'un fichier de config qui listerait les types concernés. Ici, tes types sont connus.

    D'où te viennent ces chaînes en entrée ? D'un fichier ? Du code lui-même ?

  9. #9
    Invité
    Invité(e)
    Par défaut
    Les chaînes viennent de la sortie d'un programme externe, NetSH (je n'ai pas vu d'autre moyen de récupérer/requêter les infos DHCP).
    Pour une simplification du Main(), je voulais pouvoir gérer tous les éléments de manière générique et transparente (le Main joue le rôle de manager et se fout de savoir ce que font/sont les données, il les dispatch et basta).
    Du coup après avoir bouffé un peu de doc et d'exemples sur Activator (genre ça : http://www.developpez.net/forums/d25...sse-generique/ ), et un équivalent de reinterpret_cast & co, je pense que je ne vais pas pouvoir couper au switch/case en utilisant un GetType (qui renvoie bien la classe fille, alors que la reflection renvoie la classe mère dans mon exemple).

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    312
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 312
    Points : 411
    Points
    411
    Par défaut
    Bonjour,

    En partant du principe que j'ai bien compris..

    En gros, le main() passe un string à la Factory qui lui renvoie un objet upcasté (le main() ne connait pas son type et s'en fiche), puis le main envoie cet objet crée à la classe d'inventaire qui fait ce qu'elle a à faire en connaissant, elle, le type de l'objet passé (et le stock dans un des dictionnaires servant d'inventory).
    C'est ici que tu 'casse' la logique objet qui veut que justement, les types manipulés ne soient pas connus, pour garder ton abstraction.

    Si ta factory fait
    if ... then return new typeA
    if ... then return new typeB

    et que ta classe 'inventaire' fait de son coté :

    if typeA then ...
    if typeB then ...

    Ca ne sert à rien, car l'objectif de l'héritage (ou l'implémentation d'interface) n'est pas de transporter des objets sans connaitre leur type (car à ce moment la le type de base Object suffirait), mais d'appeler des méthodes sans connaitre leur implémentation concrète.

    Pour conclure, idéalement tu ne devrais pas avoir de if dans ta classe 'inventaire' mais uniquement.

    ObjectMachin.doJob();

    En considérant que ObjetMachin est ton type parent.

  11. #11
    Invité
    Invité(e)
    Par défaut
    Merci Nah pour la contribution
    Je dois avouer ne pas être une star de l'objet... Dans mon cas, je ne vois pas vraiment comment je pourrai faire ça de la manière décrite
    La classe Inventaire n'ayant aucun rapport avec les classes d'objets gérés, comment ces classes pourraient "s'autogreffer" ?
    Le seul parallèle que je pourrais faire serait que le Main est le contrôleur, la classe d'inventaire une base de données, et les données... sont les données (d'ailleurs ça me rappelle le MVC ça). Du coup comment les données pourraient avoir le moindre contrôle sur ce que la base va en faire ?

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    312
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 312
    Points : 411
    Points
    411
    Par défaut
    Mais je t'en prie.

    Je connais malheureusement que trop peu ton projet pour être précis.
    Néanmoins, l'idée de base est que ta classe doit masquer son comportement.

    Imaginons un jeu de combat..

    Tu crée des unités avec ta factory qui te retourne des classes filles de 'personnage', mais ce sera en fait des instances de 'mecBaleze' ou 'filleFragile'.

    Voici ce que ca devrait donner:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Factory fact = new Factory(); // notre factory nous retourne un mecBaleze ou une fille fragile
     
    fact.getPersonnage(currentContext).Combattre(); // ici j'appelle combattre sans savoir sur quelle implémentation de personnage je suis
    Voila en gros l'idée, en espérant avoir été clair.


    Peut être pourrais tu préciser quelles spécificités sont liées aux if dans la classe inventaire ?

  13. #13
    Invité
    Invité(e)
    Par défaut
    Donc je récapépette depuis le bédut avec précision.
    En gros, je code un programme qui interagit avec le serveur DHCP de Microsoft. Je souhaite récupérer les informations (scopes, ranges, etc...) et les organiser de manière simple.
    J'avais donc pensé à une classe de Factory qui s'occupe de récupérer en paramètre une chaîne de caractère (c'est la sortie d'une commande permettant d'avoir les infos du DHCP), de la parser, et retourner le bon type d'objet upcasté.
    Cet objet serait ensuite passé à une classe "simple" qui s'occuperait de savoir ou placer l'objet (dans quelle liste, à quel endroit selon l'adresse et la parentalité de l'objet).
    Ce dernier point implique de connaître le type de la classe (et comme je trouve plus simple que la portion de code faisant la jonction entre la fabrique et l'inventaire ne tienne pas compte du type pour être générique, je me complique la vie).

Discussions similaires

  1. surcharge de fonction
    Par BigNic dans le forum C++
    Réponses: 2
    Dernier message: 21/03/2006, 18h57
  2. API HOOK, Dump dll, Surcharge de Fonction
    Par MicroAlexx dans le forum Windows
    Réponses: 2
    Dernier message: 30/12/2005, 10h39
  3. [MFC] Surcharger des fonctions de CView
    Par Philippe320 dans le forum MFC
    Réponses: 2
    Dernier message: 22/11/2005, 21h24
  4. singleton, polymorphisme, et surcharge de fonction
    Par legend666 dans le forum C++
    Réponses: 11
    Dernier message: 14/11/2005, 09h27
  5. Surcharge de fonction d'un edit dynamique
    Par Tartar Ukid dans le forum C++Builder
    Réponses: 4
    Dernier message: 13/10/2003, 11h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo