有一个任务是创建一个注册动物的程序,对象是熟悉继承,多态等等。
让我感到困惑的一件事是,无论我读到多少,这似乎毫无意义。
我创建了我的主要类动物,它是一些适用于所有动物的通用字段,可以说出名称,年龄和种类。
到目前为止,所有动物都有这么好的信息,但每只动物都有一个独特的领域,所以生病了我的猫作为公共阶级的猫:动物,并给猫的田间牙齿。例如。
现在我想创建一个新的动物猫,我从几个列表框中获取数据,所以我需要一个带有这些参数的构造函数,这是我没有得到的,我是否必须在每个子类中声明它们藏汉?
我知道我的动物应该有动物类的3个参数加上猫类的另一个参数所以新的猫应该接受(名字,年龄,种类,牙齿)但似乎我必须告诉猫的构造函数我接受所有这些以及我的问题,动物课的目的是什么?如果我仍然需要在所有子类中编写代码,为什么要有基类?可能我没有得到它,但我读的越多,我就越困惑。
答案 0 :(得分:4)
没有继承
class Cat
{
float height;
float weight;
float energy;
string breed;
int somethingSpecificToCat;
public Cat()
{
//your constructor. initialize all fields
}
public Eat()
{
energy++;
weight++;
}
public Attack()
{
energy--;
weight--;
}
}
class Dog
{
float height;
float weight;
float energy;
string breed;
int somethingSpecificToDog;
public Dog()
{
//your constructor. initialize all fields
}
public Eat()
{
energy++;
weight++;
}
public Attack()
{
energy--;
weight--;
}
}
继承
动物常见的一切都会转移到基类。这样,当您想要设置新动物时,您不需要再次输入它。
abstract class Animal
{
float height;
float weight;
float energy;
string breed;
public Eat()
{
energy++;
weight++;
}
public Attack()
{
energy--;
weight--;
}
}
class Cat : Animal
{
int somethingSpecificToCat;
public Cat()
{
//your constructor. initialize all fields
}
}
class Dog : Animal
{
int somethingSpecificToDog;
public Dog()
{
//your constructor. initialize all fields
}
}
另一个优点是,如果您想要使用唯一ID标记每个动物,则不需要在每个构造函数中包含该动物,并保留使用的最后一个ID的全局变量。您可以在Animal构造函数中轻松执行此操作,因为每次实例化派生类时都会调用它。
示例强>
abstract class Animal
{
static int sID = 0;
float height;
float weight;
int id;
public Animal()
{
id = ++sID;
}
}
现在你做的时候;
Dog lassie = new Dog(); //gets ID = 1
Cat garfield = new Cat(); // gets ID = 2
如果您想要“农场”中所有动物的列表,
没有继承
List<Cat> cats = new List<Cat>(); //list of all cats
List<Dog> dogs = new List<Dog>(); //list of all dogs
...etc
继承
List<Animal> animals = new List<Animal>(); //maintain a single list with all animals
animals.Add(lassie as Animal);
animals.Add(garfield as Animal);
这样,如果你想看看你是否有一只名为Pluto
的动物,你只需要迭代一个列表(动物)而不是多个列表(猫,狗,猪等)。 p>
编辑以回应您的评论
您不需要实例化Animal。您只需创建一个您想要的动物对象。事实上,由于Animal永远不会是通用动物,因此您可以将Animal
创建为抽象类。
abstract class Animal
{
float height;
float weight;
float energy;
string breed;
public Eat()
{
energy++;
weight++;
}
public Attack()
{
energy--;
weight--;
}
}
class Cat : Animal
{
int somethingSpecificToCat;
public Cat()
{
//your constructor. initialize all fields
}
}
class Dog : Animal
{
int somethingSpecificToDog;
public Dog()
{
//your constructor. initialize all fields
}
}
Cat garfield = new Cat();
garfield.height = 24.5;
garfield.weight = 999; //he's a fat cat
//as you can see, you just instantiate the object garfield
//and instantly have access to all members of Animal
Animal jerry = new Animal(); //throws error
//you cannot create an object of type Animal
//since Animal is an abstract class. In this example
//the right way would be to create a class Mouse deriving from animal and then doing
Mouse jerry = new Mouse();
修改您的评论
如果将其存储在动物列表中,您仍然可以访问所有字段。你只需要将它恢复到原来的类型。
List<Animal> animals = new List<Animal>();
animals.Add(garfield as Animal);
animals.Add(lassie as Animal);
//if you do not cast, you cannot access fields that were specific to the derived class.
Console.WriteLine(animals[0].height); //this is valid. Prints Garfield's height
Console.WriteLine(animals[0].somethingSpecificToCat); //invalid since you haven't casted
Console.WriteLine((animals[0] as Cat).somethingSpecificToCat); //now it is valid
//if you want to do it in a loop
foreach(Animal animal in animals)
{
//GetType() returns the derived class that the particular animal was casted FROM earlier
if(animal is Cat)
{
//the animal is a cat
Cat garfield = animal as Cat;
garfield.height;
garfield.somethingSpecificToCat;
}
else if (animal is Dog)
{
//animal is a dog
Dog lassie = animal as Dog;
lassie.height;
lassie.somethingSpecificToDog;
}
}
答案 1 :(得分:1)
你需要告诉构造函数接受参数(如果你不想要它们),但是你不需要再次实现这些属性:
public class Animal
{
public string Name { get; set; }
public Animal(string Name)
{
Name = name;
}
}
public class Cat : Animal
{
public int Teeth { get; set; }
public Cat(string name, int teeth)
{
Name = name; //<-- got from base
Teeth = teeth; //<-- defined localy
}
//or do this
public Cat(string name, int teeth) : base(name)
{
Teeth = teeth;
}
}
您还可以执行以下操作:
Cat cat = new Cat("cat", 12);
Animal kitty = cat as Animal;
这是有道理的,例如如果您想要List<Animal>
这样的列表,可以添加Cat
- 实例:
List<Animal> animals = new List<Animal>();
animals.Add(new Animal("Coco"));
animals.Add(cat);
foreach(Animal animal in animals)
{
Console.WriteLine(String.Format("Name: {0}", animal.Name));
if(animal is Cat)
{
Console.WriteLine(String.Format("{0} is a Cat with {1} teeth.", animal.Name
(animal as Cat).Teeth));
}
Console.WriteLine("============");
}
将输出:
Name: Coco
============
Name: cat
cat is a Cat with 12 teeth.
============
答案 2 :(得分:1)
您可能需要记住,您正在使用的示例非常简单。如果你需要一些复杂的方法来确定一个基类值,你不希望在多个类中编写/复制它,因为这将变得乏味并使代码的维护变成一场噩梦,在这些类型的情况下声明构造函数中的一些参数变得微不足道。
答案 3 :(得分:1)
好处是您不必在每种动物中声明年龄的名称。你得到它们为你预先制作。继承让你做的另一个重点是。让我们说你想拥有一系列动物。所以你输入类似的东西。 Arraylist arr =等等... 但这只会持有猫型物品。因此,你可以做一些像Arraylist这样的东西,它可以容纳所有类型的动物,猫和狗。基本上,基类的变量可以指向派生类的变量。这在大多数情况下都非常方便,因为事情变得复杂。
答案 4 :(得分:0)
继承不仅仅是构造函数。例如,在您的基类Animal中,您可以声明方法Eat(something)或Grow(),这对于所有后继者来说都是相同的。
BTW,没有问题是只用三个参数调用默认的Cat()构造函数(所以调用基础Animal构造函数),然后通过设置适当的字段或属性来指定牙齿。
答案 5 :(得分:0)
我不知道以下信息对您是否有用,但我认为值得一提的是继承的使用。 继承的许多用途之一,或者更具体地说,超类的一个用途是你可以将它们放在同一个集合中:
List<Animal> animals = new List<Animal>();
animals.Add(new Cat());
animals.Add(new Dog());
等。等
答案 6 :(得分:0)
不要忘记你也可以将构造函数params传递给基础构造函数。您不必在每个派生类中初始化它们。
例如(窃取chaffin的代码):
public class Animal
{
public string Name { get; set; }
}
public class Cat : Animal
{
public int Teeth { get; set; }
public Cat(string name, int teeth) : Base(name) //pass name to base constructor
{
Teeth = teeth;
}
}
答案 7 :(得分:0)
你完全是在想这个。
动物类的目的是什么?
你的场景非常非常简单。试着用这种方式来思考:特征越常见,它应该放置在层次结构中越高。为什么?只是为了避免重复和冗余。想象一下,几乎没有额外的课程:狗,马和青蛙。由于猫,狗和马是哺乳动物,你也可以创造一种定义共同哺乳动物特征的类哺乳动物。为什么?例如,避免为相似物种编写相同的构造函数,字段,方法。在您的情况下,尝试将您的动物类视为所有动物共有的特征库。
答案 8 :(得分:0)
是的,您将不得不创建构造函数,但这并不会使继承毫无意义,尽管优先考虑组合而不是继承,这实际上是一种很好的设计实践,即 通常 强>(并非总是)最好有一只猫是动物,而不是猫是动物的猫。
回归继承,当它真的得到回报时,因为你的所有动物都会想到一些属性,腿,耳朵,头部,并且通过使用继承,你不必在每个类中声明这些属性你创建
同样使用继承,你可以使用多态,比如你有这个类(伪代码)
public abstract class Animal()
{
//Some atributes
//Methods
makeSound();
}
然后你有
public class Cat extends Animal
{
makeSound()
{
System.out.println("meow");
}
}
然后说你也为狗延伸动物:
public class Dog extends Animal
{
makeSound()
{
System.out.println("woof")
}
}
现在说你有一个像这样声明的数组:
List<Animal> animals = new ArrayList<Animal>();
animals.add(cat);
animals.add(dog);
然后说你希望每个动物发出声音然后你可以使用多态,这将使每个实现调用它的makeSound方法:
for (Animals animal : animals)
{
animal.makeSound();
}
这将为猫打印 “喵”
和狗
“汪汪”
答案 9 :(得分:0)
继承允许您编写在类之间共享的代码,以便将常用功能/数据放在基类中并从中派生其他类。要使用您的示例:
class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
}
class Cat : Animal
{
// Put cat-only properties here...
public Cat(string name) : base(name)
{
// Set cat-specific properties here...
}
}
你甚至不需要为每个类构造函数提供相同数量的参数 - 如果cat没有名称(对于一个人为的例子),只需创建一个没有参数的Cat
构造函数并传递在适合基础构造函数的东西中。这允许您控制所有动物类的设置方式。
答案 10 :(得分:0)
你很困惑,因为你只想到构造函数。事实如msdn中所述,“构造函数和析构函数不是继承的”。因此,当您继承类时,基类构造函数不适用于派生类。衍生类必须提到它自己的construtor /析构函数集。要理解为什么会这样,您可以在这里查看:Why are constructors not inherited?。
现在回答你的问题,是的,你必须在cat类中添加一个构造函数来接受所有这四个参数。但是您不必在cat类中再次实现这3个字段。动物类的所有公共保护和内部字段和方法仍然可用于您的cat类,您不必在派生类中重新实现它们。这是您的基类为派生类提供服务的方式。