2010-08-18 63 views
21

Tôi muốn làm điều này:Thừa kế giao diện: có thể mở rộng các thuộc tính có thể không?

interface IBase 
{ 
    string Property1 { get; } 
} 

interface IInherited : IBase 
{ 
    string Property1 { get; set; } 
} 

Vì vậy mà IInherited sẽ có thừa hưởng tài sảnProperty1 với tính năng bổ sung để cho phép set.

Điều đó có khả thi không? Cú pháp là gì?

CHỈNH SỬA: vui lòng lưu ý tôi đặt từ "được thừa kế" bằng khuôn mặt đậm. Tôi yêu cầu cụ thể về kế thừa tài sản, không giấu nó đằng sau một tài sản mới.

+3

Tôi nghĩ có sự nhầm lẫn ở đây giữa thừa kế các lớp và kế thừa giao diện. Một giao diện không thực sự kế thừa một giao diện khác trong cùng một lớp là thừa hưởng một lớp khác. Tất cả các mã của bạn được nói là IInherited cũng thực hiện IBase. Mã này là tốt, nó chỉ làm tăng một cảnh báo trình biên dịch bởi vì bạn đang thay đổi chữ ký của Property1 trong giao diện. Bạn có thể xóa điều này bằng mới. –

+0

Như đã lưu ý bởi nhiều người khác, cú pháp này là không thể, nhưng tôi cảm thấy rằng nó nên được. Nếu bạn sử dụng Reflection trên Property1 thì sẽ có một phương thức Property1_get và một phương thức Property1_set, vì vậy về mặt logic có vẻ như bạn sẽ có thể thực hiện chúng một cách riêng biệt. Và việc thiếu khả năng làm như vậy chắc chắn sẽ khiến tôi lặp lại mã đôi khi (hoặc đưa lên từ khóa mới). – cedd

Trả lời

24

Nếu thực tế là cách duy nhất để làm điều này là bằng cách sử dụng các từ khóa new phiền bạn, sau đó theo ý kiến ​​của tôi bạn đang suy nghĩ về giao diện sai.

Chắc chắn, bạn có thể nói rằng IInherited "được kế thừa từ" IBase; nhưng điều đó thực sự là gì có nghĩa là? Đây là những giao diện; họ thiết lập các hợp đồng mã. Bằng cách ẩn thuộc tính IBase.Property1 với new string Property1 { get; set; }, bạn không bị che khuất bất kỳ chức năng nào. Vì vậy, lý do truyền thống mà rất nhiều nhà phát triển coi là ẩn là một điều "xấu" - rằng nó vi phạm đa hình - là không liên quan trong trường hợp này.

Hãy tự hỏi: những gì thực sự vấn đề khi nói đến giao diện? Họ cung cấp một đảm bảo đáp ứng các cuộc gọi phương thức nhất định, phải không?

Vì vậy, với hai giao diện sau:

interface IBase 
{ 
    string Property1 { get; } 
} 

interface IInherited : IBase 
{ 
    new string Property1 { set; } 
} 
  1. Nếu một đối tượng thực hiện IBase, bạn có thể đọc tài sản Property1 của nó.
  2. Nếu một đối tượng thực hiện IInherited, bạn có thể đọc thuộc tính Property1 của nó (giống như thực hiện IBase) và bạn cũng có thể ghi vào đó.

Một lần nữa, thực sự không có vấn đề gì ở đây.

+4

Bạn là chính xác, ngoại trừ trong trường hợp thực hiện rõ ràng. Bạn có thể có các triển khai khác nhau cho mỗi phiên bản, do đó nó vẫn đang ẩn. Có một tiềm năng mà một người nào đó thực hiện giao diện của bạn có thể làm điều này, hoặc là vô tình hoặc có mục đích, vì vậy bạn nên cẩn thận cách bạn tiêu thụ các giao diện này. Nếu bạn muốn tránh vấn đề này, và vẫn còn sử dụng thừa kế, sau đó sử dụng "mới", nhưng không thêm một getter thứ hai. –

+0

@Merlyn, đã đồng ý. Bằng cách xóa câu lệnh nhận thứ 2, điều này sẽ không còn vi phạm LSP nữa. –

+1

Mã của bạn khai báo hai * getters * khác nhau cho thuộc tính. Trong 'IInherited' bạn có thể muốn khai báo chỉ' chuỗi mới Property1 {set; } '. Một lớp thực hiện 'IInherited' có thể có một thuộc tính với getter và setter và nó sẽ thực hiện cả hai giao diện. – Timwi

1

Mã của bạn nên hoạt động ... nó chỉ tạo cảnh báo complier vì ẩn Thuộc tính1. Để xóa dấu cảnh báo này Property1 trong IInherited với tiền tố mới

0

Bạn có thể đánh dấu các tài sản với từ khóa "mới", hoặc bạn có thể bỏ qua thừa kế:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited : IBase 
{ 
    new string Property1 { get; set; } 
} 

Hoặc:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited 
{ 
    string Property1 { get; set; } 
} 

Dù bằng cách nào, điều này sẽ làm việc:

public class SomeClass : IInherited, IBase 
{ 
    public string Property1 
    { 
     get 
     { 
      // ... 
     } 
     set 
     { 
      // ... 
     } 
    } 
} 

bạn có thể muốn suy nghĩ thật kỹ trước khi bạn thực hiện một chuỗi kế thừa cho các giao diện của bạn, mặc dù. Ai sẽ xem giao diện nào? Bạn có cần truyền tới IInherited khi thông qua IBase không? Nếu vậy, bạn có thể được đảm bảo rằng bạn có thể làm điều đó diễn viên (nếu bạn cho phép người dùng tạo ra các lớp học, sau đó câu trả lời là không)? Kiểu thừa kế này thực sự có thể làm tổn thương (tái) khả năng sử dụng nếu bạn không cẩn thận.

1

Không rõ ràng, không. Bạn có hai lựa chọn:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited : IBase 
{ 
    void SetProperty1(string value); 
} 

Hoặc bạn chỉ có thể giết chết các trình biên dịch cảnh báo với từ khóa new:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited : IBase 
{ 
    new string Property1 { get; set; } 
} 

Trừ khi bạn thực hiện một cách rõ ràng IInherited.Property1, IBase sẽ ràng buộc để thực hiện settable của bạn tự động.

+0

Khi triển khai, chỉ có 1 thuộc tính được gọi là Property1. Nếu bạn truy cập vào thực hiện thông qua một tham chiếu đến IInherited bạn có thể gọi getter và setter, và nếu bạn truy cập nó thông qua IBase bạn chỉ có thể gọi getter. Nhưng đây là hành vi cần thiết phải không? –

+0

Vâng, đó là những gì tôi muốn. Nhưng tôi có lẽ sẽ phải đi với các phương thức getter/setter, vì có vẻ như điều này không thể được thực hiện ... – Malki

+0

Nó có thể được thực hiện, chính xác như bạn đang làm ... bạn nhận được một lỗi trình biên dịch? Lỗi là gì? –

1

Rất tiếc là không thể mở rộng thuộc tính như vậy. Tuy nhiên, bạn chỉ có thể che giấu tài sản bằng cách sử dụng mới:

interface IInherited : IBase 
{ 
    // The new is actually unnecessary (you get warnings though), hiding is automatic 
    new string Property1 { get; set; } 
} 

Hoặc, bạn có thể thực hiện phương thức getter và setter riêng phương pháp của bạn có thể được overriden (tốt 'ol Java phong cách):

interface IBase 
{ 
    string GetProperty1(); 
} 
interface IInherited : IBase 
{ 
    void SetProperty1(string str); 
} 

Thuộc tính thực sự được chuyển đổi thành các phương thức getter và setter bởi trình biên dịch.

1

Ẩn thành viên đang vi phạm Nguyên tắc thay thế Liskov và khá nhiều việc không nên thực hiện, bao giờ hết. Bằng cách ẩn thành viên này, bạn đang giới thiệu một lỗi rất khó xác định vị trí vì 2 kết quả khác nhau sẽ xảy ra tùy thuộc vào việc bạn cast đối tượng như IBase1 hay cast nó vào IBase.

http://en.wikipedia.org/wiki/Liskov_substitution_principle

+2

-1. Đây không phải là ẩn trong ý nghĩa truyền thống, vì giao diện không hỗ trợ đa hình anyway. –

+1

Adam: Tôi nghĩ rằng Chris là chính xác, IFF chúng tôi đặt bình luận của mình trong bối cảnh của các giao diện thực hiện một cách rõ ràng, với các triển khai khác nhau. –

+2

Một lớp thực hiện IInherited có thể được gán như IBase với quyền ít hơn để sửa đổi nó, nó không ghi đè việc thực hiện một phương thức, vì getter sẽ trả về giống nhau (như hai giao diện 'chia sẻ' triển khai getter) –

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