2008-10-01 30 views
32

Theo như tôi biết nó không thể làm như sau trong C# 2,0Tôi có thể ghi đè với các loại có nguồn gốc không?

public class Father 
{ 
    public virtual Father SomePropertyName 
    { 
     get 
     { 
      return this; 
     } 
    } 
} 

public class Child : Father 
{ 
    public override Child SomePropertyName 
    { 
     get 
     { 
      return this; 
     } 
    } 
} 

tôi workaround vấn đề bằng cách tạo ra các tài sản trong lớp có nguồn gốc là "mới", nhưng tất nhiên đó không phải là đa hình.

public new Child SomePropertyName 

Có giải pháp nào trong 2.0 không? Còn bất kỳ tính năng nào trong 3.5 giải quyết vấn đề này thì sao?

+0

Tất cả các giải pháp ngoại trừ giải pháp cho biết bạn không thể thực sự không giúp được tình huống. Tôi nghĩ rằng điểm là để có thể có một lớp học trẻ em (đúc như một người cha) và gọi SomeProperty và có trả lại một đứa trẻ mà không có một diễn viên.Tất nhiên tôi đoán tại mục đích thực sự như evryone khác :) – mattlant

+0

Mặc dù không có giải pháp hoàn hảo tồn tại, ý tưởng là để tìm ra cách rõ ràng nhất để thực hiện điều này, với những thứ ồn ào nhất, như phôi, kiểm tra các loại bằng cách sử dụng "là" và vân vân. –

+3

Mặc dù bạn không thể làm điều này trong C#, tôi đã viết một loạt các bài đăng trên blog về cách thực hiện chính xác điều này: http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/14 /93495.aspx – thecoop

Trả lời

18

Điều này không thể thực hiện được trong bất kỳ ngôn ngữ .NET nào vì những lo ngại về an toàn kiểu. Trong các ngôn ngữ kiểu an toàn, bạn phải cung cấp hiệp phương sai cho các giá trị trả về và đối xứng cho các tham số. Hãy mã này:

class B { 
    S Get(); 
    Set(S); 
} 
class D : B { 
    T Get(); 
    Set(T); 
} 

Đối với Get phương pháp, hiệp phương sai có nghĩa là T phải hoặc là S hoặc một loại có nguồn gốc từ S. Nếu không, nếu bạn có một tham chiếu đến một đối tượng kiểu D được lưu trữ trong một biến được gõ B, khi bạn gọi là B.Get() bạn sẽ không nhận được một đối tượng đại diện như là một S quay lại - phá vỡ hệ thống kiểu.

Đối với phương thức Set, sự đối nghịch có nghĩa là T phải là S hoặc loại S xuất phát từ đó. Nếu không, nếu bạn có tham chiếu đến đối tượng thuộc loại D được lưu trữ trong một biến được nhập B, khi bạn gọi là B.Set(X), trong đó X là loại S nhưng không thuộc loại T, D::Set(T) sẽ nhận được đối tượng thuộc loại không mong đợi. Trong C#, đã có quyết định không cho phép thay đổi loại khi quá tải thuộc tính, ngay cả khi chúng chỉ có một cặp getter/setter, vì nó sẽ có hành vi rất không nhất quán ("Ý của bạn là, tôi có thể thay đổi loại trên một với một getter, nhưng không phải với một getter và setter? Tại sao không?!? " - Anonymous Alternate Universe Newbie).

+3

Điểm của bạn là hợp lệ, nhưng ví dụ này có thuộc tính chỉ đọc. – Anthony

+0

Được minh chứng hoàn hảo! –

+1

Bạn đang giới thiệu một ràng buộc thực sự rằng 'Get' phải trả về cùng' T' mà 'Set' nhận được. Không có ràng buộc này, đối số của bạn không giữ. Xem câu trả lời này để có giải thích tốt hơn: http://stackoverflow.com/questions/5709034/does-c-sharp-support-return-type-covariance – lex82

1

No. C# không hỗ trợ ý tưởng này (được gọi là "trả về hiệp phương sai"). Tuy nhiên, bạn có thể thực hiện việc này:

public class FatherProp 
{ 
} 

public class ChildProp: FatherProp 
{ 
} 


public class Father 
{ 
    public virtual FatherProp SomePropertyName 
    { 
     get 
     { 
      return new FatherProp(); 
     } 
    } 
} 


public class Child : Father 
{ 
    public override FatherProp SomePropertyName 
    { 
     get 
     { 
      // override to return a derived type instead 
      return new ChildProp(); 
     } 
    } 
} 

tức là sử dụng hợp đồng được xác định bởi lớp cơ sở, nhưng trả về loại có nguồn gốc. Tôi đã thực hiện một mẫu chi tiết hơn để làm cho điểm này rõ ràng hơn - trở về "điều này" một lần nữa sẽ không thay đổi bất cứ điều gì. Có thể (nhưng lộn xộn) để kiểm tra đối tượng được trả về cho loại thực tế của nó (tức là "nếu someObject là ChildProp"), nhưng tốt hơn nên gọi phương thức ảo trên nó làm điều đúng cho loại của nó.

Phương thức ảo lớp cơ sở (trong trường hợp này là thuộc tính ảo) không chỉ có triển khai mà còn xác định hợp đồng: lớp con có thể cung cấp triển khai khác SomePropertyName nếu đáp ứng hợp đồng này (tức là SomePropertyName trả về đối tượng của loại "ChaProp"). Trả về một đối tượng thuộc loại "ChildProp" có nguồn gốc từ "FatherProp" đáp ứng hợp đồng này. Nhưng bạn không thể thay đổi hợp đồng trong "Trẻ em" - hợp đồng này áp dụng cho tất cả các lớp học từ "Cha".

Nếu bạn lùi lại một bước và nhìn vào thiết kế rộng hơn, có các cấu trúc ngôn ngữ khác trong bộ công cụ C# mà bạn cũng có thể muốn suy nghĩ - Generics hoặc giao diện.

+0

Không chắc chắn nó có giá trị một câu trả lời khác, nhưng trong C++, bạn có thể thay đổi kiểu trả về để trả về Child thay vì Cha vì các kiểu trả về được phép chuyên sâu hơn trong các hàm ảo lớp dẫn xuất. Tuy nhiên, lý do duy nhất là vì chúng có thể được sử dụng làm kiểu cơ sở để nó thừa thừa :) – workmad3

+0

Java cũng cho phép điều này là 1,5. Nó được gọi là hiệp phương sai kiểu trả về. –

+0

Jon Skeet: cảm ơn thông tin đó, tôi đã thêm nó. – Anthony

39

Bạn có thể khai báo lại (mới), nhưng bạn không thể khai báo lại và ghi đè cùng một lúc (có cùng tên). Một lựa chọn là sử dụng một phương pháp bảo vệ để ẩn các chi tiết - điều này cho phép cả hai đa hình và ẩn cùng một lúc:

public class Father 
{ 
    public Father SomePropertyName 
    { 
     get { 
      return SomePropertyImpl(); 
     } 
    } 
    protected virtual Father SomePropertyImpl() 
    { 
     // base-class version 
    } 
} 

public class Child : Father 
{ 
    public new Child SomePropertyName 
    { 
     get 
     { // since we know our local SomePropertyImpl actually returns a Child 
      return (Child)SomePropertyImpl(); 
     } 
    } 
    protected override Father SomePropertyImpl() 
    { 
     // do something different, might return a Child 
     // but typed as Father for the return 
    } 
} 
+9

Tôi sử dụng thủ thuật này mọi lúc, và tôi ghét nó :) – Trillian

7

Từ Wikipedia:

Trong C# ngôn ngữ lập trình, hỗ trợ cho cả hai return-type hiệp phương sai và tham số contravariance cho các đại biểu đã được thêm trong phiên bản 2.0 của ngôn ngữ. Không phải hiệp phương sai cũng không đối nghịch được hỗ trợ để ghi đè phương pháp.

Nó không nói rõ ràng về hiệp phương sai của các thuộc tính.

+6

thuộc tính chỉ là hai phương pháp (set_Foo và get_Foo) – sehe

11

Không, nhưng bạn có thể sử dụng Generics trong 2 trở lên:

public class MyClass<T> where T: Person 
{ 
    public virtual T SomePropertyName 
    { 
     get 
     { 
      return ...; 
     } 
    } 
} 

Sau đó, Cha và Con là phiên bản generic của lớp cùng

+1

Không hoàn toàn giống nhau. Theo mã của mình, điều này: Cha con = new Child(); Child newChild = child.SomePropertyName; sẽ yêu cầu dàn diễn viên. Với máy của bạn, nó thậm chí sẽ không biên dịch. –

+0

Bạn nói đúng, không phải vậy. Đây chỉ là một ví dụ khá cơ bản về một cái gì đó tương tự - bạn có thể không hoàn toàn phù hợp với mã của mình với Generics, tôi nghĩ rằng bạn sẽ nhận được một tham chiếu kiểu vòng tròn. – Keith

2

Bạn có thể tạo một giao diện chung cho cha và đứa con và trở lại một loại giao diện đó.

+0

Đây là câu trả lời ngắn và lười biếng. Nhưng đó là câu trả lời simpelest mà làm việc trong trường hợp này, tôi thích nó :-) +1 – Mendelt

+0

Câu trả lời lười biếng nhất là: Vì Child cũng thuộc kiểu Cha, bạn có thể trả lại con mà không cần thay đổi kiểu trả về của thuộc tính. ;-) – VVS

+0

Hoặc .. "Phương thức hiệp phương sai kiểu trả về không tồn tại trong C#" – VVS

0

No. C# không hỗ trợ ý tưởng này (được gọi là "kiểu trả về hiệp phương sai").

Từ Wikipedia:

Trong C# ngôn ngữ lập trình, hỗ trợ cho cả hai trở lại kiểu hiệp phương sai và tham số contravariance cho các đại biểu đã được bổ sung trong phiên bản 2.0 của ngôn ngữ. Không phải hiệp phương sai cũng không đối nghịch được hỗ trợ để ghi đè phương pháp.

Bạn có thể khai báo lại (mới), nhưng bạn không thể khai báo lại và ghi đè tại số cùng một lúc (có cùng tên). Một tùy chọn là sử dụng một phương pháp bảo vệ để ẩn chi tiết - điều này cho phép cả hai đa hình và ẩn cùng thời gian:

Các giải pháp tốt nhất là nên sử dụng Generics:

public class MyClass<T> where T: Person 
{ 
    public virtual T SomePropertyNameA 
    {   
     get { return ...; }  
    } 
}//Then the Father and Child are generic versions of the same class 
0

này là gần nhất tôi có thể đi (cho đến nay):

public sealed class JustFather : Father<JustFather> {} 

    public class Father<T> where T : Father<T> 
    { public virtual T SomePropertyName 
     { get { return (T) this; } 
     } 
    } 

    public class Child : Father<Child> 
    { public override Child SomePropertyName 
     { get { return this; } 
     } 
    } 

Nếu không có lớp JustFather, bạn không thể khởi tạo một Father<T> trừ khi đó là một số loại có nguồn gốc khác.

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