Пониженный экземпляр, использующий его тип - PullRequest
2 голосов
/ 02 февраля 2012

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

public abstract class Animal {
}
public class Dog : Animal {
}
public class Cat : Animal {
}
public class AnimalHandler {
    public virtual void Pet<T>(T animal)
    {
    }
}

Учитывая коллекцию животных.

public List<Animal> Animals { get; set; }

Как убить отдельных животных перед вызовом метода Pet ?

Я сейчас работаю в этой форме.

if (animal is Dog) {
    _animalHandler.Pet((Dog)animal);
}
if (animal is Cat) {
    _animalHandler.Pet((Cat)animal);
}

В идеале это было бы что-то вроде этой формы.

var type = animal.GetType();
_animalHandler.Pet(animal.CastTo(type));

Разъяснение: Мне нужно, чтобы экземпляр был пониженного типа, прежде чем он будет передан методу. Мне нужна собака или кошка специально для передачи.

Ответы [ 3 ]

2 голосов
/ 02 февраля 2012

Это можно сделать с помощью нового ключевого слова .NET 4 dynamic:

namespace Rextester
{
    public class Program
    {
    public abstract class Animal {
    }
    public class Dog : Animal {
    }
    public class Cat : Animal {
    }
    public class AnimalHandler {
        public virtual void Pet<T>(T animal) {
            Console.WriteLine(typeof(T));
        }
    }

    public class Problem {
        public List<Animal> Animals { get; set; }
        private readonly AnimalHandler _animalHandler;

        public Problem(AnimalHandler animalHandler) {
            _animalHandler = animalHandler;
            Animals = new List<Animal> { new Cat(), new Dog() };
        }

        public void MakeDecision() {
            foreach (var animal in Animals) {
                 _animalHandler.Pet(animal);
            }
        }
    }

        public static void Main(string[] args)
        {
            List<Animal> animals = new List<Animal>();
            animals.Add(new Dog());
            animals.Add(new Cat());

            var handler = new AnimalHandler();
            handler.Pet((dynamic)animals[0]);
            handler.Pet((dynamic)animals[1]);
        }
    }
}

Обратите внимание на приведение к dynamic в вызовах на Pet.Хотя, делая это таким образом, мы исключаем необходимость того, чтобы Pet было в общем родовым.

1 голос
/ 02 февраля 2012

Если, как говорится в вашем разъяснении, вам «нужен конкретный Dog или Cat для передачи», то ваш метод не может принять общий Animal.

Однако, что я вас подозреваюпропало то, что Handler не нужно знать, какое у него животное, только как обращаться с этим животным.См. Этот расширенный пример:

public abstract class Animal {
    public abstract void Speak();
}
public class Dog : Animal {
    public override void Speak(){ Console.WriteLine("WOOF!! You're great."); }
}
public class Cat : Animal {
    public override void Speak(){ Console.WriteLine("Meow. Im better than you."); }
}
public class AnimalHandler {
    public virtual void Pet<T>(T animal) where T : Animal
    {
        animal.Speak();
    }
}

При выполнении этого с помощью следующего кода:

List<Animal> animals = new List<Animal>();
animals.Add(new Dog());
animals.Add(new Cat());

var handler = new AnimalHandler();
handler.Pet(animals[0]);
handler.Pet(animals[1]);

Я уверен, что вы можете представить себе вывод (если нет, пример в реальном времени: http://rextester.com/UJZRB90548). Как вы видите, у меня есть только список Base Animal, но когда он передается в обработчик, вызывается правильный метод.

0 голосов
/ 02 февраля 2012

У вас есть несколько альтернатив.Один из подходов состоит в том, чтобы иметь подклассы AnimalHandler для каждого типа животных и вручную отсылать к правильному:

public class AnimalHandler
{
    public void Pet(Animal animal)
    {
        if (animal is Cat)
        {
            this.catHandler.Pet((Cat) animal);
        }
        else if (animal is Dog)
        {
            this.dogHandler.Pet((Dog) animal);
        }
        else
        {
            throw new ArgumentOutOfRangeException("animal");
        }
    }

    private readonly CatHandler catHandler;
    private readonly DogHandler dogHandgler;
}

В качестве альтернативы всем «если» и «есть», вы можете добавить AnimalType Перечислите свойство класса Animal и используйте оператор switch.

Приведенный выше подход можно сделать несколько более общим и расширяемым, если создать интерфейс для представления обработчика определенного типа животного:

public enum AnimalType
{
    Cat,
    Dog,
}

public interface ISpecificAnimalHandler
{
    AnimalType AnimalType { get; }
    void Pet(Animal animal);
}

public class AnimalHandler
{
     public AnimalHandler()
     {
         var handlers = new ISpecificAnimalHandler[] { new CatHandler(), new DogHandler() };
         this.handlersByAnimal = handlers.ToDictionary(handler => handler.AnimalType, handler);
         // ...
     }

     public void Pet(Animal animal)
     {
         ISpecificAnimalHandler handler;
         if (!this.handlersByAnimalType.TryGet(animal.AnimalType, out handler))
         {
              throw new ArgumentOutOfRangeException("Animal of type " + animal.GetType().Name " not supported.");
         }

         handler.Pet(animal);
     }

     private IDictionary<AnimalType, ISpecificAnimalHandler> handlerByAnimalType;
}

public class CatHandler : ISpecificAnimalHandler
{
    public AnimalType AnimalType
    {
        get { return AnimalType.Cat;
    }

    public void Pet(Animal animal)
    {
        var cat = (Cat) animal;
        // do petting, meow
    }
}

Другой, другой, вариант - использовать динамическую отправку:

public class AnimalHandler
{
    public void Pet(Animal animal)
    {
        PetAnimal((dynamic) animal); // will dispatch to most appropriate method at runtime
    }

    public void PetAnimal(Cat cat)
    {
        // do petting, meow
    }

    public void PetAnimal(Dog dog)
    {
        // do petting, woof
    }
}
...