2012-01-24 81 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