Pregunta

¿Existe una mejor manera de vincular una lista de clases base a una UI que no sea la conversión descendente, por ejemplo:

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;
    }
}
¿Fue útil?

Solución

Cuando me enfrento a este tipo de problema, sigo el patrón de visitantes .

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);
    }  
  }
}

Por lo tanto, el patrón de visitante simula el envío doble en un lenguaje convencional orientado a objetos de envío único, como Java, Smalltalk, C # y C ++.

La única ventaja de este código sobre jop ' s es que la interfaz de IVisitor se puede implementar en una clase diferente más adelante cuando necesite agregar un nuevo tipo de visitante (como un XmlSerializeVisitor o un FeedAnimalVisitor ).

Otros consejos

¿Por qué no hacer que Animal incluya un método abstracto que Pig y Dog están obligados a implementar?

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 esta manera, cada clase específica asume la responsabilidad de sus acciones, haciendo que su código sea más simple. Tampoco necesitarás lanzar dentro de tu bucle foreach.

Otra forma de hacer esto es realizar una comprobación de tipo antes de llamar al método:

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

Lo que estás buscando es un envío múltiple. NO - C # no admite envíos múltiples. Sólo es compatible con un solo envío. C # solo puede invocar dinámicamente un método basado en el tipo de receptor (es decir, el objeto en el lado izquierdo de la llamada del método)

Este código utiliza doble envío . Dejaré que el código hable por sí mismo:

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);
    }
}

NOTA: Su código de ejemplo es mucho más simple que esto. Pienso en el doble despacho como una de las artillerías pesadas en la programación de C #, solo lo tomo como último recurso. Pero si hay muchos tipos de objetos y muchos tipos diferentes de enlaces que debe hacer (por ejemplo, debe enlazarlo a una página HTML pero también debe vincularlo a un WinForms o un informe o un CSV) , Eventualmente refactorizaría mi código para usar doble despacho.

No estás aprovechando al máximo tu clase base. Si tenía una función virtual en su clase de animales, Dog & amp; Anulación del cerdo, no necesitarías lanzar nada.

A menos que tenga un ejemplo más específico, simplemente invalide ToString ().

Creo que quieres una clase de vista asociada con una fábrica.

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));

Entonces tu DogView y PigView heredarán AnimalView que se parece a algo como:

class AnimalView {
  abstract void DoStuff();
}

Terminarás haciendo algo como:

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

Supongo que también podría decir que esta es una implementación del patrón de estrategia.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top