c # downcasting при привязке и интерфейсе - PullRequest
3 голосов
/ 29 сентября 2008

Есть ли лучший способ привязки списка базового класса к пользовательскому интерфейсу, кроме downcasting, например:

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

Ответы [ 6 ]

7 голосов
/ 29 сентября 2008

Почему бы не сделать так, чтобы Animal включал абстрактный метод, который Pig и Dog вынуждены применять

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

Таким образом, каждый конкретный класс берет на себя ответственность за свои действия, делая ваш код проще. Вам также не нужно будет использовать внутри цикла foreach.

6 голосов
/ 29 сентября 2008

Столкнувшись с проблемой такого типа, я следую схеме посетителя .

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

Таким образом, шаблон посетителя имитирует двойную диспетчеризацию в традиционном объектно-ориентированном языке с одной диспетчеризацией, таком как Java, Smalltalk, C # и C ++.

Единственное преимущество этого кода перед jop состоит в том, что интерфейс IVisitor может быть реализован в другом классе позже, когда вам нужно добавить новый тип посетителя (например, XmlSerializeVisitor *). 1011 * или FeedAnimalVisitor ).

4 голосов
/ 29 сентября 2008

Другой способ сделать это - выполнить проверку типов перед вызовом метода:

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

То, что вы ищете, это многократная отправка. НЕТ - C # не поддерживает множественную диспетчеризацию. Он поддерживает только одну отправку. C # может только динамически вызывать метод, основанный на типе получателя (то есть объект с левой стороны от. В вызове метода)

Этот код использует двойную отправку . Я позволю коду говорить за себя:

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

ПРИМЕЧАНИЕ. Ваш пример кода намного проще, чем этот. Я думаю о двойной диспетчеризации как об одной из тяжелых артиллерийских программ в программировании на C # - я использую ее только в качестве крайней меры. Но если вам нужно сделать много типов объектов и много разных типов привязок (например, вам нужно привязать их к странице HTML, но вам также нужно привязать их к WinForms, отчету или CSV) Я в конечном итоге реорганизовал бы мой код для использования двойной отправки.

0 голосов
/ 29 сентября 2008

Я думаю, вам нужен класс представления, связанный с фабрикой.

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

Тогда ваши DogView и PigView унаследуют AnimalView, который выглядит примерно так:

class AnimalView {
  abstract void DoStuff();
}

В итоге вы сделаете что-то вроде:

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

Полагаю, вы могли бы также сказать, что это реализация шаблона стратегии.

0 голосов
/ 29 сентября 2008

Если у вас нет более конкретного примера, просто переопределите ToString ().

0 голосов
/ 29 сентября 2008

Вы не в полной мере используете свой базовый класс. Если у вас есть виртуальная функция в классе Animal, которую переопределяет Dog & Pig, вам не нужно ничего разыгрывать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...