2012-02-23 60 views
7

Tôi nghĩ rằng tôi đã đóng đinh, và sau đó tôi đi xem một số nguồn tại nơi làm việc và tôi tự hỏi tại sao có rất nhiều mâu thuẫn trong những gì tôi đọc từ msdn và những gì tôi thấy trong nguồn. ...C# Virtual & Override Keywords

Hiểu biết của tôi là từ khóa ảo có thể được sử dụng trong khai báo phương pháp để cho phép mọi lớp phát sinh ghi đè lên.

Các từ khóa override sau đó sẽ cần phải được sử dụng trong các lớp được thừa kế khi thực hiện phương pháp ảo cha ....

Ví dụ:

public abstract class A 
{ 
    public virtual string GetName(); 
} 


public class B:A 
{ 

    //assume there are some defined properties. 
    public override string GetName() 
    { 
     return FirstName; 
    } 
} 

Tôi có một vài câu hỏi:

1) Có thực sự cần thiết để xác định một phương thức là ảo nếu nó không thực hiện không? Chắc chắn nó chỉ có thể được ghi đè trong lớp con mà không sử dụng ảo và ghi đè?

2) Nếu (1) là không chính xác, tôi ngay trong suy nghĩ rằng mọi phương pháp ảo phải được ghi đè trong lớp con sử dụng nó ....

EDIT:

Bạn nói đúng mã của tôi sẽ không biên dịch ... tôi muốn biết tại sao .... tôi uinderstand câu trả lời của bạn, nhưng sau đó tôi thấy điều này:

public abstract class RequestHandler<TRequest, TResponse> : RequestHandler, IRequestHandler<TRequest>, IRequestHandler, IDisposable, ITypedRequestHandler 
    where TRequest : global::Agatha.Common.Request 
    where TResponse : global::Agatha.Common.Response, new() 
{ 
    protected RequestHandler(); 

    public virtual void AfterHandle(TRequest request); 
    public virtual void BeforeHandle(TRequest request); 
    public override Response CreateDefaultResponse(); 
    public TResponse CreateTypedResponse(); 
    public override Response Handle(Request request); 
    public abstract Response Handle(TRequest request); 
} 

các doesnt trên gây ra trình biên dịch để phàn nàn ...

+0

Ví dụ của bạn không biên dịch ("' A.GetName() 'phải khai báo nội dung vì nó không được đánh dấu là' abstract', 'extern', hoặc' partial' "). – dtb

+0

Trình biên dịch lớp A của bạn có lưu ý tóm tắt thiếu của nó không? – V4Vendetta

+0

Chắc chắn phương thức trong lớp A phải là chuỗi trừu tượng công khai GetName(); – Vedran

Trả lời

10

thứ nhất thứ e ở trên mã không hợp lệ. Một phương pháp ảo vẫn phải có một cơ thể với một cài đặt mặc định. để làm những gì bạn đã làm ở trên, bạn sẽ cần phải sử dụng phím số abstract thay vì ảo.

abstract có nghĩa là không có phương thức nào được cung cấp nhưng bất kỳ lớp nào xuất phát từ nó phải thực hiện phương pháp này (trừ khi nó cũng trừu tượng).

Tôi nghĩ rằng đây khá nhiều câu trả lời câu hỏi của bạn ....

  1. Nếu nó không có thực hiện sau đó nó không có thể ảo, nó phải là trừu tượng. Nếu nó có một thực hiện mà chỉ cần làm gì thì đó phải được thực hiện.

  2. Toàn bộ điểm của lớp ảo là nó có hành vi mặc định để bạn có thể chọn ghi đè hay không. Nếu nó đã được trừu tượng thì bạn sẽ phải ghi đè lên nó (trừ khi bạn đang bắt nguồn từ một lớp trừu tượng khác).

+0

Vâng đó là chính xác những gì tôi nghĩ nhưng tôi có một số nguồn ở đây mà không gây ra trình biên dịch để khiếu nại ... xin vui lòng xem chỉnh sửa ở trên – user559142

+0

Chính xác. Nếu thực sự cơ thể tồn tại, với 'virtual' nó sẽ không tranh luận về lớp thừa hưởng để thực hiện phương thức. – Aphelion

+0

Cảm ơn điều đó có ý nghĩa nhưng tôi vẫn còn nhầm lẫn về nguồn ở trên trong chỉnh sửa – user559142

0

ok, bằng cách tuyên bố phương pháp như ảo (và không có nhu cầu để ghi đè các phương thức trong lớp con), bạn sẽ được bước vào thế giới của phương pháp ẩn nếu bạn đã giới thiệu một phương pháp với cùng tên vào lớp con.Bạn phải xác định nó như là như sau:

public abstract class A 
{ 
    public virtual string GetName() 
    { 
     return null; 
    } 
} 

public class B : A 
{ 
    //assume there are some defined properties. 
    public new string GetName() 
    { 
     return FirstName; 
    } 
} 

tôi muốn khuyên bạn nên nghĩ đến phương pháp ghi đè trừu tượng:

public abstract class A 
{ 
    public abstract string GetName() 
} 

// to override 
public class B : A 
{ 
    //assume there are some defined properties. 
    public override string GetName() 
    { 
     return FirstName; 
    } 
} 

này sẽ hoàn toàn khác. Tôi thực sự khuyên bạn chỉ nên ghi đè phương thức trừu tượng trong lớp con.

Tuy nhiên, đây là một ref nhanh về đề tài này (cũ nhưng đọc tốt):

http://www.akadia.com/services/dotnet_polymorphism.html

+0

Điều này sẽ không biên dịch, bạn không thể ẩn một phương thức trừu tượng. – softveda

+0

ahh -ok, trong trường hợp này lớp A phải là ảo rồi. sẽ sửa đổi câu trả lời –

+0

Bạn thực sự ** không thể ** tránh ghi đè phương thức trừu tượng. Đó là một điều bắt buộc. – Tigran

2

Là nó thực sự cần thiết để xác định một phương pháp như ảo nếu nó không có thực hiện?

Bạn có thể làm cho phương pháp trừu tượng (nó sẽ ngầm làm cho nó ảo).

Chắc chắn nó chỉ có thể được ghi đè trong lớp con mà không sử dụng ảo và ghi đè?

Nếu bạn chỉ "ghi đè" nó mà không có trọng nó một cách rõ ràng, nó sẽ không phải là cùng một phương pháp, và gọi phương thức trên một biến của lớp cơ sở sẽ không gọi phương thức có nguồn gốc (nó sẽ không tham gia vào đa hình). Bạn sẽ chỉ "ẩn" phương thức của lớp cơ sở (trình biên dịch thực sự cảnh báo bạn về điều này, nếu đó thực sự là điều bạn muốn làm, bạn phải sử dụng công cụ sửa đổi new.)

Ví dụ sẽ làm rõ hơn:

class B 
{ 
    public virtual void M() { Console.WriteLine("B.M") }; 
} 

class D1 : Base 
{ 
    // Hides the base method 
    public new void M() { Console.WriteLine("D1.M") }; 
} 


class D2 : Base 
{ 
    // Overrides the base method 
    public override void M() { Console.WriteLine("D2.M") }; 
} 

... 

D1 d1 = new D1(); 
d1.M(); // Prints "D1.M" 
B b1 = d1; 
b1.M(); // Prints "B.M", because D1.M doesn't override B.M 

D2 d2 = new D1(); 
d2.M(); // Prints "D2.M" 
B b2 = d2; 
b2.M(); // Also prints "D2.M", because D2.M overrides B.M 

Nếu (1) là không chính xác, tôi ngay trong suy nghĩ rằng mọi phương pháp ảo phải được ghi đè trong lớp con sử dụng nó ....

Không, chỉ khi nó là trừu tượng .. một phương pháp ảo có thể có triển khai và trong đó các lớp bắt nguồn từ trường hợp không bắt buộc phải ghi đè lên nó.

0

Sự cần thiết cho virtual được quyết định bởi đa hình. Hãy suy nghĩ về những gì xảy ra khi bạn xác định lại một phương pháp trong một lớp con sử dụng new:

class Parent 
{ 
    void Foo() { Console.WriteLine("Parent"); } 
    virtual void Bar { Console.WriteLine("Parent"); } 
} 

class Child : Parent 
{ 
    new void Foo() { Console.WriteLine("Child"); } // another method Foo 
    override void Bar { Console.WriteLine("Child"); } 
} 

var child = new Child(); // a Child instance 
var parent = (Parent)child; // same object, but statically typed as Parent 

c.Bar(); // prints "Child" 
p.Bar(); // again, prints "Child" -- expected virtual behavior 

c.Foo(); // prints "Child" 
p.Foo(); // but this prints "Parent"!! 

Vì lý do này, các lớp học được dự định được nguồn gốc từ (tức là không sealed) nên luôn luôn đánh dấu các phương pháp có thể được ghi đè như virtual - nếu không sẽ có hành vi thời gian chạy khó hiểu như trên. Không có sự khác biệt nếu việc thực hiện cha mẹ thực sự làm bất cứ điều gì hay không; vấn đề là nó hoạt động khác nhau.

0

Đó chính là sự khác biệt giữa một virtual và một phương pháp abstract:

Một ảo phương pháp thể được ghi đè bởi các lớp thừa kế nếu muốn. Một phương pháp ảo có thể hoặc không thể thực hiện mặc định cho người thừa kế để gọi vào, được thực hiện bằng cách sử dụng từ khóa base.

Một trừu tượng phương pháp phải được ghi đè bởi các lớp thừa kế. Các ràng buộc của nó là:

  • Nó chỉ có thể được định nghĩa trong lớp trừu tượng.
  • Nó không thể triển khai, vì nó tùy thuộc vào người thừa kế để xác định hành vi của nó.
0

1) Có thực sự cần thiết để xác định phương pháp ảo nếu không thực hiện ? Chắc chắn nó chỉ có thể được ghi đè trong phân lớp mà không cần sử dụng ảo và ghi đè?

Bạn có thể sử dụng một abstract phương pháp, mà không có cơ thể, nhưng họ nếu bạn lấy được từ lớp học mà bạn phải để ghi đè phương thức đó. Tất cả các phương pháp abstact phải được overriden trong lớp trẻ em.

2) Nếu (1) là không chính xác, tôi ngay trong suy nghĩ rằng mọi phương pháp ảo phải được ghi đè trong lớp con sử dụng nó ....

Không, thay vào đó, bạn có thể sử dụng từ khóa virtual để thực hiện bên trong, nhưng làm không ghi đè bắt buộc trong lớp con bắt buộc. Giúp bạn linh hoạt hơn, từ quan điểm đó.

Usauly họ sử dụng để tạo ra abstract ép cứng nhắc cho Childs, vì vậy mỗi con bắt nguồn từ nó phải thực hiện thành viên cụ thể của lớp cơ sở.

1

1) Có thực sự cần thiết để xác định một phương thức là ảo nếu nó không có triển khai không? Chắc chắn nó chỉ có thể được ghi đè trong lớp con mà không sử dụng ảo và ghi đè?

Như đã nói trong các câu trả lời khác, virtual phương pháp cần phải triển khai. Bạn đang bối rối với abstract.

Nếu bạn đang hỏi xem virtual phương thức nào do cần triển khai thực hiện virtual: Trong C#, vâng, điều đó là cần thiết. Trong Java, bạn có thể ghi đè lên bất kỳ phương thức cũ nào. Đó là một quyết định thiết kế C# cần được ghi đè để được cho phép cụ thể với từ khóa virtual, do đó các phương thức không thể bị ghi đè trừ khi được người lập trình dự định.

Nếu lập trình viên không thể hiện ý định bằng cách không sử dụng virtual, bạn vẫn có thể "ghi đè" phương pháp, với từ khóa new. Tuy nhiên, điều này hoạt động hơi khác một chút.Hy vọng rằng đoạn mã này sẽ giúp minh họa cho khái niệm:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var baseC = new BaseClass(); 
     var extC = new ExtClass(); 
     var lazyC = new LazyClass(); 

     Console.WriteLine(baseC.NewMethod()); 
     Console.WriteLine(baseC.VirtualOverrideMethod()); 

     Console.WriteLine("---"); 

     Console.WriteLine(extC.NewMethod()); 
     Console.WriteLine(extC.VirtualOverrideMethod()); 

     Console.WriteLine("---"); 

     Console.WriteLine(((BaseClass) extC).NewMethod()); 
     Console.WriteLine(((BaseClass) extC).VirtualOverrideMethod()); // Redundant typecast 

     Console.WriteLine("---"); 

     Console.WriteLine(lazyC.VirtualOverrideMethod()); 

     Console.ReadKey(); 
    } 

    public class BaseClass 
    { 
     public BaseClass() 
     { 

     } 

     public string NewMethod() 
     { 
      return "NewMethod of BaseClass"; 
     } 

     public virtual string VirtualOverrideMethod() 
     { 
      return "VirtualOverrideMethod of BaseClass"; 
     } 
    } 

    class ExtClass : BaseClass 
    { 
     public new string NewMethod() 
     { 
      return "NewMethod of ExtClass"; 
     } 

     public override string VirtualOverrideMethod() 
     { 
      return "VirtualOverrideMethod of ExtClass"; 
     } 
    } 

    class LazyClass : BaseClass 
    { 
    } 
} 

Output:

NewMethod of BaseClass 
VirtualOverrideMethod of BaseClass 
--- 
NewMethod of ExtClass 
VirtualOverrideMethod of ExtClass 
--- 
NewMethod of BaseClass 
VirtualOverrideMethod of ExtClass 
--- 
VirtualOverrideMethod of BaseClass 

2) Nếu (1) là không chính xác, tôi ngay trong suy nghĩ rằng mọi phương pháp ảo phải được ghi đè trong lớp con sử dụng nó ....

Không hề. virtual là một cách để nói, "không sao nếu bạn muốn ghi đè phương thức này". Trong thực tế, tôi đã bao gồm LazyClass trong mã của tôi ở trên để hiển thị điều này.

Các doesnt trên gây ra trình biên dịch để phàn nàn ...

tôi đã không sử dụng giao diện nhiều, nhưng điều đó trông giống như một. Trong mã của tôi, nếu tôi thay đổi

class ExtClass : BaseClass 
    { 
     public new string NewMethod() 
     { 
      return "NewMethod of ExtClass"; 
     } 

     public override string VirtualOverrideMethod() 
     { 
      return "VirtualOverrideMethod of ExtClass"; 
     } 
    } 

tới:

class ExtClass : BaseClass 
    { 
     public new string NewMethod() 
     { 
      return "NewMethod of ExtClass"; 
     } 

     public override string VirtualOverrideMethod() 
     { 
      return "VirtualOverrideMethod of ExtClass"; 
     } 
    } 

tôi nhận được:

error CS0501: 'OverrideTest.Program.ExtClass.VirtualOverrideMethod()' must declare a body because it is not marked abstract, extern, or partial 

Từ Visual Studio 2010. Cũng vậy với phương pháp virtual của BaseClass tôi.