2010-03-23 22 views
5

Các trường hợp sử dụng một số điều như thế này:Tại sao khái niệm "hiệp phương sai" và "Contravariance" được áp dụng trong khi thực hiện các phương thức của một giao diện?

public class SomeClass: IClonable 
{ 
    // Some Code 

    //Implementing interface method 
    Public object Clone() 
     { 
     //Some Clonning Code 
     } 
} 

Bây giờ câu hỏi của tôi là: "Tại sao nó không thể sử dụng 'SomeClass (Vì nó là derivd từ objec)' như một kiểu trả về của Clone() phương pháp nếu chúng ta xem xét các fUNDA của Hiệp phương sai và Contravariance.

ai đó có thể giải thích cho tôi lý do đằng sau implemementaion này của Microsoft ????

+1

Nếu giao diện 'IClonable' được viết trước mã của bạn, phương thức Clone có thể trả về các loại cụ thể như thế nào? Người bảo đảm duy nhất chúng tôi có là bất cứ điều gì được trả lại bởi việc bạn thực hiện Clone, sẽ được bắt nguồn từ "Object". Vì bạn phải tuân theo định nghĩa giao diện chính xác, bạn sẽ phải trả lại giao diện được nói là "Đối tượng". –

+0

Ở đây tôi muốn rằng việc thực hiện phương pháp Clone của tôi sẽ có thể trả về kiểu cụ thể không phải là phương thức Clone() của IClonable khi tôi nhúng vào giao diện IClonable. Nhưng điều này là không được phép, chúng tôi nhận được một lỗi biên dịch. Vì vậy, tôi muốn biết lý do, tại sao khái niệm hiệp phương sai hoặc Contravariance không được phép thực hiện một giao diện. – Amit

Trả lời

4

Việc triển khai không thực hiện phương sai triển khai giao diện sẽ phải là biến thể trong kiểu trả về và contravariant trong các loại đối số.

Ví dụ:

public interface IFoo 
{ 
    object Flurp(Array array); 
} 

public class GoodFoo : IFoo 
{ 
    public int Flurp(Array array) { ... } 
} 

public class NiceFoo : IFoo 
{ 
    public object Flurp(IEnumerable enumerable) { ... } 
} 

Cả hai đều là hợp pháp theo quy định "mới", phải không? Nhưng những gì về điều này:

public class QuestionableFoo : IFoo 
{ 
    public double Flurp(Array array) { ... } 
    public object Flurp(IEnumerable enumerable) { ... } 
} 

Rất khó để biết triển khai ngầm định nào tốt hơn ở đây. Giá trị đầu tiên là đối sánh chính xác cho loại đối số, nhưng không khớp với kiểu trả về. Thứ hai là đối sánh chính xác cho loại trả về, nhưng không phải là loại đối số. Tôi đang nghiêng về phía đầu tiên, bởi vì bất cứ ai sử dụng giao diện IFoo chỉ có thể cung cấp cho nó một Array, nhưng nó vẫn không hoàn toàn rõ ràng.

Và đây không phải là điều tồi tệ nhất, cho đến nay. Nếu chúng tôi làm điều này thay thế:

public class EvilFoo : IFoo 
{ 
    public object Flurp(ICollection collection) { ... } 
    public object Flurp(ICloneable cloneable) { ... } 
} 

Giải thưởng nào giành giải thưởng? Đó là một tình trạng quá tải hợp lệ hoàn toàn, nhưng ICollectionICloneable không có gì để làm với nhau và Array thực hiện cả hai. Tôi không thể nhìn thấy một giải pháp rõ ràng ở đây.

Nó chỉ trở nên tồi tệ hơn nếu chúng ta bắt đầu thêm quá tải vào giao diện chính nó:

public interface ISuck 
{ 
    Stream Munge(ArrayList list); 
    Stream Munge(Hashtable ht); 
    string Munge(NameValueCollection nvc); 
    object Munge(IEnumerable enumerable); 
} 

public class HateHateHate : ISuck 
{ 
    public FileStream Munge(ICollection collection); 
    public NetworkStream Munge(IEnumerable enumerable); 
    public MemoryStream Munge(Hashtable ht); 
    public Stream Munge(ICloneable cloneable); 
    public object Munge(object o); 
    public Stream Munge(IDictionary dic); 
} 

Chúc may mắn cố gắng để làm sáng tỏ bí ẩn này mà không đi điên.

Tất nhiên, tất cả điều này là tranh luận nếu bạn khẳng định rằng việc triển khai giao diện chỉ nên hỗ trợ phương sai kiểu trả về và không phải phương sai kiểu đối số. Nhưng hầu như tất cả mọi người sẽ xem xét việc thực hiện một nửa như vậy là hoàn toàn bị hỏng và bắt đầu gửi báo cáo lỗi spam, vì vậy tôi không nghĩ rằng nhóm C# sẽ làm điều đó.

Tôi không biết đây có phải là lý do chính thức tại sao nó không được hỗ trợ trong C# ngày hôm nay hay không, nhưng nó sẽ là một ví dụ tốt về loại mã "chỉ ghi" mà nó có thể dẫn đến, và một phần triết lý thiết kế của đội ngũ C# là cố gắng ngăn chặn các nhà phát triển viết mã khủng khiếp.

+0

Đồng ý với bạn điểm Aaronaught. Có thể đây là lý do microsoft đã đi theo cách này. – Amit

+1

Tôi không nghĩ rằng hầu hết mọi người sẽ nghĩ rằng một nửa thực hiện như vậy là bị hỏng. C++ hỗ trợ hiệp phương sai kiểu trả về nhưng không hỗ trợ kiểu đối số chính quy. Eiffel hỗ trợ phương sai hiệp phương thức trả về và kiểu tham số chính thức * hiệp phương sai *, điều này khá lạ. (Eiffel có một cách tiếp cận khác thường để phân nhóm.) Thu thập hỗ trợ cả hai. Tôi đã không nhìn thấy một sự di chuyển hàng loạt từ C++ để Sather như là một kết quả. –

+0

@Eric: Tôi không thể tranh luận với bất kỳ điều gì (mặc dù tôi có thể nói rằng lý do mọi người không đổ xô đến Sather là vì họ chưa bao giờ nghe về nó: P), nhưng bằng một ngôn ngữ như C# đã * * hỗ trợ đối số-loại contravariance như một phần của phương pháp chuyển đổi nhóm phương sai, tôi nghĩ rằng thiếu tính năng đó để thực hiện giao diện chắc chắn sẽ cảm thấy bị hỏng; như một quy tắc, C# và Khuôn khổ .NET cố gắng khá khó để nhất quán. Hai xu của tôi! – Aaronaught

2

Bạn phải thực hiện phương pháp của một giao diện chính xác như họ đang có trong giao diện. Clone ICloneable của phương thức trả về một đối tượng, vì vậy SomeClass của bạn cũng phải trả về một đối tượng. er, trở về một trường hợp SomeClass trong phương pháp Clone SomeClass mà không cần bất kỳ vấn đề, nhưng định nghĩa phương pháp phải phù hợp với giao diện:

public class SomeClass: IClonable 
{ 
    // Some Code 

    //Implementing interface method 
    Public object Clone() 
     { 
     SomeClass ret = new SomeClass(); 
     // copy date from this instance to ret 
     return ret; 
     } 
} 
+0

Và điều đó sẽ được mong đợi. Với công cụ .NET 4 mới xung quanh góc với co/contra phương sai trong/out danh sách, có thể làm chính xác những gì cô ấy đang yêu cầu không? –

0

Theo C# đặc điểm kỹ thuật, bạn phải sử dụng một phương pháp với một chữ ký giống hệt nhau khi trọng hoặc thực hiện một phương thức giao diện. Hãy nhớ rằng Microsoft không sở hữu C#. Trình biên dịch C# của họ đơn giản là việc thực hiện nó. Vậy tại sao spec lại làm mọi thứ theo cách này? Tôi chỉ có thể đoán, nhưng tôi nghi ngờ nó là để dễ thực hiện.

1

Về mặt giải thích lý do đằng sau C# quyết định, Eric Lippert từ Microsoft đã viết nhiều giải thích Contra/hiệp phương sai trong C# ... đây là danh sách thẻ từ blog của mình: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

[Chỉnh sửa] cụ thể để câu hỏi của bạn, đây có thể là bài viết phù hợp .. http://blogs.msdn.com/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx

+0

Không hoàn toàn. Những bài viết này là về phương sai kiểu tham số. Câu hỏi đặt ra là về phương thức trả về kiểu, khác nhau. –

0

Có vẻ như loại điều họ có thể đã sử dụng Generics, nhưng có vẻ như có một lý do chính đáng tại sao họ không làm vậy.

Người ta nói về ở đây:

http://bytes.com/topic/c-sharp/answers/469671-generic-icloneable

Về cơ bản, một giao diện chung mà sẽ cho phép: public class MyClass : IClonable<MyClass>

cũng sẽ cho phép: public class MyClass : IClonable<MyOtherClass>

mà không thực sự cung cấp bất kỳ lợi ích nào và có thể gây nhầm lẫn cho mọi thứ.

+0

Tôi sẽ khẳng định rằng IClonable sẽ là một mô hình thực sự tốt, đặc biệt nếu nó được thừa hưởng ISelf , trong đó bao gồm một thuộc tính "tự" chỉ đọc của loại T. Trong nhiều trường hợp, tôi sẽ tiết kiệm các loại lộ một phương pháp nhân bản công cộng không được kế thừa, nhưng phải có loại cơ sở giống hệt ngoại trừ việc có phương thức Clone được bảo vệ. Bằng cách này, "foo" và "derivedFoo" có thể có cả các dẫn xuất không có khả năng sinh ra, nhưng một thường trình có thể được viết để chấp nhận cả CloneableFoo và CloneableDerivedFoo (vì cả hai đều là ICloneable ). – supercat

8

Hãy để tôi nói lại câu hỏi:

ngôn ngữ như C++ cho phép một phương pháp trọng để có một kiểu trả cụ thể hơn so với phương pháp ghi đè. Ví dụ, nếu chúng ta có các loại

abstract class Enclosure {} 
class Aquarium : Enclosure {} 
abstract class Animal 
{ 
    public virtual Enclosure GetEnclosure(); 
} 

thì đây là không hợp pháp trong C# nhưng mã tương đương sẽ là hợp pháp trong C++:

class Fish : Animal 
{ 
    public override Aquarium GetEnclosure() { ... 

tính năng này là gì của C++ được gọi là gì?

Tính năng này được gọi là "phương sai hiệp phương thức trả lại". (Như một câu trả lời chỉ ra, nó cũng sẽ có thể hỗ trợ "chính thức loại tham số contravariance", mặc dù C + + không.)

Tại sao nó không được hỗ trợ trong C#?

Như tôi đã chỉ ra nhiều lần, chúng tôi không phải cung cấp lý do tại sao tính năng không được hỗ trợ; trạng thái mặc định của tất cả các tính năng là "không được hỗ trợ". Chỉ khi một lượng lớn thời gian và công sức được đưa vào thực hiện một tính năng mới được hỗ trợ. Thay vào đó, các tính năng mà thực hiện là được triển khai phải có lý do cho chúng và lý do chính đáng khi xem xét chi phí để tạo ra chúng là bao nhiêu.

Điều đó nói rằng, có hai "điểm lớn" chống lại tính năng này là những thứ chính ngăn không cho nó hoàn thành.

  1. CLR không hỗ trợ.Để thực hiện công việc này, về cơ bản chúng ta phải thực hiện phương thức khớp chính xác và sau đó thực hiện một phương thức trợ giúp gọi nó. Đó là doable nhưng nó được để được lộn xộn.

  2. Anders nghĩ rằng đó không phải là một tính năng ngôn ngữ rất tốt. Anders là kiến ​​trúc sư trưởng và nếu anh ta nghĩ rằng đó là một tính năng xấu, tỷ lệ cược là tốt của nó sẽ không được thực hiện. (Bây giờ, hãy nhớ bạn, chúng tôi nghĩ rằng các thông số được đặt tên và tùy chọn cũng không đáng giá, nhưng điều đó cuối cùng cũng được hoàn thành. Đôi khi nó trở nên rõ ràng là bạn phải nghiến răng và thực hiện một tính năng mà bạn không thực sự thích tính thẩm mỹ để đáp ứng nhu cầu thực tế.)

Tóm lại, chắc chắn sẽ hữu ích và đây là tính năng được yêu cầu thường xuyên. Tuy nhiên, không chắc chúng ta sẽ làm điều đó. Lợi ích của tính năng này không trả cho chi phí của nó; nó làm phức tạp đáng kể việc phân tích các phương thức ngữ nghĩa, và chúng ta không có cách thực sự dễ dàng để thực hiện nó.

+2

Vì vậy, câu hỏi tiếp theo rõ ràng: * "Tại sao Anders nghĩ rằng phương sai hiệp phương sai trả về là một tính năng xấu?" * – LBushkin

+1

@LBushkin: lần tới gặp anh ta, hãy hỏi anh ta. :-) –

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