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 :

covariance et interfaces


Sujet :

C#

  1. #1
    Membre averti
    Inscrit en
    Novembre 2005
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 24
    Par défaut covariance et interfaces
    salut.
    je suis désolé si cette question a été posée, j'ai cherché dans la FAQ C# sans succès.

    j'ai une interface IMessage qui représente un message à envoyer:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    public interface IMessage
    {
    string Corps {get; set;}
     
    string Sujet {get; set;}
     
    string Expediteur {get; set;}
     
    ICollection<string> Destinataires {get; set;}
     
    void Composer();
     
    void Concatener(Icollection<IMessage> messages);
    }
    La classe Email implémente IMessage:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    public class Email : IMessage
    {
    public string Corps {/* Ici des get et set sur le champ privé */}
     
    public string Sujet {/* Ici des get et set sur le champ privé */}
     
    public string Expediteur {/* Ici des get et set sur le champ privé */}
     
    public ICollection<string> Destinataires {/* Ici des get et set sur le champ privé */}
     
    public MailMessage LeMail;
     
    public void Composer() {/* crée un objet MailMessage et l'attache à LeMail */}
     
    public void Concatener(ICollection<Email>) {/* concatene les corps de monEmail1+monEmail2+this */}
     
    //tous les champs privés ici...
    }
    Le problème: le compilo râle parce que dans l'interface Concatener() prend en argument une ICollection<IMessage> alors que l'implémentation prend une ICollection<Email>.

    Pourtant Email est bien une implémentation de IMessage.

    Je pourrais implémenter Concatener() comme prenant en argument une ICollection<IMessage> mais alors comment empêcher un client de concaténer des Emails et des Sms, comme dans:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    Sms sms1 = new Sms();
     
    Sms sms2 = new Sms();
     
    ICollection<IMessage> smss = new List<Sms>(2);
     
    smss.Add(sms1);
     
    smss.Add(sms2);
     
    Email unEmail = new Email();
     
    unEmail.Concatener(smss); //Sms implémente IMessage !
     
    //...
    J'aimerais imposer la condition suivante sur les implémentations de IMessage:
    chaque objet ne peut concaténer que des objets du même type.

    (Je suis pas sur que le code ci-desssus compile mais c'est l'idée).
    Merci !

  2. #2
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 273
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 273
    Par défaut
    Ce dont tu as besoin s'appelle un design pattern adapteur;

    Bref tu as besoin d'adapter la concaténation en fonction du type de message
    --> mail avec mail
    --> sms avec sms

    C'est ici

    Et en plus perso, je le trouve très très bien ce tuto !

  3. #3
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 273
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 273
    Par défaut
    sinon, tant que j'y pense, tu peux aussi faire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
     
     
    namespace Bidules
    {
        namespace ClassSupport
        {
            public interface IMessage
            {
                string Corps { get; set; }
                string Sujet { get; set; }
                string Expediteur { get; set; }
                ICollection<string> Destinataires { get; set; }
                void Composer();
                void Concatener<T>(ICollection<T> messages);
            }
     
            public abstract class AbstractMessage<T> : IMessage
            {
     
                #region IMessage Members
     
                public string Corps
                {
                    get
                    {
                        throw new NotImplementedException();
                    }
                    set
                    {
                        throw new NotImplementedException();
                    }
                }
     
                public string Sujet
                {
                    get
                    {
                        throw new NotImplementedException();
                    }
                    set
                    {
                        throw new NotImplementedException();
                    }
                }
     
                public string Expediteur
                {
                    get
                    {
                        throw new NotImplementedException();
                    }
                    set
                    {
                        throw new NotImplementedException();
                    }
                }
     
                public ICollection<string> Destinataires
                {
                    get
                    {
                        throw new NotImplementedException();
                    }
                    set
                    {
                        throw new NotImplementedException();
                    }
                }
     
                public void Composer()
                {
                    throw new NotImplementedException();
                }
     
                public virtual void Concatener<T>(ICollection<T> messages)
                {
                    throw new NotImplementedException();
                }
     
     
                #endregion
            }
     
            public class Email:AbstractMessage<Email>
            {
                public override void Concatener<Email>(ICollection<Email> messages)
                {
                    base.Concatener<Email>(messages);
                }
            }
            public class Sms : AbstractMessage<Sms>
            {
                public override void Concatener<Sms>(ICollection<Sms> messages)
                {
                    base.Concatener<Sms>(messages);
                }
            }
     
            public class howto
            {
                static void main()
                {
                    Sms sms=new Sms();
                    sms.Concatener(new Collection<Sms>());
     
                    Email mail=new Email();
                    mail.Concatener(new Collection<Email>());
     
                }
            }
     
        }
    }
    Tu as toujours le bénéfice de l'interface, et surtout tu peux t'appuyer sur des génériques plutôt que de la réflexion pour gérer la dissociation.

  4. #4
    Membre averti
    Inscrit en
    Novembre 2005
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 24
    Par défaut
    Citation Envoyé par B.AF Voir le message
    Ce dont tu as besoin s'appelle un design pattern adapteur...
    sinon, tant que j'y pense, tu peux aussi faire ça :...
    ok merci pour ces 2 bonnes idées.
    petites remarques:
    - pour le design pattern:
    c'est bien mais j'espérais que la solution serait moins compliquée. Le fait d'encapsuler mon Email dans un autre objet ça me fait mal vu que j'avais fait exprès une interface pour que toutes les classes du genre Email, Sms etc... soient manipulables de la même façon...c'est embêtant de devoir réencapsuler tous mes trucs...vu que c'est moi qui définit les classes et les interfaces je devrais bien pouvoir m'en sortir autrement...
    - pour les génériques:
    là y a la totale: interface+classe abstraite+génériques ! c'est bien mais c'est vraiment la grosse artillerie. mais c'est pas bête...

    en fait je croyais bêtement que je pouvais m'en sortir avec du polymorphisme...merci pour les idées.

  5. #5
    Membre averti
    Inscrit en
    Novembre 2005
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 24
    Par défaut
    après avoir réfléchi je me rends compte que c'est normal que le compilo se plaigne: mon contrat stipule que Concatener() prend une ICollection<IMessage> comme argument mais ma fonction réelle exige une ICollection<Email>...ie je dis au client: "passe moi ce que tu veux du moment que ça implémente IMessage" mais dès qu'il veut appeler ma méthode je lui dis "ah non désolé je prends que des Email"...
    l'argument de l'implémentation devrait être plus général que celui de l'interface, pas moins.
    comment c'est déjà ? ah ouais

  6. #6
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 273
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 273
    Par défaut
    Oui c'est pour ça que je te dis :

    Soit tu as besoin d'adapter
    Soit tu as besoin de spécifier quel type reçoit ta collection

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

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Problème classique, vieux comme le C++

    Avec une chtite contrainte (clause where),c e genre de problème se résoud très bien. Remplace
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Concatener(Icollection<IMessage> messages);
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Concatener<U>(Icollection<U> messages) where U : IMessage;
    La prochaine version de C# (4) gèrera la covariance, en attendant on se débrouille.

  8. #8
    Membre averti
    Inscrit en
    Novembre 2005
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 24
    Par défaut
    Citation Envoyé par Guulh Voir le message
    Problème classique, vieux comme le C++

    Avec une chtite contrainte (clause where),c e genre de problème se résoud très bien. Remplace
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Concatener(Icollection<IMessage> messages);
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Concatener<U>(Icollection<U> messages) where U : IMessage;
    La prochaine version de C# (4) gèrera la covariance, en attendant on se débrouille.
    si je n'ai pas répondu à B.AF c'est que je n'ai pas compris sa réponse
    non pas que ça soit pas clair, mais je voyais pas comment ça marchait.

    La tienne non plus je la comprends pas mais je te crois .

    faut que j'y réfléchisse.

    merci.

    edit: le remplacement c'est dans l'interface, dans la classe abstraite ou dans l'implémentation ?

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

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Juste un truc : a quoi sert ta classe, vu qu'elle a les memes champs que System.Net.Mail.MailMessage ?
    Citation Envoyé par pequatre Voir le message
    edit: le remplacement c'est dans l'interface, dans la classe abstraite ou dans l'implémentation ?
    Bah dans tous. Ce que je te dis juste, indépendamment de ta problématique d'interface et d'implementation, c'est que si tu veux qu'une methode puisse prendre n'importe quel Machin<U> pourvu que U hérite de T, tu dois changer sa signature de void f(Machin<T>) en void f(Machin<U>) where U : T.

    Mais ca ne repond pas a ta question, que j'avais mal comprise Sans passer par un DP, ce que tu souhaites n'a pas l'air possible, comme le montre B.AF. Une meme methode a forcement la meme signature pour une classe (ou une interface bien sur) et tous ses descendants. Il est pas possible de la specialiser en cours de route.

    Et si j'ai bien compris la derniere suggestion de B.AF :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    interface IMessage<T>
    {
      ...
      void Concatener<T>(ICollection<T> messages);
    }
     
    class Email : IMessage<Email>
    {
     ...
    }
     
     
    class SMS : IMessage<SMS>
    {
     ...
    }
    Tu vois le truc ?

  10. #10
    Membre averti
    Inscrit en
    Novembre 2005
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 24
    Par défaut
    Citation Envoyé par Guulh Voir le message
    Juste un truc : a quoi sert ta classe, vu qu'elle a les memes champs que System.Net.Mail.MailMessage ?
    l'idée c'est: tous les messages (emails, sms, autres) ont en général un sujet, un expéditeur, un destinataire et un corps. Rassembler tout ça sous l'interface IMessage permettrait à un client de traiter un IMessage, éventuellement de modifier le sujet et le corps, puis de l'envoyer, sans qu'il ait besoin de savoir s'il a affaire à un email, un sms, un message jabber, etc...
    Bah dans tous. Ce que je te dis juste, indépendamment de ta problématique d'interface et d'implementation, c'est que si tu veux qu'une methode puisse prendre n'importe quel Machin<U> pourvu que U hérite de T, tu dois changer sa signature de void f(Machin<T>) en void f(Machin<U>) where U : T.
    d'accord.
    Une meme methode a forcement la meme signature pour une classe (ou une interface bien sur) et tous ses descendants. Il est pas possible de la specialiser en cours de route.
    ok. ce que j'essaie de faire c'est exposer une méthode IMessage.Concatener(IMessage message) mais en exigeant une contrainte plus forte dans l'implémentation (i.e. qu'un IMessage ne puisse concaténer que des IMessages du même type réel que lui.)
    Je crois avoir compris que c'est impossible puisque mon client n'a aucun moyen de savoir quels IMessages il peut ou ne peut pas passer en arguments à Concatener...
    Tu vois le truc ?
    je crois. je vais voir ce que ça donne dans visual studio.
    merci.

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

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par pequatre Voir le message
    ok. ce que j'essaie de faire c'est exposer une méthode IMessage.Concatener(IMessage message) mais en exigeant une contrainte plus forte dans l'implémentation (i.e. qu'un IMessage ne puisse concaténer que des IMessages du même type réel que lui.)
    Je crois avoir compris que c'est impossible puisque mon client n'a aucun moyen de savoir quels IMessages il peut ou ne peut pas passer en arguments à Concatener...
    C'est possible si ton interface n'est pas IMessage, mais IMessage<T>.

    Prend l'interface IEquatable<T>, par exemple. Eh bien, dansle framework, int implémente IEquatable<int>, double implémente IEquatable<double>. Et dont la méthode Equals d'un int ne peut prendre qu'un int en paramètre. C'est exactement ce que tu veux

  12. #12
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 273
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 273
    Par défaut
    Oui, c'est ce que je lui ai proposé, à la différence que l'utilisation de l'héritage permet de décaler le besoin de généricité au niveau de la collection, soit là où il a son problème par surcharge de la méthode concat.

    Et c'est pas du tout lourd.
    Ce qui est lourd justement c'est de gérer avec du code ce qui peut être gérer par le paradigme objet.

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

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Par défaut
    Citation Envoyé par B.AF Voir le message
    Oui, c'est ce que je lui ai proposé, à la différence que l'utilisation de l'héritage permet de décaler le besoin de généricité au niveau de la collection, soit là où il a son problème par surcharge de la méthode concat.

    Et c'est pas du tout lourd.
    Ce qui est lourd justement c'est de gérer avec du code ce qui peut être gérer par le paradigme objet.
    Beh disons que cette approche permet certes d'avoir une méthode pour toute une série d'objet. En revanche, elle complique l'utilisation de la classe IMessage : on ne peut plus manipuler une instance d'IMessage sans savoir le type concret. Si l'on veut manipuler des collections de messages de types hétérogènes, il faudrait une interface mère IMessage, avec toutes les propriétés et méthodes non génériques, et une interface IMessage<T> qui en hérite et ajoute le fameux Concatener<T>. Une telle collection serait alors une List<IMessage> (et on ne pourrait pas appeler Concatener sur ses éléments, ce qui est normal puisque ne connaissant pas le type des objets on ne saurait détecter des erreurs )

  14. #14
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 273
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 273
    Par défaut
    Citation Envoyé par Guulh Voir le message
    . Si l'on veut manipuler des collections de messages de types hétérogènes, il faudrait une interface mère IMessage, avec toutes les propriétés et méthodes non génériques, et une interface IMessage<T> qui en hérite et ajoute le fameux Concatener<T>. Une telle collection serait alors une List<IMessage> (et on ne pourrait pas appeler Concatener sur ses éléments, ce qui est normal puisque ne connaissant pas le type des objets on ne saurait détecter des erreurs )
    Ben c'est ce qu'il cherche.
    Quite à ne pas accepter la possibilité d'avoir un SMS dans les EMails, autant que ce contrôle ne soit pas fait dans le code.

    C'est inutile d'utiliser des interfaces pour finalement réfléchir les types pour savoir si il sont élligibles ou pas à une collection d'objets qui implémentent tous cette interface.

    Donc, soit l'interface ne sert à rien.
    Rendre l'interface générique ne fait que repousser le problème : T ne sera pas T dans l'implémentation.
    Donc que la collection soit typée du fait de l'interface ou du fait d'un héritage, c'est blanc manteau et manteau blanc.

    Le postulat de base de toutes façon est faux :
    Utiliser des interfaces pour généraliser des comportements alors qu'en fait les comportements ne sont pas identiques, c'est que le modèle est faux.

    Donc en fait, le prob est plus haut.
    Nous ne voyons que sa conséquence.

  15. #15
    Membre averti
    Inscrit en
    Novembre 2005
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 24
    Par défaut
    je passe le sujet en résolu.merci pour votre aide.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [VB6] [Interface] ComboBox à plusieurs colonnes
    Par mtl dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 30/03/2004, 17h35
  2. [VB6] [Interface] Horloge 7 segments
    Par selenay dans le forum VB 6 et antérieur
    Réponses: 11
    Dernier message: 07/10/2002, 16h15
  3. interface utilisateur avec OpenGL
    Par demis20 dans le forum OpenGL
    Réponses: 6
    Dernier message: 03/10/2002, 12h27
  4. [VB6] [Interface] Tester le Type de Controle
    Par SpaceFrog dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 16/09/2002, 09h51
  5. [VB6] [Interface] Icones de boutons de barre d'outils
    Par elifqaoui dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 13/09/2002, 15h50

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