2012-06-14 8 views
18

Khi kế thừa một lớp được thừa kế, hành vi mới/ghi đè không phải là những gì tôi mong đợi:Trường hợp sử dụng cho tính riêng tư kế thừa này là gì?

$ cat Program.cs 
using System; 

class A { 
    public virtual void SayHi() { 
     Console.WriteLine("From A"); 
    } 
} 
class B : A { 
    public new virtual void SayHi() { 
     Console.WriteLine("From B"); 
    } 
} 
class C : B { 
    public override void SayHi() { 
     Console.WriteLine("From C"); 
    } 
} 

public class Program { 
    public static void Main() { 
     A p = new C(); 
     p.SayHi(); 
    } 
} 

$ ./Program.exe 
From A 

Như lớp C override phương thức sayHi() Tôi mong chờ đầu ra được From C. Tại sao công cụ sửa đổi new của lớp B được ưu tiên ở đây? Trường hợp sử dụng cho điều đó là gì? Đặc biệt là khi nó phá vỡ trường hợp sử dụng rõ ràng của việc C ghi đè thực sự A.

Lưu ý rằng mã trên được chạy trên Mono 2.10 chạy trên bản phân phối gốc Debian. Nhưng tôi đã xác nhận hành vi tương tự bằng cách sử dụng trình biên dịch C# trong MS Visual Studio.

+2

'new' từ 'b' được giấu sự quá tải ảo trong 'C'. Thay đổi 'p' thành kiểu' B' và bạn sẽ thấy đầu ra từ 'C' – asawyer

+0

Câu hỏi đặt ra là" trường hợp sử dụng cho cái này là gì? " nhưng câu hỏi thực sự được hỏi là "tại sao nó lại làm điều này?". Tôi tò mò về trường hợp sử dụng cho điều này là - cố ý đánh bại thừa kế theo cách không rõ ràng đối với người tiêu dùng API của bạn có vẻ vô cùng nguy hiểm. – Chuu

+0

@Chuu: Cảm ơn, tôi đã làm rõ. – dotancohen

Trả lời

21

new modifier làm cho thành viên ẩn, phá vỡ mối quan hệ đa hình trong phân cấp lớp của bạn. Phương pháp SayHi của B được coi là riêng biệt (không ghi đè) từ A 's (do đó, lựa chọn từ “mới” làm từ khóa). Phương pháp của C sau đó ghi đè B, không phải là A ’(vẫn bị ẩn).

Vì vậy, khi bạn gọi SayHi về một trường hợp C thông qua một tài liệu tham khảo A, bộ thực thi sẽ giải quyết nó chống lại các loại A, không phải là C loại (trong đó SayHi là một phương pháp “mới” thừa hưởng từ B).

Nếu, mặt khác, bạn đã chạy:

B p = new C(); 
p.SayHi(); 

... bạn sẽ có được kết quả đa hình dự kiến:

From C 

Sửa: Kể từ khi bạn yêu cầu một use-case , đây là một. Trước khi giới thiệu các generic trong .NET Framework 2.0, việc ẩn thành viên đôi khi được sử dụng như một phương tiện thay đổi kiểu trả về của các phương thức kế thừa trong các lớp dẫn xuất (một cái gì đó bạn không thể làm khi ghi đè) để trả về các kiểu cụ thể hơn. Ví dụ:

class ObjectContainer 
{ 
    private object item; 

    public object Item 
    { 
     get { return item; } 
     set { item = value; } 
    } 
} 

class StringContainer : ObjectContainer 
{ 
    public new virtual string Item 
    { 
     get { return base.Item as string; } 
     set { base.Item = value as string; } 
    } 
} 

class QuotedStringContainer : StringContainer 
{ 
    public override string Item 
    { 
     get { return "\"" + base.Item + "\""; } 
    } 
} 

Thuộc tính của lớp ObjectContainerItem trả về một đồng bằng object. Tuy nhiên, trong StringContainer, thuộc tính được kế thừa này bị ẩn để trả về một số string thay thế. Như vậy:

ObjectContainer oc = new StringContainer(); 
object o = oc.Item; // Valid, since ObjectContainer.Item is resolved 
string s1 = oc.Item; // Not valid, since ObjectContainer.Item is still resolved 
string s2 = ((StringContainer)oc).Item; 
         // Valid, since StringContainer.Item is now resolved 

Lớp QuotedStringContainer đè Item tài sản của StringContainer, kế thừa kiểu string trở lại của nó; tuy nhiên, nó là vẫn còn bị ẩn khỏi tài sản object -returning Item của ObjectContainer. Nếu nó không phải là cách này, sẽ không có cách dung hòa các loại lợi nhuận khác nhau của họ ...

ObjectContainer oc = new QuotedStringContainer(); 
object o = oc.Item; // Valid, since ObjectContainer.Item is resolved 
string s1 = oc.Item; // Not valid, since ObjectContainer.Item is still resolved 
string s2 = ((StringContainer)oc).Item; 
         // Valid, since QuotedStringContainer.Item is now resolved 
         // (polymorphism!) 
string s3 = ((QuotedStringContainer)oc).Item; 
         // Valid, since QuotedStringContainer.Item is now resolved 
+0

Cảm ơn bạn. Tôi hiểu làm thế nào thay đổi B sẽ chạy phương pháp của C, nhưng có một trường hợp sử dụng để phá vỡ mối quan hệ đa hình giữa A và C? Lập trình guy C có thể có lý do chính đáng để ghi đè phương thức của A, nhưng tôi không thấy trường hợp sử dụng để ngăn chặn điều đó, cho dù B được định nghĩa là 'new' hay' override'. – dotancohen

+1

Vì 'C' xuất phát từ' B' xuất phát từ 'A', không có mối quan hệ đa hình trực tiếp giữa' C' và 'A', nhưng chỉ có một mối gián tiếp thông qua' B'. Nếu chuỗi 'B' – 'A' bị hỏng, thì' C' không còn xác nhận quyền sở hữu đối với 'A', vì' B' đã phá vỡ kênh – Douglas

+0

Tôi hiểu, điều đó có ý nghĩa. Cảm ơn Douglas. – dotancohen

7

C được trọng phiên bản shadowed của phương pháp này (mà đang được shadowed trong B) chứ không phải trọng một trong A .

Kết quả, khi bạn đang sử dụng một biến kiểu A, các SayHi quy định tại A được gọi, như không được ghi đè trong C.

6

C ghi đè phương thức B, vì vậy khi bạn truyền nó đến A, bạn sẽ gọi đến số ảo được xác định trong A.

Xem ECMA 334: C# Language Specification dưới 17.5.3 là khá nhiều ví dụ của bạn (trang 294).

+0

Cảm ơn bạn đã liên kết và đề cập đến trang. Tuy nhiên, tôi vẫn không thấy trường hợp sử dụng cho hành vi này, trong khi trường hợp sử dụng ghi đè A từ C bất kể B là khá rõ ràng. – dotancohen

2

Vì lớp C không ghi đè phương thức SayHi trong lớp A, nó sẽ ghi đè phương thức 'mới' trong B. Vì dàn diễn viên của bạn là A, trình biên dịch giải quyết như là một lời gọi đến A.SayHi() thay vì C.SayHi()

2

Cuối cùng example từ trang msdn này giải thích sát với những gì đang xảy ra ở đây. Về cơ bản, công cụ sửa đổi mới làm cho phương thức trong A bị ẩn khỏi C, và vì nó được công khai khi C ghi đè nó, hãy ghi đè phương thức từ B (được coi là phương pháp riêng biệt của nó).

Nếu bạn thiết lập phương pháp trong B để tư nhân, C một lần nữa sẽ ghi đè phương pháp này trong A.

class B : A 
{ 
    private new void SayHi() 
    { 
     Console.WriteLine("From B"); 
    } 
} 

Kết quả trong: From C

+0

Cảm ơn bạn, nhưng ví dụ đó đề cập đến các phương pháp riêng tư. – dotancohen

+0

@dotancohen Có, bởi vì làm cho phương thức trong 'B' riêng hiển thị phương thức' A' bên dưới thành 'C' được ghi đè, cho kết quả mong đợi. Vì bạn có phương thức 'B' công khai, nó là phương thức được ghi đè. – NominSim