Question

Existe-t-il un meilleur moyen de lier une liste de classe de base à une interface utilisateur autre que le downcasting, par exemple:

static void Main(string[] args) {
    List<Animal> list = new List<Animal>();  
    Pig p = new Pig(5);  
    Dog d = new Dog("/images/dog1.jpg");  
    list.Add(p);  
    list.Add(d);  
    foreach (Animal a in list)   
    {  
        DoPigStuff(a as Pig);  
        DoDogStuff(a as Dog);  
    }  

}  


static void DoPigStuff(Pig p)
{
    if (p != null) 
    {  
        label1.Text = String.Format("The pigs tail is {0}", p.TailLength);
    }  
}

static void DoDogStuff(Dog d) {
    if (d != null) 
    {
        Image1.src = d.Image;
    }
}

class Animal {
    public String Name { get; set; }
}

class Pig : Animal{
    public int TailLength { get; set; }

    public Pig(int tailLength) 
    {
        Name = "Mr Pig";
        TailLength = tailLength;
    }
}

class Dog : Animal {
    public String Image { get; set; }

    public Dog(String image) 
    {
        Name = "Mr Dog";
        Image = image;
    }
}
Était-ce utile?

La solution

Face à ce type de problème, je suis le modèle de visiteurs .

interface IVisitor
{
  void DoPigStuff(Piggy p);
  void DoDogStuff(Doggy d);
}

class GuiVisitor : IVisitor
{
  void DoPigStuff(Piggy p)
  {
    label1.Text = String.Format("The pigs tail is {0}", p.TailLength);
  }

  void DoDogStuff(Doggy d)
  {
    Image1.src = d.Image;
  }
}

abstract class Animal
{
    public String Name { get; set; }
    public abstract void Visit(IVisitor visitor);
}

class Piggy : Animal
{
    public int TailLength { get; set; }

    public Piggy(int tailLength) 
    {
        Name = "Mr Pig";
        TailLength = tailLength;
    }

    public void Visit(IVisitor visitor)
    {
       visitor.DoPigStuff(this);
    }
}

class Doggy : Animal 
{
   public String Image { get; set; }

   public Doggy(String image) 
   {
     Name = "Mr Dog";
     Image = image;
   }

   public void Visit(IVisitor visitor)
   {
     visitor.DoDogStuff(this);
   }
}

public class AnimalProgram
{
  static void Main(string[] args) {
    List<Animal> list = new List<Animal>();  
    Pig p = new Pig(5);  
    Dog d = new Dog("/images/dog1.jpg");  
    list.Add(p);  
    list.Add(d);

    IVisitor visitor = new GuiVisitor();  
    foreach (Animal a in list)   
    {
      a.Visit(visitor);
    }  
  }
}

Ainsi, le modèle de visiteur simule la double répartition dans un langage orienté objet à répartition unique, tel que Java, Smalltalk, C # et C ++.

Le seul avantage de ce code par rapport à jop ' s est que l’interface IVisitor peut être implémentée ultérieurement sur une classe différente lorsque vous devez ajouter un nouveau type de visiteur (comme un XmlSerializeVisitor ou un FeedAnimalVisitor ).

Autres conseils

Pourquoi ne pas faire inclure à Animal une méthode abstraite que Pig et Dog sont obligés de mettre en oeuvre

public class Animal
{
    public abstract void DoStuff();
}

public Dog : Animal
{
    public override void DoStuff()
    {
        // Do dog specific stuff here
    }
}

public Pig : Animal
{
    public override void DoStuff()
    {
        // Do pig specific stuff here
    }
}

De cette façon, chaque classe spécifique assume la responsabilité de ses actions, ce qui simplifie votre code. Vous n'aurez également pas besoin de vous lancer dans votre boucle foreach.

Une autre méthode consiste à effectuer une vérification de type avant d'appeler la méthode:

if (animal is Pig) DoPigStuff();
if (animal is Dog) DoDogStuff();

Ce que vous recherchez, c'est l'envoi multiple. NON - C # ne prend pas en charge l'envoi multiple. Il ne prend en charge que l'envoi unique. C # ne peut invoquer dynamiquement qu’une méthode en fonction du type du destinataire (c’est-à-dire l’objet situé à gauche du. Dans l’appel de la méthode)

Ce code utilise la double envoi . Je laisserai le code parler pour lui-même:

class DoubleDispatchSample
{
    static void Main(string[]args)
    {
        List<Animal> list = new List<Animal>();
        Pig p = new Pig(5);
        Dog d = new Dog(@"/images/dog1.jpg");
        list.Add(p);
        list.Add(d);

        Binder binder = new Binder(); // the class that knows how databinding works

        foreach (Animal a in list)
        {
            a.BindoTo(binder); // initiate the binding
        }
    }
}

class Binder
{
    public void DoPigStuff(Pig p)
    {
        label1.Text = String.Format("The pigs tail is {0}", p.TailLength);
    }

    public void DoDogStuff(Dog d)
    {
        Image1.src = d.Image;
    }
}

internal abstract class Animal
{
    public String Name
    {
        get;
        set;
    }

    protected abstract void BindTo(Binder binder);
}

internal class Pig : Animal
{
    public int TailLength
    {
        get;
        set;
    }

    public Pig(int tailLength)
    {
        Name = "Mr Pig";
        TailLength = tailLength;
    }

    protected override void BindTo(Binder binder)
    {
        // Pig knows that it's a pig - so call the appropriate method.
        binder.DoPigStuff(this);
    }
}

internal class Dog : Animal
{
    public String Image
    {
        get;
        set;
    }

    public Dog(String image)
    {
        Name = "Mr Dog";
        Image = image;
    }

    protected override void BindTo(Binder binder)
    {
        // Pig knows that it's a pig - so call the appropriate method.
        binder.DoDogStuff(this);
    }
}

REMARQUE: votre exemple de code est beaucoup plus simple que cela. Je pense à la double dépêche comme à une des artilleries lourdes de la programmation C # - je ne la résume qu'en dernier recours. Mais s'il y a beaucoup de types d'objets et de types de liaisons différents que vous devez faire (par exemple, vous devez les associer à une page HTML mais vous devez également les associer à un fichier WinForms, à un rapport ou à un fichier CSV). , Je voudrais éventuellement refactoriser mon code pour utiliser la double dépêche.

Vous ne tirez pas pleinement parti de votre classe de base. Si vous aviez une fonction virtuelle dans votre classe Animal, Dog & amp; Remplacement du cochon, vous n’auriez pas besoin de lancer quoi que ce soit.

Sauf exemple plus spécifique, remplacez simplement ToString ().

Je pense que vous voulez une classe de vues associée à une fabrique.

Dictionary<Func<Animal, bool>, Func<Animal, AnimalView>> factories;
factories.Add(item => item is Dog, item => new DogView(item as Dog));
factories.Add(item => item is Pig, item => new PigView(item as Pig));

Ensuite, vos DogView et PigView hériteront d’AnimalView qui ressemble à quelque chose comme:

class AnimalView {
  abstract void DoStuff();
}

Vous finirez par faire quelque chose comme:

foreach (animal in list)
  foreach (entry in factories)
    if (entry.Key(animal)) entry.Value(animal).DoStuff();

Je suppose que vous pouvez également dire qu'il s'agit d'une mise en œuvre du modèle de stratégie.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top