2011-05-28 9 views
55

Tôi khá bối rối giữa một số khái niệm về OOP: virtual, override, newsealed override. Bất cứ ai có thể giải thích sự khác biệt?Sự khác biệt giữa ghi đè ảo, ghi đè, ghi đè mới và bị ghi

Tôi khá rõ ràng rằng nếu phương pháp lớp dẫn xuất được sử dụng, người ta có thể sử dụng từ khóa override để phương thức lớp cơ sở sẽ được ghi đè bởi lớp dẫn xuất. Nhưng tôi không chắc chắn về newsealed override.

Trả lời

74

Từ khóa virtual được sử dụng để sửa đổi phương thức, thuộc tính, chỉ mục hoặc khai báo sự kiện và cho phép nó được ghi đè trong lớp dẫn xuất. Ví dụ, phương thức này có thể được ghi đè bởi bất kỳ lớp nào thừa hưởng nó: Sử dụng công cụ sửa đổi mới để ẩn một thành phần rõ ràng được thừa kế từ một lớp cơ sở. Để ẩn một thành viên được thừa kế, khai báo nó trong lớp dẫn xuất sử dụng cùng một tên và sửa đổi nó với công cụ sửa đổi mới.

Đây là tất cả để làm với đa hình. Khi một phương thức ảo được gọi trên một tham chiếu, kiểu thực tế của đối tượng mà tham chiếu đề cập đến được sử dụng để quyết định việc triển khai thực hiện phương thức nào. Khi một phương thức của một lớp cơ sở được ghi đè trong một lớp dẫn xuất, phiên bản trong lớp dẫn xuất được sử dụng, ngay cả khi mã gọi không "biết" rằng đối tượng là một thể hiện của lớp dẫn xuất. Ví dụ:

public class Base 
{ 
    public virtual void SomeMethod() 
    { 
    } 
} 

public class Derived : Base 
{ 
    public override void SomeMethod() 
    { 
    } 
} 

... 

Base d = new Derived(); 
d.SomeMethod(); 

sẽ kết thúc gọi Derived.SomeMethod nếu điều đó ghi đè Base.SomeMethod.

Bây giờ, nếu bạn sử dụng các từ khóa mới thay vì override, phương pháp trong lớp có nguồn gốc không ghi đè lên các phương pháp trong các lớp cơ sở, nó chỉ ẩn nó. Trong trường hợp đó, mã như sau:

public class Base 
{ 
    public virtual void SomeOtherMethod() 
    { 
    } 
} 

public class Derived : Base 
{ 
    public new void SomeOtherMethod() 
    { 
    } 
} 

... 


Base b = new Derived(); 
Derived d = new Derived(); 
b.SomeOtherMethod(); 
d.SomeOtherMethod(); 

Trước tiên, hãy gọi Base.SomeOtherMethod, sau đó Derived.SomeOtherMethod. Chúng là hai phương thức hoàn toàn tách biệt có cùng tên, chứ không phải phương thức dẫn xuất ghi đè phương thức cơ bản.

Nếu bạn không chỉ định mới hoặc ghi đè, kết quả đầu ra giống như bạn đã chỉ định mới, nhưng bạn cũng sẽ nhận được cảnh báo trình biên dịch (vì bạn có thể không biết rằng mình đang ẩn phương thức trong phương thức lớp cơ sở, hoặc thực sự bạn có thể đã muốn ghi đè lên nó, và chỉ quên bao gồm từ khóa).

Tuyên bố thuộc tính ghi đè có thể bao gồm dấu hiệu sửa đổi được niêm phong. Sử dụng công cụ sửa đổi này ngăn cản một lớp dẫn xuất từ ​​việc ghi đè thêm thuộc tính. Những người truy cập tài sản bị phong ấn cũng được niêm phong.

+0

cảm ơn cho đầu vào .. nhưng một điều không nhận được trong tâm trí của tôi là ..điều gì đang sử dụng Base b = new Derived()? Đây có phải là đối tượng tạo lớp cơ sở hoặc lớp dẫn xuất không ?? –

+2

Lớp có nguồn gốc. Tôi nghĩ bạn phải tìm hiểu thêm về tính đa hình. Đây là một cách tốt để bạn đọc. http://msdn.microsoft.com/en-us/library/ms173152 (v = vs.80) .aspx – CharithJ

+3

@Xor: Trong trường hợp đó, bạn đang tạo một cá thể của đối tượng 'Derived' và lưu trữ tham chiếu trong biến' Base'. Điều này là hợp lệ vì một đối tượng 'Derived' là một đối tượng' Base'. Điều đó giống như nói rằng chúng ta cần một "người" để chúng ta có được "Johnny", người sẽ là một người. Cùng một thỏa thuận ở đây. –

15

Theo mặc định, không thể ghi đè phương thức trong lớp dẫn xuất trừ khi được khai báo virtual hoặc abstract. virtual nghĩa là kiểm tra các triển khai mới hơn trước khi gọiabstract có nghĩa là giống nhau, nhưng nó được đảm bảo bị ghi đè trong tất cả các lớp dẫn xuất. Ngoài ra, không cần thực hiện trong lớp cơ sở vì nó sẽ được định nghĩa lại ở nơi khác.

Trường hợp ngoại lệ ở trên là công cụ sửa đổi new. Phương pháp không được khai báo virtual hoặc abstract có thể được xác định lại bằng công cụ sửa đổi new trong lớp dẫn xuất. Khi phương thức được gọi trong lớp cơ sở, phương thức cơ sở được thi hành và khi được gọi trong lớp dẫn xuất, phương thức mới được thực thi.Tất cả các từ khóa new cho phép bạn thực hiện là có hai phương thức có cùng tên trong phân cấp lớp.

Cuối cùng, công cụ sửa đổi sealed vi phạm chuỗi các phương pháp virtual và khiến chúng không thể ghi đè lại. Điều này không được sử dụng thường xuyên, nhưng tùy chọn là có. Nó có ý nghĩa hơn với một chuỗi 3 lớp mỗi phát sinh từ trước đó một

A -> B -> C 

nếu A có một phương pháp virtual hoặc abstract, đó là overridden trong B, sau đó nó cũng có thể ngăn C từ việc thay đổi nó một lần nữa bằng cách tuyên bố số điện thoại sealed trong B.

sealed cũng được sử dụng trong classes và đó là nơi bạn thường gặp phải từ khóa này.

Tôi hy vọng điều này sẽ hữu ích.

29

Bất kỳ phương pháp nào cũng có thể được ghi đè (= virtual) hay không. Quyết định được thực hiện bởi một trong những người định nghĩa phương pháp:

class Person 
{ 
    // this one is not overridable (not virtual) 
    public String GetPersonType() 
    { 
     return "person"; 
    } 

    // this one is overridable (virtual) 
    public virtual String GetName() 
    { 
     return "generic name"; 
    } 
} 

Bây giờ bạn có thể ghi đè lên các phương pháp đó có overridable:

class Friend : Person 
{ 
    public Friend() : this("generic name") { } 

    public Friend(String name) 
    { 
     this._name = name; 
    } 

    // override Person.GetName: 
    public override String GetName() 
    { 
     return _name; 
    } 
} 

Nhưng bạn không thể ghi đè lên các phương pháp GetPersonType vì nó không phải là ảo.

Hãy tạo ra hai trường hợp của những lớp:

Person person = new Person(); 
Friend friend = new Friend("Onotole"); 

Khi phương pháp không ảo GetPersonType được gọi bởi Fiend dụ nó thực sự Person.GetPersonType đó được gọi là:

Console.WriteLine(friend.GetPersonType()); // "person" 

Khi phương pháp ảo GetName được gọi là bởi Friend thể hiện là Friend.GetName được gọi là:

Console.WriteLine(friend.GetName()); // "Onotole" 

Khi phương pháp ảo GetName được gọi bởi Person dụ nó Person.GetName đó được gọi là:

Console.WriteLine(person.GetName()); // "generic name" 

Khi phương pháp không ảo được gọi là thân phương pháp không được ngẩng đầu lên - trình biên dịch đã biết phương pháp thực tế mà cần được gọi là. Trong khi các trình biên dịch phương thức ảo không thể chắc chắn cái nào cần gọi, và nó được tra cứu trong thời gian chạy trong hệ thống phân cấp lớp từ dưới lên bắt đầu từ kiểu cá thể mà phương thức được gọi là: friend.GetName, nó bắt đầu tại Friend lớp học và tìm thấy nó ngay lập tức, cho person.GetName lớp nó bắt đầu tại Person và tìm thấy nó ở đó.

Đôi khi bạn thực hiện một lớp con, ghi đè lên một phương pháp ảo và bạn không muốn bất kỳ ghi đè hơn xuống trong hệ thống phân cấp - bạn sử dụng sealed override cho rằng (nói rằng bạn là người cuối cùng ai sẽ ghi đè phương pháp):

class Mike : Friend 
{ 
    public sealed override String GetName() 
    { 
     return "Mike"; 
    } 
} 

Nhưng đôi khi bạn bè của bạn Mike quyết định thay đổi giới tính của mình và do đó tên của mình để Alice :) bạn có thể có thể thay đổi mã gốc hoặc thay vì phân lớp Mike:

class Alice : Mike 
{ 
    public new String GetName() 
    { 
     return "Alice"; 
    } 
} 

Ở đây bạn tạo ra một phương pháp hoàn toàn khác nhau có cùng tên (bây giờ bạn có hai). Phương pháp nào và khi nào được gọi? Nó phụ thuộc vào cách bạn gọi nó là:

Alice alice = new Alice(); 
Console.WriteLine(alice.GetName());    // the new method is called, printing "Alice" 
Console.WriteLine(((Mike)alice).GetName());  // the method hidden by new is called, printing "Mike" 

Khi bạn gọi nó từ Alice 'quan điểm của bạn gọi Alice.GetName, khi từ Mike' s - bạn gọi Mike.GetName. Không có tra cứu thời gian chạy nào được thực hiện ở đây - vì cả hai phương thức đều không phải là ảo.

Bạn luôn có thể tạo các phương thức new - cho dù các phương pháp bạn đang ẩn có phải là ảo hay không.

Điều này cũng áp dụng cho các thuộc tính và sự kiện - chúng được thể hiện dưới dạng phương thức bên dưới.

+0

Không có một câu trả lời đơn giản và đầy đủ hơn điều này tôi tìm thấy ở bất cứ đâu. Cảm ơn Loki – Reevs

5
public class Base 
{ 
    public virtual void SomeMethod() 
    { 
    Console.WriteLine("B"); 
    } 
    } 

    //This one is Simple method 
public class Derived : Base 
{ 
    public void SomeMethod() 
    { 
    Console.WriteLine("D"); 
    } 

    //This method has 'new' keyword 
    public new void SomeMethod() 
    { 
    Console.WriteLine("D"); 
    } 

    //This method has 'override' keyword 
    public override void SomeMethod() 
    { 
    Console.WriteLine("D"); 
    } 
} 

điều Bây giờ Đầu tiên Đầu tiên

Base b=new Base(); 
Derived d=new Derived(); 
b.SomeMethod(); will always write B 
d.SomeMethod(); will always write D 

Bây giờ các từ khóa là tất cả về Polymorphism

       Base b = new Derived(); 
  1. Sử dụng virtual trong lớp cơ sở và ghi đè trong Derived sẽ cho D (Polymorphism).
  2. Sử dụng override mà không cần virtual trong Base sẽ gây ra lỗi.
  3. Tương tự như viết một phương thức (không ghi đè) với virtual sẽ viết 'B' với cảnh báo (vì không có đa hình được thực hiện).
  4. Để ẩn cảnh báo như ở trên điểm viết new trước phương pháp đơn giản đó trong Derived.
  5. new từ khoá là một câu chuyện khác, nó chỉ đơn giản là ẩn các cảnh báo cho biết rằng thuộc tính có cùng tên là có trong lớp cơ sở.
  6. virtual hoặc new cả hai đều như nhau ngoại trừ new modifier

  7. newoverride không thể được sử dụng trước đó cùng một phương pháp hay tài sản.

  8. sealed trước khi bất kỳ lớp hoặc phương pháp nào khóa nó để sử dụng trong lớp Có nguồn gốc và nó cung cấp lỗi thời gian biên dịch.
+0

Xin lỗi, nhưng -1 vì nhiều lỗi biên dịch: phương thức được khai báo nhiều lần với cùng thông số, không có dấu ngoặc kép xung quanh chuỗi B & D ... – DeveloperDan