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 :

Instance d'une classe avec une string


Sujet :

C#

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut Instance d'une classe avec une string
    Bonjours à tous,

    Je souhaiterais instancier l'objet d'une classe dont le nom serait connu uniquement dans une variable string...

    Je sait que ce problème a déja été traité maintes fois sur ce forum ou même partout ailleurs.

    Malheureusement, j'ai eu quelques difficultés en ce qui concerne l'interprétation, voire la compréhension des info que j'ai pu recueillir sur le sujet !

    Je vais clairement expliquer le but de la manoeuvre, à présent...

    --> Dans un programme C#(mode console), je dispose des classes suivantes:
    1 - classe program : qui contient les méthodes (actions: lister, ajouter, miodifier, etc...) communes à tout cas d'utilisation !
    2 - classe mère "Article" : c'est la classe mère qui possède des méthodes et des attributs commun à toutes les classes fille !
    3 - plusieurs classes filles telle que "ArticleAcheté" : ces classes possèdes des attributs et méthodes qui leur sont spécifiques, et peuvent évidement accéder aux attributs et méthodes(virtual-override) commun de la classe mère...

    Sachant, que les actions (méthodes : lister, ajouter, modifier, etc...), se trouve dans la classe Program, voici l'exemple précis de mon cas d'utilisation:
    --> pour la fonction Lister :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    string nomClass = "ArticleAcheté";
    static public void LISTER (string nomClass)
    {
    nomClass toto = new nomClass();
    ....
    }
    Dans cet exemple, l'objet nouvellement crée "toto", devrait se voir appliquer les méthodes et attributs de la classe fille "ArticleAcheté"...
    Cependant, mon exemple est faux : en effet, on ne peut pas instancier un objet directement avec une variable string !

    Par conséquent, je recherche actuellement le moyen de créer l'objet dit "toto", a partir de la variable string "nomClass", laquelle contient le nom de la classe à instancier pour l'objet "toto" !

    De cette manière, l'objet "toto" devrait se voir affecter tout les attributs et méthodes de la classe fille "ArticleAcheté"...

    --> Exemple : Si la classe fille "ArticleAcheté" possède des méthodes spécifiques telles ques : afficher(), saisir(), getFournisseur(), etc...
    Alors, on pourrait faire ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    toto.afficher();
    toto.saisir();
    toto.getFournisseur();
    Voila, j'espere avoir été le plus explicite possible...

    Je reste dès à présent ouvert à toutes suggestions...

    Je vous remercie par avance, pour toute l'aide que vous pourrez m'apporter...

  2. #2
    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
    Oula, tu te compliques la vie
    Etant donné que dans ta méthode "Lister", tu veux utiliser des méthodes propres à ton objet "ArticleAcheté", elle est fortement lié à cette classe ; tu peux faire directement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public void ListerArticleAcheté()
    {
    ArticleAcheté toto = new ArticleAcheté();
    toto.
    }
    Tu vois ce que je veux dire ? pourquoi tu veux rendre générique une méthode, alors qu'elle ne manipulera qu'un seul type d'objet ?

    Sinon, connais-tu les generics ? Ca permet de paramétrer une méthode par type, c'est très puissant, joli, et tout

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    En fait, si je procéde comme l'extrait de code, je suis obligé de nommé explicitement la classe (ArticleAcheté), pour laquelle mon objet va être instancié...

    Mais je dispose de nombreuses classes filles, il n'y a pas que la classe fille "ArticleAcheté" en fait...

    Par conséquent, cela voudrait dire qu'il y aurait autant d'objet à instancier que de classe fille, et le code de la méthode Lister (dans mon exemple), devrait être recopier autant de fois qu'il y aurait d'objet différent...
    Imagine le rendu dans une architecture de type Framework !!

    Je veux simplement rendre les éléments plus modulaire et indépendant, en instanciant un objet, pour une classe donnée à partir de son nom (lequel est fournie uniquement dans une variable string, passé en paramètre à la méthode appelante)

    Autre bémol, la méthode ne peut pas s'appeler "ListerArticleAcheté", étant donné qu'elle ne listera pas que des éléments de la classe "ArticleAcheté", mais de toutes les classes filles !

  4. #4
    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 beetlejuice722 Voir le message
    Dans cet exemple, l'objet nouvellement crée "toto", devrait se voir appliquer les méthodes et attributs de la classe fille "ArticleAcheté"...
    Donc t'es bien d'accord que dans méthode Lister, tu vas appeler des méthodes propres à ArticleAcheté ?

    Sinon, comme je te disais, les generics devraient satisfare ton besoin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public static void Lister<T>() where T : Article, new()
    {
      T toto = new T(); // possible grâce à la contrainte new() sur le type T
      toto.afficher(); // possible grâce à la contrainte d'héritage sur T
      ...
    }

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    Je suis tout à fait d'accord avec ton exemple...

    Je l'ai testé, mais avec cette manière l'objet toto, n'aurait pas accès aux attributs et méthodes spécifiques des classes fille, telle que "getFournisseur()" pour la classe fille "ArticleAcheté" !

    N'y a t-il pas un moyen pour passé en paramètre la classe à instancier (Article dans ton exemple), sans qu'elle soit désigné explicitement ?

    quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    string maClasseFille = "ArticleAcheté";
    // Ou n'importe quel autre nom de classe fille : il n'y a pas que "ArticleAcheté"
    public static void Lister<T>() where T : maClasseFille, new()
    {
      T toto = new T(); // possible grâce à la contrainte new() sur le type T
      toto.getFournisseur(); // appel d'une méthode spécifique à une classe fille
      ...
    }
    En tout cas, je te remercie de m'aider dans mon problème !

  6. #6
    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
    Je reprends ce que je disais tout à l'heure.

    Tu veux appeler des méthodes propres à la classe fille dans Lister. Donc t'es bien d'accord que Lister est "propre" à ta classe fille ? Que tu vas toujours l'appeler comme ça : Lister("ArticleAcheté"), puisque sinon tu pourras pas appeler getFournisseur, puisque sinon ton toto sera autre chose qu'un ArticleAcheté ?

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    Non ce n'est pas ca...

    Tout est dans le "sinon" que tu exprimé, à savoir :

    1 - mon "toto" peut être autre chose qu'un ArticleAcheté : par exemple il peut être un ArticleEpuisé, élément d'une autre classe fille "ArticleEpuisé" !

    2 - Je ne l'appelerais pas comme cela :
    Lister("ArticleAcheté")
    mais plutot comme cela :
    string nomClasseFille = "nom_de_nimporte_quel_classe_fille"
    Lister(nomClasseFille)

    la variable string "nomClasseFille", peut prendre des valeurs différentes : "ArticleAcheté", "ArticleEpuisé", et autant d'autre valeur(ou nom) qu'il y aura de classe fille !

    Mais dans tout les cas, la variable string (nomclassefille), sera initialisée au nom de la classe appelante de la methode Lister !

    Pour être plus précis, voila le déroulement des evenements :
    1- Je veut agir (ou gérer) des article épuisé, donc mon menu général (le main), me renvoi vers un sous-menu : celui de la classe fille "ArticleEpuisé"
    2 - Une fois dans ce sous-menu, je peut par exemple lister mes articles épuisé
    : par conséquent, mon sous menu (classe ArticleEpuisé) appele la méthode générique Lister (qui se trouve dans la classe Program)...
    3 - Mais lorsque j'appele la méthode lister, je lui passe en paramètre une string nommé nomClasse, laquelle est initialisé à "ArticleEpuisé", puiske je suis dans la classe ArticleEpuisé...
    Cela veut dire que si on était dans la classe "ArticleAcheté", il y aurait aussi une variable string nomClasse, mais qui serait initialisée à "ArticleAcheté", puis on listerait des ArticleAcheté...

    Pour éviter toutes confusion, voici quelques extraits de codes, à titre indicatif :
    Je suis dans la classefille "ArticleEpuisé", au niveau du sous menu qui permet la gestion des article épuisé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    string nomClasse = "ArticleEpuisé";
    //J'appel ici la améthode lister de la classe program, afin de lister (dans ce //cas précis) des article épuisé
    Program.lister(nomClasse)
    Maintenand, nous voici dans la classe program (qui repertorie les methodes génériques telle que lister, ajouter, rechercher, etc...) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    static public lister (string nomClasse) 
    // Donc, dans la variable nomClasse , on récupère le nom de la classe fille appelante //de notre méthode lister
    {
    }
    De cette manière, on doit pouvoir lister aussi bien des article acheté que des article épuisé de facon tout à fait indépendante...

    Ma problématique réside dans le fait de créer un objet dont le type (celui de la classe fille qui appèle la methode lister) serait uniquement connu à travers la variable string nomClass...

    Une fois de plus, merci pour toute l'aide que tu m'apporte...

  8. #8
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 197
    Par défaut
    il est possible de créer une instance à partir du nom de la classe as string avec la fonction CreateInstance
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  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
    Citation Envoyé par sperot51 Voir le message
    il est possible de créer une instance à partir du nom de la classe as string avec la fonction CreateInstance
    Non, pitié, pas de réflexion quand ça sert à rien

    Pourquoi absolument passer par des chaînes ? Selon ton menu, tu appelleras Lister<Bidule>() ou Lister<Truc>(). Passer par des chaînes de caractère ne sert à rien.

  10. #10
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 197
    Par défaut
    j'ai pas lu exactement ce qu'il voulait, je répondais juste à la question du titre ^^

    mais la reflection c'est très pratique, et c'est pas lent tant qu'on ne fait des boucles qui font de la reflection dans tous les sens
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    Pour répondre au 1er message de sperot51 :
    Je connais cette fonction, mais dans la pratique, je ne suis pas encore parvenu à la mettre en place !

    Voici ce que j'avais fait avec cette fonction, par rapport à mon exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // Admettons quon liste des article épuisé,
    //on aurait alors quelque chose comme :
    string nomClasse = "ArticleEpuisé";
    static public void lister(string nomClasse)
    {
         Type z = Type.GetType(nomClasse);
         object toto =  Activator.CreateInstance(z);
    }
    Mais dans ce cas précis, l'objet toto n'hérite pas des attributs et méthodes spécifiques de la classe fille "ArticleEpuisé"...
    En effet, on ne peut pas faire : toto.getNomArticleEpuisé()

    En revanche, cela marche, si l'on procéde comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    string nomClasse = "ArticleEpuisé";
    static public void lister(string nomClasse)
    {
         Type z = Type.GetType(nomClasse);
         ArticleAcheté toto = (ArticleAcheté)Activator.CreateInstance(z);
    }
    Mais ce deuxième procédé est inutile dans mon cas d'utilisation, puisque la classe fille "ArticleAcheté" est explicitement désignée lors de la création de l'objet toto...

    N'y aurait-il pas une autre manière de faire, sans pour autant nommer explicitement la classe fille, dans la méthode lister ?

  12. #12
    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 sperot51 Voir le message
    j'ai pas lu exactement ce qu'il voulait, je répondais juste à la question du titre ^^

    mais la reflection c'est très pratique, et c'est pas lent tant qu'on ne fait des boucles qui font de la reflection dans tous les sens
    Je sais je sais Mais comme ça se place en dehors d'une modélisation objet "propre", je pense qu'il ne faut s'en servir que si nécessaire. Surtout quand il n'y a pas de binding, ni de sérialisation, etc. Et si on en dépend trop, un changement de nom de classe (par exemple) risque de plus marcher, le compilo n'étant pas capable statiquement de savoir si un bout de code avec de la réflexion de partout pètera ou pas.

  13. #13
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 197
    Par défaut
    dans le code que tu mets, tu utilises createinstance avec un type, moi je dis que c'est faisable avec un string

    enfin je comprends pas trop ton soucis, cherche des infos sur le design pattern factory, peut etre que c'est ca qu'il te faut
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    La solution de Guulh m'a l'air intéressante, mais je reste septique en ce qui concerne le <Bidule> ou le <Truc>

    Pour reprendre ton exemple de tout à l'air tu m'a suggérer cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public static void Lister<T>() where T : Article, new()
    {
      T toto = new T(); // possible grâce à la contrainte new() sur le type T
      toto.afficher(); // possible grâce à la contrainte d'héritage sur T
      ...
    }
    dans cet exemple tu indique que le <T> est de type "Article", afin de travailler sur les éléments de la classe Article (classe mère)...

    Ma question est: Ce fameux <T>, peut-il être paramètré en amont de le fonction Lister, c'est à dire lors de son appel dans une des classes filles ?

    Lors de l'appel de la fonction lister (dans une des classes fille), on aurait quelque chose comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    // Par exemple, si on est dans la classe fille "ArticleAcheté",
    // au niveau de l'appel, cela pourrait donner :
    Program.lister<T>() where T : ArticleAcheté new;
    Cependant, le bout de code ci-dessus, reste fictif car dans la pratique cet appel de la fonction Lister, ne marche pas...mais l'idée est la !

  15. #15
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    pour le createinstance avec un string en paramètre, je teste de suite...

    Merci pour l'idée !

  16. #16
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    Voila, près avoir esseyer le createinstance, ca doner quelque chose comme ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    string nomClasse = "ArticleAcheté";
    static public void liste(string nomClasse)
    {
          object toto =  Activator.CreateInstance(nomClasse);               
    }
    --> Ce procédé ne marche pas : l'objet toto est bien créer mais il ne dispose pas des méthodes et attributs de la classe fille paramétré dans le string nomClasse("ArticleAcheté")

    Il y a seulement 4 méthodes génériques, fournient par défaut par le compilateur : GetHashCode, ToString, GetType, Equals !

  17. #17
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 197
    Par défaut
    une fois instancié, l'intellisense ne propose que les choses en fonction de la déclaration du type, si tu veux avoir accès aux méthodes, il faut typer ta variable et pas la mettre as object
    si tes classes sont hérité de quelque chose pour qu'elles aient toutes les meme methodes, il faut type selon la classe de base

    et en vb on If TypeOf Machin Is Truc pour ensuite pourvoir faire d'autres choses, je crois que c'est avec gettype en c#
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  18. #18
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    A la base, c'est ce que j'avais fait dans le message 11
    Regarde ici :
    http://www.developpez.net/forums/d63...g/#post3744778

    Et dans chacun des 2 cas (avec un cast de mon type ou avec un object), cela pose les 2 même problème :
    - Soit il faut clairement désigner le nom de la classe fille, si je type (cast) ma variable
    - Soit mon objet est bien créer en tant que "object toto = ...", c'est à dire qu'il n'est pas typé, mais je ne n'accède pas aux methodes specifique de la classe fille !

    Y aurait-il un moyen pour caster (ou typer) ma variable sans forcement désigner le nom de la classe fille ("ArticleAcheté" dans l'exemple du message 11) ?

  19. #19
    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 beetlejuice722 Voir le message
    Ma question est: Ce fameux <T>, peut-il être paramètré en amont de le fonction Lister, c'est à dire lors de son appel dans une des classes filles ?
    Oui. Je t'invite à consulter un tutorial sur les generics. Ici tu aurais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // dans ArticleEpuisé
    Program.Lister<ArticleEpuisé>();
    ...
    // dans ArticleMachin
    Program.Lister<ArticleMachin>();
    Simple, n'est-ce pas ?

    Et d'ailleurs, le mot-clé where ne sert que dans la déclaration de la méthode, pas dans son utilisation.

  20. #20
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 36
    Par défaut
    Je sait qu'en Java, l'instruction Class.forname(string var) pourrait régler ce problème, mais je vois aucun équivalent en C# !!

Discussions similaires

  1. Réponses: 1
    Dernier message: 13/04/2015, 11h17
  2. [XL-2002] Macro de comparaison d'une cellule d'une feuille avec une cellule d'une autre feuille.
    Par steelydan dans le forum Macros et VBA Excel
    Réponses: 6
    Dernier message: 08/09/2010, 12h59
  3. Réponses: 4
    Dernier message: 15/10/2009, 13h33
  4. Réponses: 9
    Dernier message: 08/07/2009, 17h10
  5. Eval d'une propriété d'une classe dans une classe
    Par bizet dans le forum ASP.NET
    Réponses: 4
    Dernier message: 28/10/2008, 09h43

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