需要一些帮助来理解继承

时间:2012-01-24 12:17:39

标签: c# class inheritance abstract

有一个任务是创建一个注册动物的程序,对象是熟悉继承,多态等等。

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

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

到目前为止,所有动物都有这么好的信息,但每只动物都有一个独特的领域,所以生病了我的猫作为公共阶级的猫:动物,并给猫的田间牙齿。例如。

现在我想创建一个新的动物猫,我从几个列表框中获取数据,所以我需要一个带有这些参数的构造函数,这是我没有得到的,我是否必须在每个子类中声明它们藏汉?

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

11 个答案:

答案 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类,您不必在派生类中重新实现它们。这是您的基类为派生类提供服务的方式。