2010-08-31 10 views
8

Tôi vừa mới học cách che dấu một thành viên lớp cơ sở (sử dụng mới) nhưng thiếu điểm là tại sao tôi muốn làm điều đó. Mặt nạ có cung cấp cho chúng tôi một mức độ bảo vệ nhất định như trường hợp trong việc sử dụng đóng gói không? Xin cho biết.Tại sao bạn sẽ che giấu một thành viên lớp cơ sở?

+2

Không chắc chắn tôi hiểu câu hỏi, bạn có thể cung cấp ví dụ về ý nghĩa của * bạn * bằng cách che dấu một thành viên lớp cơ sở. – Lazarus

+0

Mặc dù một lớp dẫn xuất không thể xóa bất kỳ thành viên nào mà nó đã kế thừa, nó có thể ẩn chúng. 1. Để ẩn thành viên được thừa kế, hãy khai báo thành viên mới cùng loại và có cùng tên. 2. Bạn cũng có thể ẩn các thành viên tĩnh. Lợi thế khi ẩn là gì? – Asterix

Trả lời

3

Nó không chỉ được sử dụng để che mặt nạ. Nó thực sự phá vỡ chuỗi thừa kế, vì vậy nếu bạn gọi phương thức lớp cơ sở, phương thức trong lớp dẫn xuất sẽ không được gọi (chỉ là một trong lớp cơ sở).

Về cơ bản, bạn đang tạo phương thức mới không liên quan gì đến phương pháp lớp cơ sở. Do đó, từ khóa "mới".

Lưu ý rằng từ khóa "mới" có thể được sử dụng nếu bạn muốn xác định phương thức có cùng chữ ký với phương thức kiểu cơ sở nhưng có loại trả lại khác.

+0

Nhưng điều đó không thực sự giải thích lý do tại sao bạn muốn làm điều đó. (tức là, tất cả những gì bạn đã làm là cho biết chức năng bạn viết KHÔNG được gọi là gì - nếu nó không được gọi, điểm là gì?) –

+0

Tại sao? Nếu bạn không muốn phương thức mới là một phần của cùng một chuỗi kế thừa của một phương thức ảo có cùng tên và các kiểu tham số. Là một tác dụng phụ, nó cho phép bạn thực hiện lại một phương thức có cùng chữ ký, ngoại trừ kiểu trả về. Miễn là bạn nhớ rằng nó sẽ không được gọi khi bạn gọi phương thức của kiểu cơ sở. –

+0

Câu "trả lời phụ" của bạn trả lời câu hỏi. Phần còn lại có thể được thực hiện tốt hơn bằng cách a) cho phương thức một tên khác, hoặc b) không viết gì cả. –

3

Bạn sẽ rất hiếm khi sử dụng "mới" để che dấu thành viên cấp cơ sở.

Nó được sử dụng chủ yếu cho các trường hợp lớp dẫn xuất có thành viên đầu tiên, và sau đó nó được thêm vào lớp cơ sở --- cùng tên cho một mục đích khác. Các new là có mà bạn thừa nhận rằng bạn biết bạn đang sử dụng nó một cách khác nhau. Khi một thành viên cơ sở được thêm vào trong C++, nó chỉ âm thầm kết hợp phương thức hiện có vào chuỗi kế thừa. Trong C#, bạn sẽ phải chọn giữa newoverride, để cho bạn biết điều gì đang xảy ra.

+0

+1 đây là _probably_ cách sử dụng phổ biến nhất, nhưng tôi vẫn không thích nó vì thiếu đa hình phá vỡ ý định (và ai biết điều gì khác) của nguồn gốc. – Marc

3

Các chỉ hợp lệ an toàn ví dụ mà tôi đã đi qua đang được cụ thể hơn với các loại trả lại hoặc cung cấp một bộ accessor trên một bất động sản. Tôi không nói đó là những người duy nhất, nhưng đó là tất cả những gì tôi đã tìm thấy.

Ví dụ, giả sử bạn có một nền tảng rất đơn giản mà trông như thế này:

public abstract class Base 
{ 
    public string Name { get; protected set; } 

    public Base(string name) 
    { Name = name; } 
} 

Bạn có thể có một nguồn gốc mà trông giống như thế này:

public class Derived : Base 
{ 
    public new string Name 
    { 
    get { return base.Name; } 
    set { base.Name = value; } 
    } 

    public Derived(string name) : base(name) 
    { }   
} 

Giả sử quy tắc kinh doanh cho phép này cụ thể Có nguồn gốc để có một tên có thể thay đổi, tôi tin rằng điều này là chấp nhận được. Vấn đề với new là nó thay đổi hành vi tùy thuộc vào loại cá thể được xem như. Ví dụ: nếu tôi muốn nói:

Derived d = new Derived("Foo"); 
d.Name = "Bar"; 
Base b = d; 
b.Name = "Baz"; // <-- No set available. 

Trong ví dụ nhỏ, chúng tôi không sao. Chúng tôi đang ghi đè hành vi với new, nhưng không phải là một cách phá vỡ. Thay đổi kiểu trả về yêu cầu một chút khéo léo hơn. Cụ thể là, nếu bạn sử dụng new để thay đổi loại trả về trên một loại có nguồn gốc, bạn không nên cho phép loại đó được đặt bởi cơ sở. Kiểm tra ví dụ này:

public class Base 
{ 
    public Base(Base child) 
    { Child = child; } 

    public Base Child { get; private set; } 
} 

public class Derived 
{ 
public Derived(Derived child) : base(child) 
{ } 

public new Derived Child 
{ get { return (Derived)base.Child; } } 

} 

Nếu tôi có thể set Child trên lớp Base, tôi có thể có một vấn đề đúc trong lớp Derived. Một ví dụ khác:

Derived d = new Derived(someDerivedInstance); 
Base b = d; 
var c = b.Child; // c is of type Base 
var e = d.Child; // e is of type Derived 

Tôi không thể phá vỡ bất cứ quy tắc kinh doanh bằng cách xử lý tất cả các Derived lớp học của tôi như Căn cứ, nó chỉ là thuận tiện để không gõ kiểm tra và dàn diễn viên.

0

Điều này thực sự được gọi là ẩn thành viên.Có một vài trường hợp phổ biến mà điều này có thể được sử dụng một cách thích hợp.

  • Nó cho phép bạn làm việc xung quanh các vấn đề về phiên bản trong đó tác giả cơ sở hoặc lớp dẫn xuất vô tình tạo tên thành viên va chạm với số nhận dạng hiện tại.
  • Nó có thể được sử dụng để mô phỏng hiệp phương sai trên các loại trả về.

Về điểm đầu tiên ... có thể là tác giả của lớp cơ sở sau này có thể thêm thành viên có cùng tên với thành viên hiện tại trong lớp dẫn xuất. Tác giả lớp cơ sở có thể không có kiến ​​thức về các lớp dẫn xuất và do đó không có kỳ vọng rằng cô ấy nên tránh xung đột tên. C# hỗ trợ sự tiến hóa độc lập của phân cấp lớp bằng cách sử dụng các cơ chế ẩn.

Về điểm thứ hai ... bạn có thể muốn một lớp thực hiện giao diện quy định chữ ký phương thức nhất định và do đó bạn bị khóa vào các phiên bản trả về của một loại nhất định trong khi đồng thời bạn đã phân loại loại đó và thực sự muốn người gọi xem loại bê tông thay thế. Hãy xem xét ví dụ này.

public interface IFoo { } 

public class ConcreteFoo { } 

public abstract class Base 
{ 
    private IFoo m_Foo; 

    public Base(IFoo x) { m_Foo = x; } 

    public IFoo Foo { get { return m_Foo; } } 
} 

public class Derived 
{ 
    public Derived(ConcreteFoo x) : base(x) { } 

    public new ConcreteFoo Foo { get { return (ConcreteFoo)base.Foo; } } 
} 
1

Điều bạn đang đề cập đến được gọi là Name Hiding. Nó chủ yếu là một tính năng tiện lợi. Nếu bạn kế thừa từ một lớp mà bạn không kiểm soát nguồn bằng cách sử dụng new sẽ cho phép bạn thay đổi hành vi của một phương thức ngay cả khi nó không được khai báo là ảo (hoặc hoàn toàn thay đổi chữ ký nếu nó là ảo). Từ khóa new chỉ đơn giản là ngăn chặn một cảnh báo trình biên dịch. Về cơ bản, bạn đang thông báo cho trình biên dịch rằng bạn đang cố ý che giấu phương thức từ một lớp cha.

Delphi có từ khóa reintroduce vì cùng một lý do.

Điều này khiến bạn mua gì khác hơn là cảnh báo bị chặn? Không phải là một toàn bộ rất nhiều. Bạn không thể truy cập phương thức new từ lớp cha. Bạn có thể truy cập nó từ một giao diện nếu lớp con của bạn trực tiếp triển khai thực hiện giao diện (như được kế thừa để kế thừa nó từ lớp cha của nó). Bạn vẫn có thể gọi cho thành viên của lớp cha từ đứa trẻ. Bất kỳ hậu duệ bổ sung nào của lớp học của bạn sẽ kế thừa thành viên new chứ không phải là thành viên trong phụ huynh.

3

Tôi vừa học được cách để che giấu thành viên lớp cơ sở (sử dụng mới)

FYI tính năng này thường được gọi là "ẩn" hơn là "mặt nạ". Tôi nghĩ rằng "mặt nạ" như thanh toán bù trừ bit trong một mảng bit.

thiếu điểm là tại sao tôi muốn làm điều đó.

Thông thường bạn không muốn. Đối với một số lý do để sử dụng và không sử dụng tính năng này, xem bài viết của tôi về đề tài này từ năm 2008:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx

Có mặt nạ cung cấp cho chúng ta một mức độ nhất định bảo vệ như trường hợp trong việc sử dụng đóng gói?

Không, không.