2010-09-07 50 views
5

在C#中,我有三個類:Person,Cat和Dog。如何讓類屬性是多個/靈活類型在C#中?

Cat和Dog類都有Eat()方法。

我希望Person類有一個屬性'Pet'。

我希望能夠通過像Person.Pet.Eat()這樣的東西來調用Cat和Dog的Eat方法,但我不能,因爲Pet屬性需要是Cat類型或狗。

目前我在Person類中使用了兩個屬性:PetDog和PetCat。

現在可以,但如果我想要100種不同類型的動物作爲寵物,那麼我並不想在Person類中擁有100種不同的寵物屬性。

是否有一種方法使用接口或繼承?有沒有一種方法可以將Pet設置爲Object類型,但仍然可以訪問分配給它的任何動物類的屬性?

+7

這是功課嗎? – 2010-09-07 17:10:19

+2

聞起來像給我做功課。家庭作業問題沒有任何問題,但他們應該被標記爲這樣。如果家庭作業相關,請在問題中添加「作業」標籤。 – 2010-09-07 17:12:18

+2

我不喜歡這個Person.Pet.Eat(),讀錯誤的方式,我可能會害怕這個人會對寵物做什麼..如何Person.FeedPet()代替。對於動物來說.. – 2010-09-07 17:14:52

回答

12

你可以讓寵物從一個共同的基類派生:

public abstract class Animal 
{ 
    protected Animal() { } 

    public abstract void Eat(); 
} 

然後貓和狗派生自這個基類:

public class Cat: Animal 
{ 
    public override void Eat() 
    { 
     // TODO: Provide an implementation for an eating cat 
    } 
} 

public class Dog: Animal 
{ 
    public override void Eat() 
    { 
     // TODO: Provide an implementation for an eating dog 
    } 
} 

而且你的Person類將有Animal類型的屬性:

public class Person 
{ 
    public Animal Pet { get; set; } 
} 

當你擁有了人的一個實例:

var person = new Person 
{ 
    Pet = new Cat() 
}; 
// This will call the Eat method from the Cat class as the pet is a cat 
person.Pet.Eat(); 

你也可以提供一些在基類中的Eat方法的共同實現,以避免在派生類中重寫它ES:

public abstract class Animal 
{ 
    protected Animal() { } 

    public virtual void Eat() 
    { 
     // TODO : Provide some common implementation for an eating animal 
    } 
} 

注意Animal仍然是一個抽象類,以防止它被直接實例化。

public class Cat: Animal 
{ 
} 

public class Dog: Animal 
{ 
    public override void Eat() 
    { 
     // TODO: Some specific behavior for an eating dog like 
     // doing a mess all around his bowl :-) 

     base.Eat(); 
    } 
} 
+0

它如何知道在調用'person.Pet.Eat()'時吃哪個類的函數? – 2010-09-07 17:20:24

+0

取決於'Pet'屬性的實際類型。 – 2010-09-07 17:21:09

+0

@Abe Miessler:它不在編譯時。這個決定是在運行時做出的,當它知道什麼類型的實例實際存儲在'person.Pet'中。 – Timwi 2010-09-07 17:22:06

1

使用接口;

abstract class Animal 
{ 
    public virtual void DoSomething() { } 
} 

interface ICanEat 
{ 
    void Eat(); 
} 

class Dog : Animal, ICanEat 
{ 
    public void Eat() 
    { 
     Console.Out.WriteLine("Dog eat"); 
    } 
} 

class Cat : Animal, ICanEat 
{ 
    public void Eat() 
    { 
     Console.Out.WriteLine("Cat eat"); 
    } 
} 

class Person 
{ 
    public Animal MyAnimal { get; set; } 
} 

然後就可以調用

(person.MyAnimal as ICanEat).Eat(); 

執行正確的空支票,當您去。

+2

製作''Pet' ICanEat'將使演員不必要。此外,你應該*永遠不會*使用'as'操作符來保證成功的投射操作,明確的投射就是你所需要的。由於失敗的可能性,在解除引用前應該跟隨一個空測試。 – Ani 2010-09-07 17:15:23

1

使用抽象類。

public abstract class Pet { public abstract void Eat(); } 

public class Dog : Pet { } 
public class Cat : Pet { } 

public class Person { 
    public Pet Pet; 
} 

我相信你可以做剩下的事情。

0

爲什麼不創建一個基類的寵物

public abstract class Pet{ 

    public abstract void Eat(); 

} 
0

有這種貓與狗從一個接口繼承IPet

public interface IPet 
{ 
    bool hungry(); 
    void Eat(); 
} 
11

是否有辦法解決這使用接口或繼承?

是。

接口:使接口IEat或IPet或任何您想要表示的概念。使界面有一個Eat方法。有貓和狗實現這個接口。讓Pet屬性成爲這種類型。

繼承:創建一個抽象基類動物或寵物或任何想要表示的概念。做一個抽象方法吃基礎類。貓和狗從這個基類繼承。讓Pet屬性成爲這種類型。

這兩者有什麼區別?

使用接口來模擬「X知道如何做Y」的想法。例如,一次性使用「一次性」意味着「我知道如何處理我所持有的重要資源」。這不是關於什麼對象的事實,關於什麼對象確實是

使用繼承來模擬「X是一種Y」的想法。狗是一種動物。

接口的事情是你可以擁有儘可能多的接口。但是你只能從直接繼承一個基類,所以如果你打算使用繼承,你必須確保你做對了。繼承問題是人們最終制造出類似「車輛」的基礎類,然後他們說「軍車是一種車輛」,「船舶是一種車輛」,現在你被困住了:基礎是什麼驅逐艦的類?它既是船舶又是軍用車輛,它不能同時爲非常仔細地選擇你的「繼承樞軸」

有沒有一種方法可以將Pet設置爲Object類型,但仍然可以訪問分配給它的任何動物類的屬性?

是的,在C#4中有,但是不這樣做。使用接口或繼承。

在C#4中,您可以使用「動態」在運行時動態分派到Pet上的對象的Eat方法。

你不想做,這是因爲這將崩潰和死亡可怕的應該有人把水果或手鋸在寵物屬性,然後試着讓它吃的原因。編譯時檢查的重點是降低程序的脆弱性。如果您有辦法進行更多編譯時檢查,請使用它。

+0

如果我有一把手鋸作爲寵物,我可能瘋了,覺得它吃了。 – 2010-09-07 17:23:40

+2

@Jeff:的確,如果你不瞭解手鋸的鷹派,你就會遇到問題。我的建議:等待南風。 – 2010-09-07 17:26:03

+0

謝謝,這非常有幫助,也很機智。一旦我得到足夠的代表,我會投票。 – Caustix 2010-09-07 17:31:35