2012-01-24 80 views
1

有一个任务来制作一个注册动物的程序,并且该对象是熟悉继承,多态等等。需要一些帮助来理解继承

让我感到困惑的一件事是,无论我读了多少关于它似乎毫无意义。

我创建了我的主要类,它是适用于所有动物的一些通用字段的动物,可以说名称,年龄和物种。

到目前为止,所有的动物都有这样的信息,但每个动物都有一个独特的领域,所以生病我创造了我的猫作为公共课程的猫:动物和猫给田野牙齿例如。

现在我想制作一个新的动物,它是一只猫,我从几个列表框中获取数据,所以我需要一个构造函数来接受这些参数,这就是我没有得到的,是否必须在每个孩子中声明它们类也是?

我知道我的动物应该有3个参数来自动物类,另外一个来自猫类,所以新的猫应该接受(姓名,年龄,物种,牙齿),但似乎我必须告诉构造函数猫类接受所有这些和theres我的问题,动物类服务的目的是什么?如果我仍然需要在所有子类中编写代码,为什么要有基类?可能我没有得到它,但我读得越多,我变得越困惑。

+1

你已经迷惑了你自己的例子。例如 - 只有猫有牙齿,还是做所有的动物?为了争论,让所有的动物都这样做(一个大多数正确的说法) - 那么牙齿不应该成为你的基类的一部分吗? –

+0

那么这只是一个例子,我将会有多个范畴和物种,每一个物种都会有一个独特的领域,但是让我们改变这个思路:p。 – Gvs

+0

@ user1083543:看起来这是某种家庭作业/课堂作业。出于这个原因,继承可能有很多优点,这个特定的任务不会导致你利用。但是分配的目的是让你熟悉继承,这样当你确实需要时,你可以使用它。 – xbonez

回答

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); 

这样,如果你想要t Ø看到,如果你有一个名为Pluto一种动物,你只需要遍历一个列表(动物),而不是多个列表(猫,狗,猪等)

编辑回应您的评论

你不需要实例化动物。你只需创建一个你想要的动物的对象。实际上,由于动物永远不会是通用动物,因此您可以创建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; 
    } 
} 
+0

对于下一个问题,那么:p。我的动物课应该从我的表格中获得它的价值,但我怎么称呼它?首先用参数实例化一个新的动物,然后调用以创建一个新的Cat? – Gvs

+0

@ user1083543:你不需要永远实例化动物。你只是实例化一只猫或一只狗。事实上,如果你的动物永远是一种特定类型的动物而不只是一个普通的动物,你应该在基类Animal上使用关键字'abstract'。这确保你不能创建一个类型为“Animal”的对象。请参阅我正在编辑的编辑以更好地解释。 – xbonez

+0

非常感谢100万的解释清楚,非常感谢。 – Gvs

0

继承不是关于构造函数。例如,在您的基类Animal中,您可以声明方法Eat(something)或Grow(),这对所有后继者都是平等的。

顺便说一句,没有问题是调用默认的Cat()构造函数只有三个参数(所以调用基本动物构造函数),然后通过设置适当的字段或属性指定牙齿。

+0

只有当我有一个方法适用于像speak()这样的所有动物时,才能有效地进行继承。 – Gvs

+0

是的,当然。但这是最常见的情况。你也可能会发现这篇文章很有用:http://msdn.microsoft.com/en-us/library/27db6csx%28v=vs.71%29.aspx –

1

你需要告诉构造器来接受的参数(如果你wan't需要它们),但你并不需要再次实现属性:

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 -instance:

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. 
============ 
0

我不知道下面的信息将是任何对你有用,但我认为值得一提的是继承的使用。 一个继承的许多用途的,或者更具体的,超阶级的是,你可以把它们放在同一个集合中:

List<Animal> animals = new List<Animal>(); 

animals.Add(new Cat()); 
animals.Add(new Dog()); 

等等,等等

1

你可能需要在裸记住你正在努力的例子非常简单。如果您需要一些复杂的方法来确定某个基类值,您不希望将其写入/复制到多个类中,因为这会变得单调乏味,并且使代码的维护成为一场噩梦,在这些类型的情况下,声明构造函数中的一些参数变得微不足道。

1

好处是,你不必在每种类型的动物中声明名称年龄种类。你让他们为你预制。继承可以让你做的另一个重点是。可以说你想拥有一系列动物。所以你输入类似的东西。 Arraylist ARR =等等...... 但这只会保存猫类型的对象。所以,你可以做一些像Arraylist这样的东西,它可以容纳所有类型的动物,猫和狗。基本上,基类的变量可以指向派生类的变量。这在大多数情况下非常方便,因为情况变得复杂。

+0

看到这是我没有得到,也许它是我的多数民众赞成在一个糟糕的但是本质上来说,这是我得到的任务:我从文本框或列表框中获取姓名,年龄和物种,然后我需要“制作”它的一个动物。比方说,我选择猫作为硬币,然后我想创建一只动物与猫的独特数据。下一次我们用一个独特的数据添加一条鱼,等等,唯一不同的是关于基类动物的一个子类中包含的每一个物种的独特数据。 – Gvs

+0

那么你的构造函数将只调用具有公共值的基础构造函数,并且具体对象将在派生构造函数中分配值 –

0

不要忘了你可以传递构造函数参数给基础构造函数。您不必在每个派生类中都初始化它们。

如(偷chrfin的代码):

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; 
    } 
} 
+0

刚刚在我的回答中加入了相同的内容(没有先读你的):D – ChrFin

0

你完全这个得太多。

动物类服务的目的是什么?

您的场景真的很简单。尝试用这种方式来思考:功能越常见,应该放置的层次结构越高。为什么?只是为了避免重复和冗余。设想几乎没有其他的课程:狗,马和青蛙。由于猫,狗和马都是哺乳动物,因此您可以创建一个定义共享哺乳动物特征的阶级哺乳动物。为什么?例如为了避免为相似物种编写相同的构造函数,字段和方法。在你的情况下,尽量把你的动物课程当作所有动物共有的特征库。

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(); 
} 

这将打印猫 “喵“

和狗

”汪汪“

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构造没有参数并传入适合基础构造函数的东西。这使您可以控制如何设置所有动物类。

0

由于您只考虑构造函数,因此您会感到困惑。事实如msdn所述,“构造函数和析构函数不是继承的”。所以当你继承你的类时,基类构造函数不适用于派生类。派生类必须提及它自己的一套构造器/析构器。要理解为什么这样,你可以看看这里:Why are constructors not inherited?

现在回到你的问题,是的,你必须在你的猫类添加一个构造函数来接受所有的四个参数。但是你不需要在你的猫类中再次实现这3个字段。你的动物类的所有公共保护和内部领域和方法仍然可用于你的猫类,你不必在你的派生类中重新实现它们。这就是你的基类为派生类服务的方式。

+0

但是,当动物类的参数是从表格中传递时,它是如何工作的?我想创建一个应该继承动物字段的猫,但是动物类没有价值,因为它们是从表单传递的?那么,如果我想创造一只猫而不是动物,动物课怎么知道我的形式有什么价值? – Gvs

+0

在继承中,父类不会知道派生类。所以动物课不会意识到猫或狗课。你也不能指望c#决定它应该实例化哪个类。根据表单中选定的值,您必须编写一个程序,该程序将创建适当类的对象。然后将表单中的值分配给对象的相应字段。这必须由你明确地完成。 – Rakesh