C# 从抽象 类 访问 属性
C# acces property from abstract classes
我有这样的 class 结构:
BaseAnimal.cs:
public abstract class BaseAnimal
{
public string? Species { get; set; }
public double Price { get; set; }
}
然后我有这两个 classes:
public abstract class Carnivore : BaseAnimal
{
public double MeatFood { get; set; }
}
public abstract class Herbivore : BaseAnimal
{
public double GreenFood { get; set; }
}
然后我有子 classes:
public class Ape : Herbivore
{
public Ape()
{
Species = "Ape";
GreenFood = 10.0;
Price = 10000.0;
}
}
然后我有一个工厂已经在使用这条线来获取动物的所有属性:
public BaseAnimal[] Animals = prototypes.Values.ToArray();
在我的 Main class 中,我想阅读 Animals 的属性:
private void cboAnimals_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Debug.WriteLine(animalFactory.Animals[0].);
}
此代码的问题是,我无法访问 Herbivore.cs 和 Carnivore.cs
中指定的属性
Animals
是BaseAnimal
的数组。此 class 没有 MeatFood
或 GreenFood
属性。这就是您无法访问这些属性的原因。如果您可以访问它们,则可以使用此代码:
BaseAnimal animal = new Ape();
Console.WriteLine(animal.MeatFood);
但是Ape
没有MeatFood
属性。如果您想访问这些属性,您已经知道您的 Animals[0]
是 Carnivore
还是 Herbivore
:
private void cboAnimals_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(animalFactory.Animals[0] is Carnivore carnivore)
{
Debug.WriteLine(carnivore.MeatFood);
}
else if (animalFactory.Animals[0] is Herbivore herbivore)
{
Debug.WriteLine(herbivore.GreenFood);
}
}
但是此时您可能意识到您不会通过此方法获得好的代码。这表明您的 class 结构不是最好的。事实上,为什么你有两个属性 MeatFood
和 GreenFood
?它们之间有什么区别?两者都是双重类型。因此,也许你应该在你的 BaseAnimal
class 中有一个 Food
属性 并通过枚举来区分食物的类型:
public enum FoodType
{
Meat,
Green
}
public abstract class BaseAnimal
{
public string? Species { get; set; }
public double Price { get; set; }
public double Food { get; set;}
public abstract FoodType FoodType {get;}
}
public abstract class Carnivore : BaseAnimal
{
public override FoodType FoodType{ get { return FoodType.Meat; } }
}
public abstract class Herbivore : BaseAnimal
{
public override FoodType FoodType{ get { return FoodType.Green; } }
}
最简单的可能是检查类型:
var foodAmount = animalFactory.Animals[0] switch {
Carnivore c => c.MeatFood ,
Herbivore h => h.GreenFood ,
_ => throw new InvalidOperationException()
};
但这是编程入门中讲授的继承的典型例子。作为一般规则,如果您需要检查类型,您可能没有充分考虑您的类型系统。继承的思想是所有具体实现都应该委托给实现,所以对象的用户不需要知道具体类型。
我很难提供具体的建议,因为这个例子太人为化了,但我建议阅读 Eric Lippert 的系列文章 Wizards and Warriors 以很好地介绍继承,以及人们经常做错的地方。
问题是动物数组是 BaseAnimal 类型。你需要将它拆箱(即将它转换为它的子类型),你还需要一些逻辑来检查类型,所以像
if (obj is Herbivore)
{
var herb = (Herbivore)obj;
herb.Greenfood= 123m;
}
看起来很乱,但在某些时候你需要得到具体的类型。
你应该做的是使用 Composition。
public interface IFood {
public double Food { get; set; }
}
public class Herbivore {
public double Food { get; set; }
}
public class Carnivore {
public double Food { get; set; }
}
public interface IAnimal {
string? Species { get; set; }
double Price { get; set; }
}
public class Animal {
string? Species { get; set; }
double Price { get; set; }
}
public class Ape {
private IAnimal _animal;
private IFood _food;
public Ape(IAnimal animal, IFood food) {
_animal = animal;
_food = food;
}
}
public void main(string[] args) {
IFood herbivore = new Herbivore(){Food = 10};
IAnimal ape = new Animal(){
Price = 1000;
Species = "Ape";
};
Ape ape = new Ape(ape, herbivore);
}
Now you can compose any class of type animal you want, you can reuse the implementation of IAnimal and IFood on other types, make new ones? Like Omnivore?
您可以实现不同的物种和/或“价格”并将其加载到任何新的 class 中,例如您想要的“狗”或“猫”,并使用您的接口组合该对象。因此不需要重写你的测试,你不需要有多个实现 say ... “Speak” on dogs that all do a "bark" you can have an class that does "bark" and then把它编成所有像狗一样的动物。
想想现在实现像“消费”这样适用于所有食草动物、杂食动物或食肉动物的行为是多么容易?
您只需编写 3 个单元测试,每个单元测试一个,而不是您最终创建的每种动物类型一个,因为它们存在依赖性分离。
并且 Consume 可以处理食物类型的任何参数,因此您甚至不必关心是否找到了定义食物的新方法,例如 rock-eater 或其他东西:)
或者如果您必须将植物描述为“消耗”的东西并且您需要阳光作为食物来源?超级简单,您也不需要为此编写 x-amount 工厂类型单元测试。
组合优于继承是迄今为止在 OOP 中实现代码的更好选择。
我有这样的 class 结构:
BaseAnimal.cs:
public abstract class BaseAnimal
{
public string? Species { get; set; }
public double Price { get; set; }
}
然后我有这两个 classes:
public abstract class Carnivore : BaseAnimal
{
public double MeatFood { get; set; }
}
public abstract class Herbivore : BaseAnimal
{
public double GreenFood { get; set; }
}
然后我有子 classes:
public class Ape : Herbivore
{
public Ape()
{
Species = "Ape";
GreenFood = 10.0;
Price = 10000.0;
}
}
然后我有一个工厂已经在使用这条线来获取动物的所有属性:
public BaseAnimal[] Animals = prototypes.Values.ToArray();
在我的 Main class 中,我想阅读 Animals 的属性:
private void cboAnimals_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Debug.WriteLine(animalFactory.Animals[0].);
}
此代码的问题是,我无法访问 Herbivore.cs 和 Carnivore.cs
中指定的属性Animals
是BaseAnimal
的数组。此 class 没有 MeatFood
或 GreenFood
属性。这就是您无法访问这些属性的原因。如果您可以访问它们,则可以使用此代码:
BaseAnimal animal = new Ape();
Console.WriteLine(animal.MeatFood);
但是Ape
没有MeatFood
属性。如果您想访问这些属性,您已经知道您的 Animals[0]
是 Carnivore
还是 Herbivore
:
private void cboAnimals_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(animalFactory.Animals[0] is Carnivore carnivore)
{
Debug.WriteLine(carnivore.MeatFood);
}
else if (animalFactory.Animals[0] is Herbivore herbivore)
{
Debug.WriteLine(herbivore.GreenFood);
}
}
但是此时您可能意识到您不会通过此方法获得好的代码。这表明您的 class 结构不是最好的。事实上,为什么你有两个属性 MeatFood
和 GreenFood
?它们之间有什么区别?两者都是双重类型。因此,也许你应该在你的 BaseAnimal
class 中有一个 Food
属性 并通过枚举来区分食物的类型:
public enum FoodType
{
Meat,
Green
}
public abstract class BaseAnimal
{
public string? Species { get; set; }
public double Price { get; set; }
public double Food { get; set;}
public abstract FoodType FoodType {get;}
}
public abstract class Carnivore : BaseAnimal
{
public override FoodType FoodType{ get { return FoodType.Meat; } }
}
public abstract class Herbivore : BaseAnimal
{
public override FoodType FoodType{ get { return FoodType.Green; } }
}
最简单的可能是检查类型:
var foodAmount = animalFactory.Animals[0] switch {
Carnivore c => c.MeatFood ,
Herbivore h => h.GreenFood ,
_ => throw new InvalidOperationException()
};
但这是编程入门中讲授的继承的典型例子。作为一般规则,如果您需要检查类型,您可能没有充分考虑您的类型系统。继承的思想是所有具体实现都应该委托给实现,所以对象的用户不需要知道具体类型。
我很难提供具体的建议,因为这个例子太人为化了,但我建议阅读 Eric Lippert 的系列文章 Wizards and Warriors 以很好地介绍继承,以及人们经常做错的地方。
问题是动物数组是 BaseAnimal 类型。你需要将它拆箱(即将它转换为它的子类型),你还需要一些逻辑来检查类型,所以像
if (obj is Herbivore)
{
var herb = (Herbivore)obj;
herb.Greenfood= 123m;
}
看起来很乱,但在某些时候你需要得到具体的类型。
你应该做的是使用 Composition。
public interface IFood {
public double Food { get; set; }
}
public class Herbivore {
public double Food { get; set; }
}
public class Carnivore {
public double Food { get; set; }
}
public interface IAnimal {
string? Species { get; set; }
double Price { get; set; }
}
public class Animal {
string? Species { get; set; }
double Price { get; set; }
}
public class Ape {
private IAnimal _animal;
private IFood _food;
public Ape(IAnimal animal, IFood food) {
_animal = animal;
_food = food;
}
}
public void main(string[] args) {
IFood herbivore = new Herbivore(){Food = 10};
IAnimal ape = new Animal(){
Price = 1000;
Species = "Ape";
};
Ape ape = new Ape(ape, herbivore);
}
Now you can compose any class of type animal you want, you can reuse the implementation of IAnimal and IFood on other types, make new ones? Like Omnivore?
您可以实现不同的物种和/或“价格”并将其加载到任何新的 class 中,例如您想要的“狗”或“猫”,并使用您的接口组合该对象。因此不需要重写你的测试,你不需要有多个实现 say ... “Speak” on dogs that all do a "bark" you can have an class that does "bark" and then把它编成所有像狗一样的动物。
想想现在实现像“消费”这样适用于所有食草动物、杂食动物或食肉动物的行为是多么容易?
您只需编写 3 个单元测试,每个单元测试一个,而不是您最终创建的每种动物类型一个,因为它们存在依赖性分离。
并且 Consume 可以处理食物类型的任何参数,因此您甚至不必关心是否找到了定义食物的新方法,例如 rock-eater 或其他东西:)
或者如果您必须将植物描述为“消耗”的东西并且您需要阳光作为食物来源?超级简单,您也不需要为此编写 x-amount 工厂类型单元测试。
组合优于继承是迄今为止在 OOP 中实现代码的更好选择。