2010-10-01 45 views
44

Tôi hơi bối rối về việc ghi đè so với ẩn một phương thức trong C#. Việc sử dụng thực tế của mỗi người cũng sẽ được đánh giá cao, cũng như giải thích cho khi người ta sẽ sử dụng từng loại.Ghi đè so với phương thức ẩn

Tôi bối rối về việc ghi đè - tại sao chúng tôi ghi đè? Những gì tôi đã học được cho đến nay là bằng cách overring chúng tôi có thể cung cấp mong muốn thực hiện một phương pháp của một lớp học có nguồn gốc, mà không thay đổi chữ ký.

Nếu tôi không ghi đè lên phương thức của siêu lớp và tôi thực hiện thay đổi đối với phương thức trong lớp phụ, điều đó có thay đổi phương thức siêu lớp không?

Tôi cũng bối rối về những điều sau đây - điều này thể hiện điều gì?

class A 
{ 
    virtual m1() 
    { 
     console.writeline("Bye to all"); 
    } 
} 

class B : A 
{ 
    override m1() 
    { 
     console.writeLine("Hi to all"); 
    } 
} 

class C 
{ 
    A a = new A(); 
    B b = new B(); 
    a = b; (what is this) 
    a.m1(); // what this will print and why? 

    b = a; // what happens here? 
} 
+0

sau khi đi qua google tôi đã ở đây với các vấn đề của tôi ... – NoviceToDotNet

+0

Có vẻ như bạn cần phải học các khái niệm lập trình C# cơ bản, trước khi bạn có thể hiểu được phương pháp ghi đè và ẩn. Hãy thử một trong rất nhiều C# cuốn sách mới bắt đầu xung quanh. – thecoop

+0

1) a.m1(); // điều này sẽ in và tại sao? Đơn giản là nó sẽ in ''hai thành tất cả' 'bởi vì bạn đang sử dụng khái niệm ghi đè phương pháp. 2) b = a; // chuyện gì xảy ra ở đây thế? Ở đây chúng ta sẽ nhận được một lỗi biên dịch vì đối tượng lớp con sẽ không tham chiếu đến đối tượng lớp cơ sở. Và ngược lại là có thể theo khái niệm Thừa kế. – Krish

Trả lời

101

xem xét:

public class BaseClass 
{ 
    public void WriteNum() 
    { 
    Console.WriteLine(12); 
    } 
    public virtual void WriteStr() 
    { 
    Console.WriteLine("abc"); 
    } 
} 

public class DerivedClass : BaseClass 
{ 
    public new void WriteNum() 
    { 
    Console.WriteLine(42); 
    } 
    public override void WriteStr() 
    { 
    Console.WriteLine("xyz"); 
    } 
} 
/* ... */ 
BaseClass isReallyBase = new BaseClass(); 
BaseClass isReallyDerived = new DerivedClass(); 
DerivedClass isClearlyDerived = new DerivedClass(); 

isReallyBase.WriteNum(); // writes 12 
isReallyBase.WriteStr(); // writes abc 
isReallyDerived.WriteNum(); // writes 12 
isReallyDerived.WriteStr(); // writes xyz 
isClearlyDerived.WriteNum(); // writes 42 
isClearlyDerived.writeStr(); // writes xyz 

Overriding là cách OO cổ điển, trong đó một lớp dẫn xuất có thể có hành vi cụ thể hơn một lớp cơ sở (trong một số ngôn ngữ bạn đã không có lựa chọn nào khác để làm như vậy). Khi một phương thức ảo được gọi trên một đối tượng, thì phiên bản được dẫn xuất nhiều nhất của phương thức này được gọi. Do đó, mặc dù chúng tôi đang xử lý isReallyDerived dưới dạng BaseClass thì chức năng được xác định trong DerivedClass được sử dụng.

Ẩn có nghĩa là chúng tôi có phương pháp hoàn toàn khác. Khi chúng tôi gọi WriteNum() trên isReallyDerived thì không có cách nào để biết rằng có một khác nhau WriteNum() trên DerivedClass do đó nó không được gọi. Nó chỉ có thể được gọi khi chúng ta đang xử lý đối tượng a DerivedClass.

Phần lớn thời gian ẩn là xấu. Nói chung, bạn nên có một phương thức ảo nếu nó có khả năng được thay đổi trong một lớp dẫn xuất và ghi đè nó trong lớp dẫn xuất. Tuy nhiên, có hai điều hữu ích cho:

  1. Tương thích về sau. Nếu DerivedClass có phương thức DoStuff() và sau đó trên BaseClass đã được thay đổi để thêm phương thức DoStuff(), (hãy nhớ rằng chúng có thể được viết bởi những người khác nhau và tồn tại trong các hội đồng khác nhau) thì lệnh cấm ẩn danh sẽ đột nhiên bị DerivedClass lỗi mà không có thay đổi. Ngoài ra, nếu DoStuff() mới trên BaseClass là ảo, thì tự động thực hiện điều đó trên DerivedClass ghi đè lên nó có thể dẫn đến phương thức đã tồn tại trước đó được gọi khi không được. Do đó nó là tốt mà ẩn là mặc định (chúng tôi sử dụng new để làm cho nó rõ ràng chúng tôi chắc chắn muốn ẩn, nhưng để lại nó ra da và phát ra một cảnh báo về biên dịch).

  2. hiệp phương sai của người nghèo. Hãy xem xét phương thức Clone() trên BaseClass trả về một BaseClass mới là bản sao được tạo. Trong ghi đè trên DerivedClass điều này sẽ tạo ra một DerivedClass nhưng trả lại dưới dạng BaseClass, điều này không hữu ích. Những gì chúng tôi có thể làm là để có một bảo vệ ảo CreateClone() được ghi đè.Trong BaseClass, chúng tôi có một số Clone() trả về kết quả của việc này - và tất cả đều tốt - trong DerivedClass, chúng tôi sẽ ẩn số này bằng một số Clone() mới trả về DerivedClass. Gọi số Clone() trên BaseClass sẽ luôn trả về tham chiếu BaseClass, giá trị này sẽ là giá trị BaseClass hoặc giá trị DerivedClass nếu thích hợp. Gọi số Clone() trên DerivedClass sẽ trả lại giá trị DerivedClass, đó là những gì chúng tôi muốn trong ngữ cảnh đó. Có những biến thể khác của nguyên tắc này, tuy nhiên cần lưu ý rằng tất cả chúng đều khá hiếm.

Một điều quan trọng cần lưu ý với trường hợp thứ hai, là chúng ta đã sử dụng một cách chính xác để trốn loại bỏ bất ngờ vào mã gọi, là người sử dụng một cách hợp lý DerivedClass có thể mong đợi nó Clone() để trả về một DerivedClass. Kết quả của bất kỳ cách nào nó có thể được gọi là được giữ phù hợp với nhau. Hầu hết các trường hợp che giấu nguy cơ giới thiệu những bất ngờ, đó là lý do tại sao họ thường cau mày. Điều này được biện minh một cách chính xác bởi vì nó giải quyết được vấn đề mà ẩn thường giới thiệu.

Trong tất cả, ẩn là đôi khi cần thiết, không thường xuyên hữu ích, nhưng nói chung là xấu, vì vậy hãy cảnh giác với nó.

+0

sir nếu tôi sử dụng công cộng ảo void WriteNum() và trong tiểu claass ..... tôi sử dụng công cộng mới virual void WriteNum() những gì phương pháp sẽ được gọi trên isReallyBase.WriteNum(); và y – NoviceToDotNet

+0

@NoviceToDotNet bằng cách sử dụng 'ảo mới' có nghĩa là nó ẩn phiên bản có nguồn gốc nhiều hơn, nhưng bản thân nó có thể là ảo.Nếu bạn gọi 'WriteNum()' trên 'isReallyBase' hoặc' isReallyDerived' thì phiên bản được định nghĩa trong 'BaseClass' sẽ được gọi, vì nó không được ghi đè lên, nhưng nếu bạn gọi nó là' isClearlyDerived' thì phiên bản ẩn sẽ được gọi là. Hơn nữa, bạn có thể có một ghi đè trong một phân lớp nữa (nói "' DerivedFromDerivedClass' ") và ghi đè này sẽ được gọi khi' DerivedFromDerivedClass' được điều khiển thông qua biến 'DerivedFromDerivedClass' hoặc biến' DerivedClass' ... –

+0

và cũng trong trường hợp nếu chúng tôi dot't override phương pháp của bcas siêu lớp nó không bắt buộc thì những gì nó sẽ làm isReallyDerived.WriteStr(); – NoviceToDotNet

23

Overriding là khi bạn cung cấp một override thực hiện mới của một phương pháp trong một lớp hậu duệ khi phương pháp mà được định nghĩa trong lớp cơ sở như virtual.

Ẩn là khi bạn cung cấp một thực hiện mới của một phương pháp trong một lớp hậu duệ khi phương pháp đó là không được định nghĩa trong lớp cơ sở như virtual, hoặc khi thực hiện mới của bạn không xác định override.

Ẩn thường rất tệ; bạn thường nên cố gắng không làm điều đó nếu bạn có thể tránh được nó. Ẩn có thể gây ra những điều bất ngờ xảy ra, bởi vì các phương thức ẩn chỉ được sử dụng khi được gọi trên một biến kiểu thực mà bạn đã định nghĩa, không phải nếu sử dụng tham chiếu lớp cơ sở ... mặt khác, các phương thức ảo bị ghi đè sẽ kết thúc bằng phiên bản phương thức thích hợp được gọi, ngay cả khi được gọi bằng cách sử dụng tham chiếu lớp cơ sở trên lớp con.

Ví dụ, hãy xem xét các lớp:

public class BaseClass 
{ 
    public virtual void Method1() //Virtual method 
    { 
    Console.WriteLine("Running BaseClass Method1"); 
    } 
    public void Method2() //Not a virtual method 
    { 
    Console.WriteLine("Running BaseClass Method2"); 
    } 
} 
public class InheritedClass : BaseClass 
{ 
    public override void Method1() //Overriding the base virtual method. 
    { 
    Console.WriteLine("Running InheritedClass Method1"); 
    } 
    public new void Method2() //Can't override the base method; must 'new' it. 
    { 
    Console.WriteLine("Running InheritedClass Method2"); 
    } 
} 

Hãy gọi nó là như thế này, với một thể hiện của InheritedClass, trong một tài liệu tham khảo phù hợp:

InheritedClass inherited = new InheritedClass(); 
inherited.Method1(); 
inherited.Method2(); 

này trả về những gì bạn nên mong đợi; cả hai phương pháp đều nói rằng họ đang chạy phiên bản InheritedClass.

Chạy InheritedClass Method1
Chạy InheritedClass Method2

Mã này tạo ra một thể hiện của cùng một, InheritedClass, nhưng lưu nó trong một tham chiếu BaseClass:

BaseClass baseRef = new InheritedClass(); 
baseRef.Method1(); 
baseRef.Method2(); 

Thông thường, dưới OOP nguyên tắc, bạn nên mong đợi cùng một đầu ra như ví dụ trên. Nhưng bạn không nhận được cùng một kết quả:

Chạy InheritedClass Method1
Chạy BaseClass Method2

Khi bạn viết mã InheritedClass, bạn có thể đã muốn tất cả các cuộc gọi đến Method2() để chạy mã bạn đã viết trong đó. Thông thường, đây sẽ là cách nó hoạt động - giả sử bạn đang làm việc với phương thức virtual mà bạn đã ghi đè.Nhưng vì bạn đang sử dụng phương thức ẩn là new, nên thay vào đó, nó gọi phiên bản trên tham chiếu bạn đang sử dụng.


Nếu đó là hành vi bạn thực sự muốn, sau đó; có bạn đi. Nhưng tôi mạnh mẽ đề nghị rằng nếu đó là những gì bạn muốn, có thể có một vấn đề kiến ​​trúc lớn hơn với mã.

+0

Tôi sẽ không nói bạn nên _never_ ẩn một phương pháp. Đôi khi, tôi thấy ẩn rất hữu ích (cho, nói, thay đổi giá trị trả lại cho một phân lớp) – thecoop

+0

Đó là lý do tại sao tôi nói bạn nên thử - không bao giờ làm điều đó :) –

+0

Có, nhưng trong trường hợp @thecoop đề cập đến bạn muốn rất cố ý Cố gắng giấu đi. Điều chính tôi nghĩ là thường ẩn có thể đưa ra những bất ngờ khó chịu, nhưng trong việc sử dụng nó cho hiệp phương sai bạn thực sự loại bỏ những điều bất ngờ. –

2

Phương pháp ghi đè đơn giản sẽ ghi đè việc triển khai mặc định phương thức lớp cơ sở trong lớp dẫn xuất.

Phương pháp Ẩn: Bạn có thể sử dụng từ khoá 'mới' trước khi một phương pháp ảo trong một lớp học có nguồn gốc

như

class Foo 
{ 
    public virtual void foo1() 
    { 

    } 
} 

class Bar:Foo 
{ 
    public new virtual void foo1() 
    { 

    } 
} 

bây giờ nếu bạn thực hiện một lớp bar1 mà có nguồn gốc từ Bar, bạn có thể ghi đè lên foo1 được xác định trong Bar.

+0

y có thể sử dụng ảo với ghi đè từ khóa nếu trong ví dụ của bạn làm .... class myclass: bar {sau đó nó sẽ nhận Foo class foo1() hoặc bar class foo1() để ghi đè lên nó} cũng trong lớp bar insted của ẩn foo1() tôi ghi đè lên nó và tôi đặt ảo trước khi nó sau đó nó sẽ được thừa kế để myClass và một lần nữa bằng cách ghi đè lên tôi có thể ghi đè lên phương pháp của lớp Bar foo1() để myClass – NoviceToDotNet

+0

@NoviceToDotNet Không, ảo có thể được sử dụng trong một lớp có nguồn gốc từ Bar, nhưng nó sẽ ghi đè Bar.foo1 và không phải là Foo.foo1. Hỗn hợp ẩn và ảo ở trên là hợp lệ, mặc dù kết hợp cả hai với nhau có lẽ không phải là giải thích tốt nhất về sự khác biệt giữa hai. –

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