2013-04-23 42 views
5

Hãy xem xét các loại sau đây:phương pháp trừu tượng với kiểu trả mạnh mẽ gõ

public abstract class Animal 
{ 
    public abstract Animal GiveBirth(); 
} 

public class Monkey : Animal 
{ 
    public override Animal GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

public class Snake : Animal 
{ 
    public override Animal GiveBirth() 
    { 
     return new Snake(); 
    } 
} 

//That one doesnt makes sense. 
public class WeirdHuman: Animal 
{ 
    public override Animal GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

Tôi đang tìm kiếm một cách để thực thi các kiểu trả về của phương pháp overrided GiveBirth để nó luôn luôn trả về kiểu lớp thực tế, do đó không WeirdHuman có thể sinh một số Monkey.

Tôi cảm thấy câu trả lời là về các loại chung chung, nhưng tôi không thể thấy cách tôi có thể làm điều đó.

dụ về kết quả mong đợi:

public abstract class Animal 
{ 
    public abstract /*here a way to specify concrete type*/ GiveBirth(); 
} 

public class Monkey : Animal 
{ 
    public override Monkey GiveBirth() //Must returns an actual Monkey 
    { 
     return new Monkey(); 
    } 
} 

"Tuyệt đối không thể" thể được một câu trả lời, nếu giải thích rõ ràng.

Trả lời

5

Đây là trả về đồng biến thể và không được C# hỗ trợ. Tôi than thở điều này hàng ngày. Điều tốt nhất bạn có thể hy vọng thực hiện là sử dụng kiểu trả về chung và chỉ định một điều kiện về kiểu chung, nhưng điều này cũng có thể khiến bạn gặp phải các vấn đề khác trên đường với các yêu cầu tham số chung phù hợp.

public abstract class Animal<TBirthType> where TBirthType : Animal<TBirthType> 
{ 
    public abstract TBirthType GiveBirth(); 
} 

public class Monkey<TBirthType> : Animal<TBirthType> where TBirthType : Monkey<TBirthType> 
{ 
    public override TBirthType GiveBirth() 
    { 
     return new Monkey<Monkey>(); 
    } 
} 

Cách khác, nếu bạn không cần thừa kế thêm, bạn có thể đóng chung.

public class Monkey : Animal<Monkey> 
{ 
    public override Monkey GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

Lưu ý rằng hiệp phương sai một mình vẫn là không đủ để đảm bảo rằng không có loại misbehaving có nguồn gốc có thể được hình thành, nhưng nó sẽ cho phép cho các loại sự trở lại được quy định như loại được sử dụng. Vẫn không có cách nào để khóa nó xuống từ lớp trừu tượng. Bạn có lẽ có thể quản lý một kiểm tra thời gian chạy thông qua sự phản ánh từ một phương pháp được thực hiện ở cấp cơ sở sẽ kiểm tra kiểu lúc chạy, nhưng điều này cũng có thể rất lộn xộn.

+0

Đó là những gì tôi nghĩ. Tôi không thể thấy bất kỳ cách nào để xác định rằng loại chung phải thực hiện loại lớp thực tế. Giống như 'nơi T: this' hoặc một cái gì đó ... – Johnny5

+0

Các ngôn ngữ hỗ trợ nó là gì? – Johnny5

+0

Java hỗ trợ nó. Không có gì. Net hỗ trợ nó như CLR là vấn đề cơ bản. Hy vọng rằng một phiên bản tương lai sẽ thêm hỗ trợ, nhưng như tôi hiểu nó sẽ có một thay đổi đáng kể để làm cho nó hoạt động. –

1

Bạn có thể làm điều gì đó như thế này, buộc người triển khai Animal<T> thực hiện phương thức Animal<T> GiveBirth() trả về cùng loại với thông số loại, chính nó bị ràng buộc là một loại động vật.

Đó không phải là khá những gì bạn muốn, nhưng chỉ để bạn có thể thấy:

public abstract class Animal<T> where T: Animal<T> 
{ 
    public abstract Animal<T> GiveBirth(); 
} 

public class Monkey: Animal<Monkey> 
{ 
    public override Animal<Monkey> GiveBirth() 
    { 
     return new Monkey(); 
    } 
} 

public class Snake: Animal<Snake> 
{ 
    public override Animal<Snake> GiveBirth() 
    { 
     return new Snake(); 
    } 
} 

public class WeirdHuman: Animal<WeirdHuman> 
{ 
    public override Animal<WeirdHuman> GiveBirth() 
    { 
     return new Monkey(); // Won't compile of course. 
    } 
} 

Nếu bạn nhận xét ra public override Animal<Monkey> GiveBirth() phương pháp, bạn sẽ thấy rằng trình biên dịch phàn nàn và nói điều gì đó như:

Lỗi 1 'ConsoleApplication1.Monkey' không thực hiện được kế thừa thành viên trừu tượng 'ConsoleApplication1.Animal.GiveBirth()'

Thật không may, bạn phải khai báo các lớp bằng cách sử dụng cú pháp SomeKindOfAnimal: Animal<SomeKindOfAnimal>, nhưng có lẽ điều này sẽ làm việc cho bạn.

(Also see this thread.)

Alas, điều này không hoàn toàn làm việc vì nó cho phép bạn thực hiện điều này:

public class Monkey: Animal<WeirdHuman> 
{ 
    public override Animal<WeirdHuman> GiveBirth() 
    { 
     return new WeirdHuman(); 
    } 
} 

Nói cách khác, nó chế các tham số kiểu là một loại động vật, và nó cũng hạn chế kiểu trả về của GiveBirth() tương tự như tham số kiểu; nhưng đó là tất cả.Trong một số trường hợp, điều này là đủ, nhưng có lẽ không phải vì mục đích của bạn.

Tuy nhiên, có lẽ cách tiếp cận này đáng để biết.

+0

Và sau đó điều gì sẽ xảy ra khi một người nào đó tạo ra một 'Con khỉ: Động vật ' và tạo ra những đứa trẻ thực sự sai lầm? – Servy

+0

@Servy :) Vâng, phương thức 'GiveBirth()' vẫn còn bị ràng buộc để chỉ trả về một 'Monkey', do đó, nó đáp ứng yêu cầu cho *" một cách để thực thi các kiểu trả về của phương thức GiveBirth được ghi đè để nó luôn trả về loại lớp thực tế * "(từ OP). Bạn được phép nói rằng một con khỉ là một con người lạ nếu bạn muốn! –

+1

Nhưng nó không bị ràng buộc để trả về kiểu của lớp thực hiện giao diện, chỉ là bất kỳ kiểu nào mà lớp thực hiện giao diện chọn. – Servy

1

Theo như tôi biết, không có cách nào rõ ràng để hỗ trợ điều này hoàn toàn trong một phân cấp lớp đơn. Sử dụng thông số loại chung định kỳ, ví dụ:

public class Animal<T> where T : Animal<T> { } 

có thể được chấp nhận nếu bạn kiểm soát toàn bộ hệ thống cấp bậc, và do đó có thể loại trừ các lớp học như

public class WierdHuman<Monkey> { } 

gì bạn thực sự muốn là một cái gì đó giống như typeclasses Haskell, nơi bạn có thể trừu tượng hơn các loại bê tông của chính lớp đó. Gần nhất bạn có thể nhận được trong C# là xác định một đối tượng thay thế thực hiện các chức năng cần thiết, và sau đó vượt qua xung quanh bất cứ nơi nào bạn yêu cầu nó.

Trong trường hợp của bạn, điều này có nghĩa là tạo một giao diện để sinh và triển khai giao diện cho từng loại động vật cụ thể.

Phương pháp của bạn yêu cầu chức năng này, sau đó cần tham số bổ sung cho 'cá thể kiểu chữ'. Các phương pháp này có thể hạn chế loại động vật chung là giống nhau:

public interface ISpawn<T> where T : Animal 
{ 
    public T GiveBirth(); 
} 

public void Populate<T>(T parent, ISpawn<T> spawn) where T : Animal 
{ 
} 
Các vấn đề liên quan