Общая проблема фабрики, приведение во время выполнения и совместная / противоречивая проблема - PullRequest
2 голосов
/ 16 октября 2019

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

Я подготовил фрагмент кода, который не требует пояснений:

public enum AnimalType { Cat, Dog}
public interface IAnimal { }
public class Dog : IAnimal { }
public class Cat : IAnimal { }

public interface IAnimalHandler<in TAnimal> where TAnimal : IAnimal { void Handle(TAnimal animal); }
public class CatHandler : IAnimalHandler<Cat>
{
    public void Handle(Cat animal) { }
}
public class DogHandler : IAnimalHandler<Dog>
{
    public void Handle(Dog animal) { }
}

public class AnimalFactory
{
    public IAnimal GetAnimal(AnimalType type) { return type == AnimalType.Cat ? (IAnimal) new Cat() : (IAnimal)new Dog();}
}
public class AnimalHandlerFactory
{
    public IAnimalHandler<TAnimal> GetAnimalHandler<TAnimal>(TAnimal animal) where TAnimal : IAnimal
    {
        switch (animal)
        {
            case Cat cat:
                return new CatHandler() as IAnimalHandler<TAnimal>;
            case Dog dog:
                return new CatHandler() as IAnimalHandler<TAnimal>;
            default:
                throw new Exception();
        }
    }
}

public class FactoryTests
{
    [Fact]
    public async Task factory_returns_concrete()
    {
        var myAnimal = new AnimalFactory().GetAnimal(AnimalType.Dog);

        var handlerForMyAnimal = new AnimalHandlerFactory().GetAnimalHandler(myAnimal);

        Assert.NotNull(handlerForMyAnimal);
    }
}

А теперь, не могли бы вы ответить на мои сомнения?

1) Почему я должен выполнить приведение в методе GetAnimalHandler () ?

2) Тест не пройденпотому что AnimalFactory возвращает объект абстракции (объявление), и этот тип используется в качестве универсального типа для метода GetAnimalHandler () , поэтому очевидно, что приведение возвращает нуль. Вопрос в том, как решить эту проблему? Я что-то пропустил?

1 Ответ

1 голос
/ 16 октября 2019

Вы правы в том, что упускаете что-то в отношении ко / контравариантности, поэтому определения должны быть рядом: ( источник )

Ковариация

Позволяет вам использовать более производный тип, чем изначально указан.

Вы можете назначить экземпляр IEnumerable переменной типа IEnumerable .

Contravariance

Позволяет вам использовать более общий (менее производный) тип, чем первоначально указанный.

Вы можете назначить экземпляр Action переменной типа Action .

Ваш обработчик животных определен с in, и это делает его контравариантным. Таким образом, с вашим определением допустимо

IAnimalHandler<PersianCat> handler = new CatHandler();

с учетом

public class PersianCat : Cat { }

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

Случай, который вы пытаетесь форсировать, не является законным:

IAnimalHandler<IAnimal> handler = new CatHandler();

с использованием моего персидского кота (и выделенного обработчика) это будет выглядеть так:

IAnimalHandler<Cat> handler = new PersianCatHandler();

Итак, у нас есть специальный обработчик, который может обрабатывать только персидских кошек, поэтому мы не можем дать ему просто случайного кота, он бы не знал, что с ним делать.

Так что в судебном деле и определении контравариантности выше more genericТип находится справа от присвоения CatHandler (или IAnimalHandler<Cat>) и присваивается менее общей (или более конкретной) переменной типа IAnimalHandler<PersianCat>.

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