我正在研究这个例子:
食物分类:
public class Food { }
public class Meat : Food { }
public class Grass : Food { }
动物类:
public abstract class Animal<TFood>
where TFood : Food
{
public void Feed(TFood food) { }
}
public abstract class Carnivore : Animal<Meat> { }
public abstract class Herbivore : Animal<Grass> { }
public class Cow : Herbivore { }
农场分类:
public abstract class Farm<TAnimal, TFood>
where TAnimal : Animal<TFood>
where TFood : Food
{
public List<TAnimal> Animals;
public TFood FoodSupply;
public void FeedAnimals()
{
foreach ( var animal in Animals )
{
animal.Feed(FoodSupply);
}
}
}
public class DariyFarm : Farm<Cow, Grass> { }
我感到很烦的是,对于一个奶牛场,我必须定义食物的类型,因为食物的类型应该已经由母牛定义了。
我觉得我在这里想念什么。理想情况下,我希望能够仅根据其饲养的动物来定义一个农场,然后由该动物定义食物类型。不幸的是,您必须在未指定食物类型T的情况下将Animal约束应用于U。
我想念什么?
答案 0 :(得分:4)
我感到很烦的是,对于一个奶牛场,我必须定义食物的类型,因为食物的类型应该已经由母牛定义了。
那是您的逻辑错误; C#类型系统完全不知道“农场”,“母牛”和“食物”在您的脑海中有那些关系。您的想法是“母牛吃食物,因此农场应该通过食物自动进行参数设置”,但是农场也会生产食物;编译器如何知道您打算将“食物”与牛吃的食物逻辑地联系起来,而不是与农场生产的食物联系起来?
我想念什么?
您正在尝试将业务逻辑放入类型系统中。不要那样做如您所知,这会带来麻烦。
我写了一系列博客文章,介绍了许多错误的解决方法,而您本人对此也很了解。您可能会发现它很有趣:https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/
答案 1 :(得分:3)
不幸的是,这是通用类型系统在C#中的工作方式。
您有此问题,因为:
Animal
类具有Feed(T food)
方法。这是关键。Farm
类具有一个T
FoodSupply,以便在其中填充Animal
。Farm
类需要在Feed(T food)
上调用Animal
,但是如果不知道T
是什么,它就不能这样做。您可以使用界面解决此问题。假设您有一个IAnimal
界面:
public interface IAnimal
{
void Feed(Food food);
}
然后Animal<T>
可以实现它:
public abstract class Animal<TFood> : IAnimal where TFood : Food
{
public void Feed(TFood food)
{
// we either need to check for null here
}
public void Feed(Food food)
{
// or we need to check food is TFood here
Feed(food as TFood);
}
}
然后,您可以更改Farm
类以完全摆脱泛型:
public abstract class Farm<TAnimal> where TAnimal : IAnimal
{
public List<TAnimal> Animals;
public Food FoodSupply;
public void FeedAnimals()
{
foreach ( var animal in Animals )
{
animal.Feed(FoodSupply);
}
}
}
现在的问题是,您可以拥有一个DairyFarm
为FoodSupply
的{{1}}(例如)-只有您不能将Meat
馈送到{ {1}},因为他们只吃Meat
。
您需要同时具有两个类型参数以使用泛型-编译器无法从Cow
推断出Grass
的特定类型。