2016-03-20 6 views
0

Предположим, я пытаюсь смоделировать, что животные делают и как выглядят животные, и я хочу, чтобы код для каждого животного был отдельным, а код действий/выглядит отдельно. Как я могу моделировать это в C#?Разделение проблем и полиморфный дизайн

Это было бы идеально:

using System.Collections.Generic; 

class PaintingAnimals { 
    class Animal {} 
    class Cat : Animal {} 
    class Dog : Animal {} 

    static void Paint(Cat cat) { System.Console.WriteLine("Cat"); } 
    static void Paint(Dog dog) { System.Console.WriteLine("Dog"); } 
    static void Paint(Animal animal) { System.Console.WriteLine("Animal"); } 

    public static void Main() { 
    var animals = new List<Animal> {new Cat(), new Dog()}; 

    foreach (var animal in animals) { 
     Paint(animal); // Always prints "Animal" not "Cat" or "Dog" 
    } 
    } 
} 

Однако, если я правильно понимаю, этот код будет компилироваться только если Paint определяется как Paint(Animal), а не как Paint(Cat) или Paint(Dog), и поэтому я не могу разделить мой код каждое животное в разные функции, так что собаки и кошки будут окрашиваться по-разному.

+2

Существует множество способов сделать это ... Как вариант проверить [шаблон посетителя] (https://en.wikipedia.org/wiki/Visitor_pattern) - возможно, реализованный с помощью Piant ((динамического) животного) 'и полный набор' Paint (Cat) '... переопределяет. –

+0

не понимают, что вы имеете в виду «тот же код для обоих объектов», можете ли вы показать свою функцию Paint? –

+0

@AlexeiLevenkov Шаблон посетителя! Полезный указатель, спасибо. – user12341234

ответ

0

То, что вы хотите сделать, это не вызов метода с животным в качестве параметра, а вызов метода на животных:

static void Main() { 
    var animals = new List<Animal> {new Cat(), new Dog()}; 

    foreach (var animal in animals) { 
    animal.Paint(); 
    } 
} 

Каждый конкретный класс Animal будет реализовывать метод Paint, как он считает нужным.

+1

К сожалению, это решение заставляет меня держать мой код «Paint» в том же месте, что и мой другой код ... Все в стороне от Cat/Класс собаки. – user12341234

+0

'Paint' действительно принадлежит к каждому конкретному классу. В противном случае вам понадобится растущий оператор 'if' или' select' в «глобальном» методе Paint, чтобы обслуживать каждое другое возможное Animal. –

+0

Растущее утверждение if является одним из решений, хотя оно сильно связывает модули. В других языках динамическая диспетчеризация или параметрический полиморфизм - это альтернативы, которые могут работать. Мне было интересно, есть ли другой способ в C#, которому не нужен гигантский переключатель. – user12341234

0

После исследования @ предложение AlexeiLevenkov, я нашел следующий код, чтобы сделать то, что я хотел:

using System.Collections.Generic; 

class PaintingAnimals { 
    class Animal {} 
    class Cat : Animal {} 
    class Dog : Animal {} 

    static void Paint(Cat cat) { System.Console.WriteLine("Cat"); } 
    static void Paint(Dog dog) { System.Console.WriteLine("Dog"); } 
    static void Paint(Animal animal) { System.Console.WriteLine("Animal"); } 

    public static void Main() { 
    var animals = new List<Animal> {new Cat(), new Dog()}; 

    foreach (dynamic animal in animals) { // Uses "dynamic", not "var" 
     Paint(animal); // Print's "Cat" and "Dog" respectively, not "Animal" 
    } 
    } 
} 

Ключевым моментом здесь является использование dynamic в цикле Еогеасп, который изменяет тип каждого элемента от Animal до Cat и Dog соответственно.