我作为开发人员工作了几年,但似乎我仍然不了解有关泛型的一些高级知识。
我准备了一些 self 解释的代码:
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() 方法的泛型类型,因此显然强制转换返回 null。问题是如何解决这个问题?我是否遗漏了一些模式?
最佳答案
你是对的,你错过了一些关于 co/contravariant 所以定义接近:(source)
Covariance
Enables you to use a more derived type than originally specified.
You can assign an instance of IEnumerable< Derived> to a variable of type IEnumerable< Base>.
Contravariance
Enables you to use a more generic (less derived) type than originally specified.
You can assign an instance of Action< Base> to a variable of type Action< Derived>.
您的动物管理员定义为 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>
类型的不太通用(或更具体)的变量.
关于c# - 通用工厂问题、运行时转换和协变/逆变问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58415949/