2010-09-07 25 views
5

Trong C# tôi có ba lớp: Person, Cat và Dog.Cách cho phép Thuộc tính lớp có nhiều loại/linh hoạt trong C#?

Cả lớp Cat và Dog đều có phương thức Eat().

Tôi muốn lớp Người có tài sản ‘Thú cưng’.

Tôi muốn có thể gọi phương thức Eat của cả Cat và Dog thông qua Person thông qua Person.Pet.Eat() nhưng tôi không thể vì thuộc tính Pet cần phải là loại Cat hoặc Chó.

Hiện tại tôi đang làm tròn điều này với hai thuộc tính trong lớp Person: PetDog và PetCat.

Điều này vẫn ổn, nhưng nếu tôi muốn có 100 loại động vật khác nhau làm vật nuôi thì tôi không thực sự muốn có 100 thuộc tính thú cưng khác nhau trong lớp Person.

Có cách nào vòng này bằng giao diện hoặc thừa kế không? Có cách nào tôi có thể đặt Pet là kiểu Object nhưng vẫn truy cập vào các thuộc tính của bất kỳ lớp động vật nào được gán cho nó?

+7

Đây có phải là bài tập về nhà không? –

+2

Mùi như bài tập về nhà với tôi. Không có gì sai với các câu hỏi về bài tập về nhà, nhưng chúng phải được gắn cờ như vậy. Thêm thẻ "bài tập về nhà" vào câu hỏi của bạn nếu đó là bài tập về nhà có liên quan. –

+2

Tôi không thích người này.Pet.Eat(), đọc sai cách tôi có thể sợ những gì người đó sẽ làm cho vật nuôi .. Làm thế nào về Person.FeedPet() để thay thế. Vì lợi ích của động vật .. –

Trả lời

12

Bạn có thể có các con vật nuôi có nguồn gốc từ một lớp cơ sở chung:

public abstract class Animal 
{ 
    protected Animal() { } 

    public abstract void Eat(); 
} 

Và sau đó có Mèo và Chó bắt nguồn từ lớp cơ sở này:

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 
    } 
} 

Và lớp Person của bạn sẽ có một loại tài sản của Animal:

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

Và khi bạn có một thể hiện của Person:

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

Bạn cũng có thể cung cấp một số thực hiện phổ biến cho phương thức Eat trong lớp cơ sở để tránh phải ghi đè nó trong lớp dẫn xuất es:

public abstract class Animal 
{ 
    protected Animal() { } 

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

Lưu ý rằng Animal vẫn là lớp trừu tượng để ngăn không cho nó được khởi tạo trực tiếp.

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

Làm cách nào để biết chức năng 'Eat' sẽ chạy khi bạn gọi 'person.Pet.Eat()'? –

+0

Tùy thuộc vào loại thực tế của thuộc tính 'Pet'. –

+0

@Abe Miessler: Nó không có thời gian biên dịch. Quyết định được đưa ra vào thời gian chạy, khi nó biết loại cá thể nào thực sự được lưu trữ trong 'person.Pet'. – Timwi

1

Sử dụng giao diện;

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

Sau đó, bạn có thể gọi

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

thực hiện đúng null-kiểm tra như bạn đi.

+2

Tạo thuộc tính 'Pet' thuộc loại' ICanEat' sẽ làm cho diễn viên không cần thiết. Ngoài ra, bạn nên * không bao giờ * sử dụng toán tử 'as' cho một phép toán được đảm bảo để thành công, một diễn viên rõ ràng là những gì bạn cần. 'as' nên được theo sau với một thử nghiệm null trước khi dereferencing vì khả năng của một diễn viên thất bại. – Ani

1

Sử dụng lớp trừu tượng.

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

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

public class Person { 
    public Pet Pet; 
} 

Tôi chắc chắn bạn có thể làm phần còn lại.

0

Tại sao không tạo một lớp cơ sở của loại thú nuôi

public abstract class Pet{ 

    public abstract void Eat(); 

} 
0

Có con mèo này và con chó kế thừa từ một giao diện IPet

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

Có cách nào vòng này sử dụng giao diện hoặc thừa kế không?

Có.

Giao diện: tạo giao diện IEat hoặc IPet hoặc bất kỳ khái niệm nào bạn muốn đại diện. Làm cho giao diện có một phương thức Eat. Yêu cầu Cat và Dog triển khai giao diện này. Có thuộc tính Pet thuộc loại đó.

Thừa kế: Tạo lớp cơ sở trừu tượng Động vật hoặc Thú cưng hoặc bất kỳ khái niệm nào bạn muốn đại diện. Tạo một phương thức trừu tượng Ăn trên lớp cơ sở. Có Cat và Dog kế thừa từ lớp cơ sở này. Có thuộc tính Pet thuộc loại đó.

Sự khác biệt giữa hai loại này là gì?

Sử dụng giao diện để lập mô hình ý tưởng "X biết cách thực hiện Y". Ví dụ: IDisposable có nghĩa là "Tôi biết cách xử lý tài nguyên quan trọng mà tôi đang nắm giữ". Đó không phải là một thực tế về những gì đối tượng , đó là một thực tế về những gì đối tượng không.

Sử dụng kế thừa để mô hình hóa ý tưởng "X là một loại Y". Chó là một loại động vật.

Điều về giao diện là bạn có thể có bao nhiêu trong số chúng tùy thích. Nhưng bạn chỉ có thể kế thừa trực tiếp từ một lớp cơ sở, vì vậy bạn phải đảm bảo bạn có được quyền nếu bạn định sử dụng thừa kế.Vấn đề với thừa kế là mọi người sẽ tạo ra các lớp cơ sở như "Xe" và sau đó họ nói "Quân đội là một loại Xe" và "Tàu là một loại Xe" và bây giờ bạn đang mắc kẹt: cơ sở là gì lớp Destroyer? Đó là cả Tàu và Quân đội và không thể là cả hai. Chọn "trục kế thừa" cực kỳ cẩn thận.

Có cách nào tôi có thể đặt Thú cưng là loại đối tượng nhưng vẫn truy cập vào thuộc tính của bất kỳ lớp học động vật nào được gán cho nó?

Có, trong C# 4 có, nhưng không làm như vậy. Sử dụng giao diện hoặc kế thừa.

Trong C# 4, bạn có thể sử dụng "động" để nhận được công văn động khi chạy đến phương thức Eat trên đối tượng trong Pet.

Lý do bạn không muốn làm điều này là vì điều này sẽ sụp đổ và chết khủng khiếp nếu ai đó đặt trái cây hoặc cưa tay trong tài sản thú cưng và sau đó thử làm cho nó ăn. Điểm kiểm tra thời gian biên dịch là giảm sự mong manh của chương trình. Nếu bạn có cách để thực hiện kiểm tra biên dịch nhiều lần hơn, hãy sử dụng nó.

+0

Nếu tôi có một Handsaw làm vật nuôi, tôi có thể đủ điên để nghĩ rằng nó ăn. –

+2

@ Jeff: Thật vậy, nếu bạn không biết một con diều hâu từ một cái cưa tay, bạn đã có vấn đề. Lời khuyên của tôi: chờ một cơn gió phía nam. –

+0

Cảm ơn, điều này rất hữu ích và cũng dí dỏm. Tôi sẽ bầu nó lên một lần tôi có đủ đại diện. – Caustix

Các vấn đề liên quan