Pergunta

Existe uma melhor maneira de ligar uma lista de classe base para uma interface de usuário diferente downcasting por exemplo:

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

Solução

Quando confrontados com este tipo de problema, eu sigo a visitante padrão .

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

Assim, o padrão do visitante simula dupla expedição em uma linguagem orientada a objeto de despacho único convencional, tais como Java, Smalltalk, C # e C ++.

A única vantagem deste código sobre jop ' s é que a interface IVisitor pode ser implementado em uma classe diferente mais tarde, quando você precisa adicionar um novo tipo de visitante (como um XmlSerializeVisitor ou FeedAnimalVisitor ).

Outras dicas

Por que não fazer animal incluem um método abstrato que porcos e cães são forçados 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
    }
}

Desta forma, cada classe específica assume a responsabilidade por suas ações, tornando o código mais simples. Você também não precisará elenco dentro de seu loop foreach.

Outra maneira de fazer isso é realizar uma typecheck antes de chamar o método:

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

O que você está procurando é múltiplo-expedição. NO - C # não suporta múltiplo expedição. Ele suporta apenas um único envio. C # só pode dinamicamente invocação de um método com base no tipo do receptor (isto é, o objecto no lado da mão esquerda da. Na chamada do método)

Esse código usa duplo expedição . Vou deixar a falar código por si:

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: O código de exemplo é muito mais simples do que isso. Eu acho que a dupla de despacho como um dos pesados ??artilharias em programação C # - Eu só tirá-lo como um último recurso. Mas se há um grande número de tipos de objetos e tipos muito diferentes de ligações que você precisa fazer (por exemplo, você precisa vinculá-lo a uma página HTML, mas você também precisa vinculá-lo a um WinForms ou um relatório ou um CSV) , eu acabaria por refatorar meu código para usar double-expedição.

Você não está tirando o máximo partido da sua classe base. Se você tivesse uma função virtual em sua classe animal que Dog & Pig substituição, você não precisaria elenco nada.

A menos que você tem um exemplo mais específico, basta substituir ToString ().

Eu acho que você quer uma classe de exibição associado com uma 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));

Em seguida, seu DogView e PigView herdará AnimalView que é algo como:

class AnimalView {
  abstract void DoStuff();
}

Você vai acabar fazendo algo como:

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

Eu acho que você poderia dizer também que esta é uma implementação do padrão de estratégia.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top