2008-11-28 40 views
113

Tôi có lớp cơ sở trừu tượng và tôi muốn khai báo một trường hoặc thuộc tính sẽ có giá trị khác nhau trong mỗi lớp thừa kế từ lớp cha này.Ghi đè các trường hoặc thuộc tính trong lớp con

Tôi muốn xác định nó trong baseclass vì vậy tôi có thể tham chiếu nó trong một phương thức lớp cơ sở - ví dụ ghi đè ToString để nói "Đối tượng này thuộc loại thuộc tính/trường". Tôi đã có ba cách mà tôi có thể thấy để làm điều này, nhưng tôi đã tự hỏi - cách tốt nhất hoặc được chấp nhận để làm điều này là gì? Câu hỏi của người mới, xin lỗi.

Tùy chọn 1:
Sử dụng thuộc tính trừu tượng và ghi đè lên lớp được kế thừa. Lợi ích này được thực thi (bạn phải ghi đè nó) và nó được sạch sẽ. Tuy nhiên, nó cảm thấy hơi sai khi trả về một giá trị mã cứng thay vì đóng gói một trường và nó là một vài dòng mã thay vì chỉ. Tôi cũng phải tuyên bố một cơ thể cho "bộ" nhưng đó là ít quan trọng (và có lẽ là một cách để tránh điều mà tôi không biết).

abstract class Father 
{ 
    abstract public int MyInt { get; set;} 
} 

class Son : Father 
{ 
    public override int MyInt 
    { 
     get { return 1; } 
     set { } 
    } 
} 

Lựa chọn 2
tôi có thể tuyên bố một lĩnh vực công cộng (hoặc một lĩnh vực được bảo vệ) và rõ ràng ghi đè lên nó trong lớp kế thừa. Ví dụ dưới đây sẽ cho tôi một cảnh báo để sử dụng "mới" và tôi có thể có thể làm điều đó, nhưng nó cảm thấy sai và nó phá vỡ đa hình, đó là toàn bộ vấn đề. Không có vẻ như là một ý tưởng tốt ...

abstract class Mother 
{ 
    public int MyInt = 0; 
} 

class Daughter : Mother 
{ 
    public int MyInt = 1; 
} 

Lựa chọn 3
tôi có thể sử dụng một lĩnh vực bảo vệ và thiết lập giá trị trong constructor. Điều này có vẻ khá gọn gàng nhưng dựa vào tôi đảm bảo các nhà xây dựng luôn luôn đặt này và với nhiều nhà xây dựng quá tải luôn luôn có một cơ hội một số đường dẫn mã sẽ không đặt giá trị.

abstract class Aunt 
{ 
    protected int MyInt; 
} 

class Niece : Aunt 
{ 
    public Niece() 
    { 
     MyInt = 1; 
    } 
} 

Đó là một chút của một câu hỏi lý thuyết và tôi đoán câu trả lời đã được lựa chọn 1 vì nó là chỉ an toàn tùy chọn nhưng tôi chỉ nhận được để hiểu thấu với C# và muốn hỏi người này với nhiều kinh nghiệm.

Trả lời

107

Trong số ba giải pháp chỉ Lựa chọn 1đa hình .

Các trường không thể ghi đè. Đó là lý do chính xác tại sao Tùy chọn 2 trả lại mới cảnh báo từ khóa.

Giải pháp cho các cảnh báo không phải là để nối thêm “mới” từ khóa, nhưng để thực hiện Phương án 1.

Nếu bạn cần lĩnh vực của bạn là đa hình bạn cần phải bọc nó trong một tài sản.

Tùy chọn 3 là OK nếu bạn không cần hành vi đa hình. Tuy nhiên, bạn nên nhớ rằng khi vào thời gian chạy thuộc tính MyInt được truy cập, lớp dẫn xuất không có quyền kiểm soát đối với giá trị được trả về. Lớp cơ sở của chính nó có khả năng trả về giá trị này.

Đây là cách thực hiện đa hình thực sự của thuộc tính của bạn có thể xem, cho phép các lớp dẫn xuất ở trong điều khiển .

abstract class Parent 
{ 
    abstract public int MyInt { get; } 
} 

class Father : Parent 
{ 
    public override int MyInt 
    { 
     get { /* Apply formula "X" and return a value */ } 
    } 
} 

class Mother : Parent 
{ 
    public override int MyInt 
    { 
     get { /* Apply formula "Y" and return a value */ } 
    } 
} 
+104

Là một sang một bên, tôi thực sự nghĩ rằng ** Cha ** nên áp dụng công thức "Y", và mẹ, hợp lý, "X". –

0

Tôi muốn sử dụng tùy chọn 3, nhưng có phương thức setMyInt trừu tượng mà các lớp con buộc phải triển khai. Bằng cách này bạn sẽ không có vấn đề của một lớp dẫn xuất quên để thiết lập nó trong constructor.

abstract class Base 
{ 
protected int myInt; 
protected abstract void setMyInt(); 
} 

class Derived : Base 
{ 
override protected void setMyInt() 
{ 
    myInt = 3; 
} 
} 

Nhân tiện, với tùy chọn, nếu bạn không chỉ định được đặt; trong thuộc tính lớp cơ sở trừu tượng của bạn, lớp dẫn xuất sẽ không phải thực hiện nó.

abstract class Father 
{ 
    abstract public int MyInt { get; } 
} 

class Son : Father 
{ 
    public override int MyInt 
    { 
     get { return 1; } 
    } 
} 
0

Bạn có thể chọn tùy chọn 3 nếu bạn sửa đổi lớp cơ sở trừu tượng để yêu cầu giá trị thuộc tính trong hàm tạo, bạn sẽ không bỏ lỡ bất kỳ đường dẫn nào. Tôi thực sự xem xét tùy chọn này.

abstract class Aunt 
{ 
    protected int MyInt; 
    protected Aunt(int myInt) 
    { 
     MyInt = myInt; 
    } 

} 

Tất nhiên, sau đó bạn vẫn có tùy chọn đặt trường ở chế độ riêng tư, tùy theo nhu cầu, để lộ trình bảo vệ thuộc tính được bảo vệ hoặc công khai.

3

Bạn có thể xác định một cái gì đó như thế này:

abstract class Father 
{ 
    //Do you need it public? 
    protected readonly int MyInt; 
} 

class Son : Father 
{ 
    public Son() 
    { 
     MyInt = 1; 
    } 
} 

Bằng cách đặt các giá trị như readonly, nó đảm bảo rằng giá trị cho lớp đó vẫn không thay đổi trong suốt vòng đời của đối tượng.

Tôi cho rằng câu hỏi tiếp theo là: tại sao bạn cần?

+0

Tĩnh là một sự lựa chọn từ ngữ kém vì nó hàm ý giá trị sau đó được chia sẻ giữa tất cả các phiên bản của lớp, tất nhiên là không. –

+0

Đó là sự thật. Tôi sẽ chỉnh sửa nó. – Ant

15

Tùy chọn 2 không khởi động - bạn không thể ghi đè các trường, bạn chỉ có thể ẩn chúng.

Cá nhân, tôi sẽ chọn tùy chọn 1 mỗi lần. Tôi cố gắng giữ cho các trường riêng tư mọi lúc. Đó là nếu bạn thực sự cần để có thể ghi đè lên tài sản ở tất cả, tất nhiên. Một tùy chọn khác là có thuộc tính chỉ đọc trong lớp cơ sở được đặt từ thông số hàm tạo:

abstract class Mother 
{ 
    private readonly myInt; 
    public int MyInt { get { return myInt; } } 

    protected Mother(int myInt) 
    { 
     this.myInt = myInt; 
    } 
} 

class Daughter : Mother 
{ 
    public Daughter() : base(1) 
    { 
    } 
} 

Đó có lẽ là cách tiếp cận phù hợp nhất nếu giá trị không thay đổi trong suốt thời gian của cá thể.

+0

Chúng ta có thể nói điều này bây giờ không chính xác dựa trên http://msdn.microsoft.com/en-us/library/9fkccyh4.aspx Bài viết này cho thấy bạn có thể ghi đè lên các thuộc tính – codingbiz

+1

@codingbiz: câu trả lời của tôi nói về đặc tính ở đâu? Các trường và thuộc tính không giống nhau. –

+0

@codingbiz: (Câu trả lời của tôi * bây giờ * nói về các đặc tính, nhưng nó không bao giờ nói bạn không thể ghi đè chúng. Nó nói - và nói rằng bạn không thể ghi đè * trường *, vẫn đúng.) –

6

tùy chọn 2 là một ý tưởng tồi. Nó sẽ dẫn đến một cái gì đó gọi là bóng; Về cơ bản bạn có hai thành viên "MyInt" khác nhau, một thành viên trong người mẹ, và người kia trong con gái. Vấn đề với điều này, là các phương pháp được thực hiện trong người mẹ sẽ tham khảo "MyInt" của người mẹ trong khi các phương pháp được thực hiện trong con gái sẽ tham chiếu đến "MyInt" của con gái. điều này có thể gây ra một số vấn đề về khả năng đọc nghiêm trọng và sự nhầm lẫn sau đó xuống dòng.

Cá nhân, tôi nghĩ tùy chọn tốt nhất là 3; vì nó cung cấp một giá trị trung rõ ràng, và có thể được tham chiếu trong nội bộ của trẻ em không có những rắc rối của việc xác định các lĩnh vực riêng của họ - đó là vấn đề với lựa chọn 1.

3

Bạn có thể làm điều này

class x 
{ 
    private int _myInt; 
    internal virtual int myInt { get { return _myInt; } set { _myInt = value; } } 
} 

class y : x 
{ 
    private int _myYInt; 
    public override int myInt { get { return _myYInt; } set { _myYInt = value; } } 
} 

ảo cho phép bạn nhận được một tài sản của một cơ thể mà không một cái gì đó và vẫn cho phép phụ lớp ghi đè lên nó.

0

Tôi đã làm điều này ...

namespace Core.Text.Menus 
{ 
    public abstract class AbstractBaseClass 
    { 
     public string SELECT_MODEL; 
     public string BROWSE_RECORDS; 
     public string SETUP; 
    } 
} 

namespace Core.Text.Menus 
{ 
    public class English : AbstractBaseClass 
    { 
     public English() 
     { 
      base.SELECT_MODEL = "Select Model"; 
      base.BROWSE_RECORDS = "Browse Measurements"; 
      base.SETUP = "Setup Instrument"; 
     } 
    } 
} 

Bằng cách này bạn vẫn có thể sử dụng các trường.

+0

Tôi cảm thấy như thế này là tốt đẹp như một giải pháp ad-hoc cho prototyping hoặc demoing. – Zimano

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