2009-07-23 23 views
16

Tôi có lớp trừu tượng, AbsClass triển khai giao diện, IClass. IClass có một số thuộc tính chỉ với Trình truy cập. AbsClass thực hiện các thuộc tính của IClass là thuộc tính trừu tượng được xác định trong các lớp bắt nguồn từ AbsClass.Thêm bộ truy cập vào thuộc tính trong lớp có nguồn gốc từ lớp trừu tượng chỉ có trình truy cập có được

Vì vậy, tất cả các lớp học có nguồn gốc từ AbsClass cũng cần phải đáp ứng iCLASS bởi có tính chất tương tự với accessors Nhận. Tuy nhiên, trong một số trường hợp, tôi muốn có thể thêm bộ truy cập đã đặt vào các thuộc tính từ IClass. Tuy nhiên, nếu tôi cố gắng ghi đè các thuộc tính trừu tượng trong AbsClass với một bộ accessor tôi nhận được lỗi này

ConcClassA.Bottom.Set không thể ghi đè bởi vì AbsClass.Bottom không có một bộ accessor overridable

Xem ConcClassA bên dưới.

Nếu tôi có một lớp chỉ triển khai giao diện IClass, nhưng không kế thừa từ AbsClass thì tôi có thể thêm người truy cập đã đặt có vấn đề. Xem ConcClassB bên dưới.

Tôi chỉ có thể triển khai IClass theo từng dẫn xuất của AbsClass thay vì trực tiếp cho AbsClass. Tuy nhiên, tôi biết từ thiết kế của tôi rằng mỗi AbsClass cũng cần phải là một IClass vì vậy tôi muốn chỉ định rằng cao hơn trong hệ thống phân cấp.

public interface IClass 
{ 
    double Top 
    { 
     get; 
    } 
    double Bottom 
    { 
     get; 
    } 
} 

abstract class AbsClass:IClass 
{ 
    public abstract double Top 
    { 
     get; 
    } 

    public abstract double Bottom 
    { 
     get; 
    } 
} 



class ConcClassA : AbsClass 
{ 
    public override double Top 
    { 
     get { return 1; } 
    } 

    public override double Bottom 
    { 
     get { return 1; } 

     //adding a Set accessor causes error: 
     //ConcClassA.Bottom.Set cannot override because AbsClass.Bottom does not have an overridable set accessor 

     //set { } 
    } 

} 

class ConcClassB : IClass 
{ 
    public double Top 
    { 
     get { return 1; } 
     //added a set accessor to an interface does not cause problem 
     set { } 
    } 
    public double Bottom 
    { 
     get { return 1; } 
    } 
} 

Cập nhật

Vì vậy, tôi nghĩ rằng điều này sẽ có ý nghĩa hơn nếu tôi giải thích chính xác những gì tôi đang cố gắng để làm chứ không phải sau đó sử dụng các ví dụ trừu tượng. Tôi làm việc cho một công ty Kiến trúc và đây là những đối tượng kinh doanh liên quan đến một dự án thiết kế kiến ​​trúc.

Tôi có lớp trừu tượng RhNodeBuilding đại diện cho một loại tòa nhà trên một dự án. Có một số chức năng chung, như khả năng có sàn, được xác định trong RhNodeBuilding. RhNodeBuilding cũng được thừa kế từ một lớp trừu tượng khác cho phép nó là một phần của cấu trúc cây dự án lớn hơn.

RhNodeBuilding cụ từ một giao diện IBuilding trong đó xác định một số chỉ đọc tài sản mà tất cả các tòa nhà sẽ có thể cung cấp như TopElevation, BottomElevation, Chiều cao, NumberOfFloors vv .etc ..Lưu ý rằng có các loại tòa nhà khác không xuất phát từ RhNodeBuilding, nhưng vẫn cần triển khai IBuilding.

Ngay bây giờ tôi có hai loại có nguồn gốc từ RhNodeBuilding: MassBuildingFootPrintBuilding. MassBuilding được xác định bằng hình dạng 3D do người dùng tạo. Hình dạng đó có TopElevationBottomElevation có thể truy cập được thông qua các thuộc tính tương ứng, nhưng bạn không thể chỉnh sửa âm lượng 3D bằng cách thay đổi các thuộc tính.

FootPrintBuilding mặt khác được xác định bằng đường cong khép kín và phạm vi chiều cao để đẩy đường cong đó qua. Vì vậy, không chỉ lớp học có thể trả lại độ cao hiện tại là gì nhưng các độ cao này cũng có thể được thay đổi để xác định lại phạm vi chiều cao.

Tóm lại. Tất cả các tòa nhà (IBuildings) cần để có thể trả về một TopElevationBottomElevation, nhưng không phải tất cả các tòa nhà nên cho phép TopElevation hoặc BottomElevation được thiết lập trực tiếp. Tất cả RhNodeBuildingsIBuildings, và các lớp học có nguồn gốc từ RhNodeBuilding có thể hoặc có thể không cần để có thể trực tiếp đặt TopElevationBottomElevation.

public interface IBuilding 
{ 
    double Top 
    { 
     get; 
    } 
    double Bottom 
    { 
     get; 
    } 
} 

abstract class RhNodeBuilding:IBuilding 
{ 
    public abstract double Top 
    { 
     get; 
    } 

    public abstract double Bottom 
    { 
     get; 
    } 
} 



class MassBuilding: AbsClass 
{ 

    //mass building only returns Top and Bottom properties so it works fine 
    public override double Bottom 
    { 
     get { return 1; } 
    } 

    public override double Top 
    { 
     get { return 1; } 
    } 

} 


class FootPrintBuilding: AbsClass 
{ 
    //Top and Bottom of FootPrintBuilding can both be retrieved and set 
    public override double Top 
    { 
     get { return 1; } 
     //adding a Set accessor causes error: 
     //cannot override because RhNodeBuilding.Top does not have an overridable set accessor 

     //set { } 
    } 

    public override double Bottom 
    { 
     get { return 1; } 

     //adding a Set accessor causes error: 
     //cannot override because RhNodeBuilding.Bottom does not have an overridable set accessor 

     //set { } 
    } 

} 

Ngay bây giờ nó có vẻ như lựa chọn tốt nhất là không có RhNodeBuilding thực hiện IBuilding, nhưng thay vì có mỗi lớp dẫn xuất từ ​​RhNodeBuilding thực hiện IBuilding. Bằng cách đó tôi có thể xác định các thuộc tính từ IBuilding trực tiếp thay vì sau đó là ghi đè.

abstract class AltRhNodeBuilding 
{ 
    public abstract double Top 
    { 
     get; 
    } 
} 


class AltFootPrintBuilding: IClass 
{ 
    public override double Top 
    { 
     get { return 1; } 

     //Can't add set access to overridden abstract property 
     set { } 
    } 

    //No problem adding set accessor to interface property 
    public double Bottom 
    { 
     get { return 1; } 
     set { } 
    } 
} 
+0

Bạn không phá vỡ giao diện "hợp đồng" bằng cách nói "tài sản này đôi khi chỉ đọc nhưng đôi khi không"? Có lẽ bạn cần phải phá vỡ giao diện của bạn (ví dụ như một giao diện khác) để cho phép những lớp đó chỉ cần đọc và những lớp khác cần đọc/ghi. –

+0

Thiếu trình truy cập thuộc tính không ngụ ý rằng thuộc tính chỉ đọc cho mọi người (nghĩa là nó không thay đổi và giá trị của nó không bao giờ thay đổi) - nó chỉ ngụ ý rằng thuộc tính là chỉ đọc cho khách hàng sử dụng giao diện cụ thể này. Vì vậy, không, nó không phải là một thay đổi hợp đồng phá vỡ. Nó chỉ là giới hạn C#/CLR. –

+0

Đồng ý, giao diện hợp đồng chỉ xác định những gì bạn phải thực hiện, không phải những gì bạn không làm. –

Trả lời

10

Điều này hoạt động theo cách của nó vì các thuộc tính không thực sự ảo - phương pháp truy cập của chúng. Do đó, bạn không thể ghi đè set nếu không có ai trong lớp cơ sở.

Những gì bạn có thể làm là ghi đè và bóng triển khai lớp cơ sở và cung cấp các thuộc tính đọc/ghi mới của riêng bạn. Tôi không biết cách nào để làm điều này mà không cần giới thiệu một lớp bổ sung trong hệ thống cấp bậc:

class AbsClassB : AbsClass 
{ 
    public override double Top { get { return ((ConcClassB)this).Top } } 
    public override double Bottom { get { return ((ConcClassB)this).Bottom } } 
} 


class ConcClassB : AbsClassB 
{ 
    new public double Top 
    { 
     get { ... } 
     set { ... } 
    } 

    new public double Bottom 
    { 
     get { ... } 
     set { ... } 
    } 
} 

Một ý tưởng tốt hơn là để xác định một phương pháp ảo được bảo vệ ngay trong AbsClass, và thực hiện Top.getBottom.get về phương pháp đó.Sau đó, bạn có thể ghi đè phương thức trực tiếp trong ConcClassB, và bóng tài sản, mà không cần đến một lớp học thêm:

abstract class AbsClass : IClass 
{ 
    public double Top 
    { 
     get { return GetTop(); } 
    } 

    public double Bottom 
    { 
     get { return GetBottom(); } 
    } 

    protected abstract double GetTop(); 

    protected abstract double GetBottom(); 
} 

class ConcClassB : AbsClass 
{ 
    new public double Top 
    { 
     get { ... } 
     set { ... } 
    } 

    new public double Bottom 
    { 
     get { ... } 
     set { ... } 
    } 

    protected override double GetTop() 
    { 
     return Top; 
    } 

    protected override double GetBottom() 
    { 
     return Bottom; 
    } 
} 
+0

Nhưng ConcClassB cần phải lấy được từ AbsClass –

+0

thêm bạn nên luôn sử dụng định nghĩa trừu tượng (nghĩa là bạn khai báo các biến của một giao diện hoặc tương tự) mở rộng giao diện là một mùi. bạn kết thúc với việc phải khai báo các đối số/biến của một lớp cụ thể -> ý tưởng tồi nếu nó có thể tránh được vì nó có thể trong trường hợp này hoặc bạn cần khai báo kiểu trừu tượng cst với kiểu cụ thể để giữ hoạt động mới. cũng không tốt vì bạn đang vi phạm liskov. Hạn chế các hoạt động iis chấp nhận được thực hiện với Ví dụ: Danh sách thực hiện IList (không có T) nhưng "ẩn" (thực thi rõ ràng) không có phương pháp đã nhập –

+0

Yup, lỗi của tôi - đã đọc sai câu hỏi. Xem phiên bản cập nhật. –

0

tôi loại tò mò là tại sao bạn lại muốn các lớp thực hiện để có phương pháp setter công cộng không phải là một phần của giao diện công khai. Nghe có vẻ như tôi thực sự muốn những điều này bị hạn chế hơn so với công chúng?

Ngoài ra, tôi đang gặp khó khăn khi nghĩ đến một vấn đề với cách tiếp cận này. Họ sẽ "ẩn" bất kỳ thuộc tính nào từ lớp cha, nhưng không có trình định vị thuộc tính nào trong lớp cha, vì vậy có vẻ ổn. Dường như nó có thể là giải pháp đơn giản nhất.

+0

Tôi đã thêm một giải thích ít trừu tượng hơn cho những gì tôi đang làm cho câu hỏi của mình. –

+0

Bạn vẫn gặp phải lỗi này nếu bạn cố gắng thêm bộ chuyển đổi * private * vào thuộc tính có trình thu thập bị ghi đè. – jwg

-13

Sử dụng biến toàn cục.

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