Phương thức ảo là gì?


81

Tại sao bạn khai báo một phương thức là "ảo".

Lợi ích trong việc sử dụng ảo là gì?

Câu trả lời:


58

Công cụ sửa đổi ảo được sử dụng để đánh dấu rằng phương thức \ thuộc tính (ect) có thể được sửa đổi trong lớp dẫn xuất bằng cách sử dụng công cụ sửa đổi ghi đè .

Thí dụ:

class A
{
    public virtual void Foo()
       //DoStuff For A
}

class B : A
{
    public override void Foo()
    //DoStuff For B

    //now call the base to do the stuff for A and B 
    //if required
    base.Foo()
}

45

Virtual cho phép một lớp kế thừa thay thế một phương thức mà lớp cơ sở sau đó sử dụng.

public class Thingy
{
    public virtual void StepA()
    {
        Console.Out.WriteLine("Zing");
    }

    public void Action()
    {
        StepA();
        Console.Out.WriteLine("A Thingy in Action.");
    }
}

public class Widget : Thingy
{
    public override void StepA()
    {
        Console.Out.WriteLine("Wiggy");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Thingy thingy = new Thingy();
        Widget widget = new Widget();

        thingy.Action();
        widget.Action();

        Console.Out.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }
 }

Khi bạn chạy Chương trình, đầu ra của bạn sẽ là:

Zing 
A Thingy in Action. 
Wiggy 
A Thingy in Action.

Lưu ý rằng mặc dù Widget được gọi là phương thức Action () được định nghĩa ở cấp Thingy, bên trong Thingy được gọi là phương thức StepA () của Widget.

Câu trả lời cơ bản là nó cung cấp cho những người kế thừa của một lớp linh hoạt hơn. Tất nhiên, bạn phải thiết kế lớp học của mình thật tốt nếu không nó có thể tàn phá yếu.


23

Phương thức ảo là một kiểu phương thức mà phương thức thực sự gọi phụ thuộc vào kiểu thời gian chạy của đối tượng bên dưới.

Phương thức không ảo là một loại phương thức trong đó phương thức thực sự được gọi phụ thuộc vào kiểu tham chiếu của đối tượng tại điểm gọi phương thức.


Đây phải là một câu trả lời - ảo không thể được xác định thông qua các điều khoản sửa đổi khai báo. Nó khác với phương thức ẩn như thế nào?
SENya

13

Phương thức ảo trên MSDN

Từ khóa virtual được sử dụng để sửa đổi phương thức hoặc khai báo thuộc tính, trong trường hợp này phương thức hoặc thuộc tính được gọi là thành viên ảo. Việc triển khai một thành viên ảo có thể được thay đổi bởi một thành viên ghi đè trong một lớp dẫn xuất.

Khi một phương thức ảo được gọi, kiểu thời gian chạy của đối tượng sẽ được kiểm tra để tìm thành viên ghi đè. Thành viên ghi đè trong lớp dẫn xuất nhất được gọi, có thể là thành viên ban đầu, nếu không có lớp dẫn xuất nào ghi đè thành viên. (Để biết thêm thông tin về kiểu thời gian chạy và cách triển khai bắt nguồn nhất, hãy xem 10.5.3 Phương thức ảo.)

Theo mặc định, các phương thức là không ảo. Bạn không thể ghi đè một phương thức không phải ảo.

Bạn không thể sử dụng công cụ sửa đổi ảo với các công cụ sửa đổi sau:

ghi đè trừu tượng tĩnh

Thuộc tính ảo hoạt động giống như các phương thức trừu tượng, ngoại trừ sự khác biệt trong cú pháp khai báo và lệnh gọi.

  • Đó là một lỗi khi sử dụng công cụ sửa đổi ảo trên thuộc tính tĩnh.
  • Một thuộc tính kế thừa ảo có thể được ghi đè trong một lớp dẫn xuất bằng cách đưa vào một khai báo thuộc tính sử dụng công cụ sửa đổi ghi đè.

6

Ngay cả khi bạn không định dẫn xuất từ ​​lớp, việc đánh dấu phương thức ảo có thể cần thiết để bắt chước lớp đó. Một số khung chế tạo chỉ cho phép bạn mô phỏng các phương pháp ảo. Lưu ý rằng các phương thức triển khai giao diện là ảo ngầm.

Tôi sử dụng RhinoMocks có hạn chế này và đã đánh dấu các phương thức của tôi là ảo theo mặc định chỉ vì lý do này. Đối với tôi, đây có lẽ là lý do lớn nhất để sử dụng các phương thức ảo vì các trường hợp kế thừa phát huy tác dụng ít thường xuyên hơn nhiều.


5

Các phương thức ảo tương tự như các phương thức trừu tượng trong các lớp cơ sở ngoại trừ việc triển khai chúng trên các lớp dẫn xuất là tùy chọn. Ngoài ra, bạn có thể đặt logic trong phương thức ảo và ghi đè chúng trong các lớp dẫn xuất.


4

Một câu hỏi ngắn, một câu trả lời ngắn gọn! Cho phép phương thức của bạn là "ảo" nếu bạn nghĩ rằng bạn sẽ kế thừa lớp mà nó thuộc về.

Câu trả lời dài hơn: "virtual cho phép bạn ghi đè, để cung cấp một ý nghĩa khác cho phương thức của bạn trong một lớp dẫn xuất.


3

Để có thể ghi đè nó trong các lớp kế thừa.

Kiểm tra mục nhập MSDN cho từ khóa. Điều đó giải thích nó sâu hơn.


1

Không cần phải nói, các phương pháp ảo có ích khi mã của bạn đang cố gắng tuân thủ Nguyên tắc Đóng Mở

Đọc thêm về Nguyên tắc đóng mở tại đây , sách trắng OCP ban đầu của Uncle Bob.

Cũng xin lưu ý rằng các phương thức không phải là ảo theo mặc định trong C # không giống như Java.



1

Các hàm ảo là các hàm không thực sự tồn tại. Lớp dẫn xuất có thể sửa đổi hàm ảo bằng cách ghi đè nó. Các hàm ảo là một trong những cách để đạt được tính đa hình thời gian chạy

    public class sample {
      public virtual void fun(){
        Console.WriteLine("base sample class \n");
      }
    }
    public class A : sample{
      public override void fun(){
        Console.WriteLine("Class A \n");
      }
    }
    public class B : sample{
      public override void fun(){
        Console.WriteLine("Class B \n");
      }
    }
    class run{
      public static void main(String[] args){
        sample obj = new sample();
        sample obj1 = new A();
        sample obj2 = new B();
        obj.fun();
        obj1.fun();
        obj2.fun();
      }
    }

Ý bạn là gì khi "không thực sự tồn tại"? Bạn có thể cung cấp tài liệu tham khảo
Mubarek

Điều này không giống như kế thừa C #. Các công cụ publicsửa đổi truy cập sau class Aclass Bgây ra lỗi thời gian biên dịch. Khả năng truy cập của các thành viên trong lớp cơ sở từ lớp dẫn xuất được chỉ định trên cơ sở riêng lẻ tạo thành lớp cơ sở (theo mặc định là các thành viên private).
Minh Tran

@Minh Tran - Đúng, Bạn nói đúng. Đó là kế thừa c ++. Dù sao thì tôi cũng đã sửa bài.
Lineesh K Mohan

1

Thời gian chạy diễn ra theo thời gian biên dịch.
Khi bạn khai báo một phương thức là ảo, việc khai báo nó trong lớp dẫn xuất sẽ yêu cầu bạn thêm một overridehoặc newbổ trợ.
chúng ta có thể thấy điều đó khi TrySpeak. Truyền vào con và cha, cả hai đều gọi là Nói của cha, trong khi TryScream, sẽ gọi từng phương thức.
Để hiểu điều này, có một số điều chúng ta nên biết, trong một ví dụ của Child, có hai Screamphương thức từ lớp Con hoặc lớp Cha. Chúng ta có thể gọi Scream từ lớp Con hoặc lớp Cha. Bởi vì VirtaulModifier đánh dấu phương thức nên nó có thể bị ghi đè bởi lớp dẫn xuất, có nghĩa là ngay cả cái Screamđược gọi từ lớp Father, nó cũng bị ghi đè, nó sẽ khác nếu bạn sử dụng modifier mới.

import system;
class Father
{
    Speak()
    {
        Console.Writeline("Father is speaking") 
    }
    virtual Scream()
    {
        Console.Writeline("Father is screaming")    
    }
}
class Child: father
{
    Speak()
    {
        Console.Writeline("Child is speaking")  
    }
    override Scream()
    {
        Console.Writeline("Child is screaming") 
    }
}
class APP
{
    public static void Main()
    {
        // We new two instances here
        Father father = new Father();
        Child child = new Child();        
        // Here we call their scream or speak through TryScream or TrySpeak
        TrySpeak(father);
        TrySpeak(child);
        //>>>"Father is speaking"
        //>>>"Father is speaking"
        TryScream(father);
        TryScream(child);
        //>>>"Father is screaming"
        //>>>"Child is screaming"
    }
    // when your method take an Parameter who type is Father
    // You can either pass in a Father instance or
    // A instance of a derived Class from Father
    // which could be Child
    public static void TrySpeak(Father person)
    {
        person.Scream();
    }
    public static void TryScream(Father person)
    {
        person.Speak();
    }
}

1

Trong C #, để ghi đè phương thức lớp cơ sở trong lớp dẫn xuất, bạn phải khai báo phương thức lớp cơ sở là phương thức lớp dẫn xuất và ảo như được ghi đè như hình dưới đây:

using System;
namespace Polymorphism
{
 class A
 {
 public virtual void Test() { Console.WriteLine("A::Test()"); }
 }

 class B : A
 {
 public override void Test() { Console.WriteLine("B::Test()"); }
 }

 class C : B
 {
 public override void Test() { Console.WriteLine("C::Test()"); }
 }

 class Program
 {
 static void Main(string[] args)
 {

 A a = new A();
 B b = new B();
 C c = new C();
 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "B::Test()"

 b = new C();
 b.Test(); // output --> "C::Test()"

 Console.ReadKey();
 }
 }
}

Bạn cũng có thể kết hợp ẩn phương thức và ghi đè phương thức bằng cách sử dụng từ khóa ảo và mới vì phương thức của một lớp dẫn xuất có thể ảo và mới cùng một lúc. Điều này là bắt buộc khi bạn muốn ghi đè thêm phương thức lớp dẫn xuất sang cấp độ tiếp theo vì tôi đang ghi đè phương thức Lớp B, Test () trong Lớp C như được hiển thị bên dưới:

using System;
namespace Polymorphism
{
 class A
 {
 public void Test() { Console.WriteLine("A::Test()"); }
 }

 class B : A
 {
 public new virtual void Test() { Console.WriteLine("B::Test()"); }
 }

 class C : B
 {
 public override void Test() { Console.WriteLine("C::Test()"); }
 }

 class Program
 {
 static void Main(string[] args)
 {

 A a = new A();
 B b = new B();
 C c = new C();

 a.Test(); // output --> "A::Test()"
 b.Test(); // output --> "B::Test()"
 c.Test(); // output --> "C::Test()"

 a = new B();
 a.Test(); // output --> "A::Test()"

 b = new C();
 b.Test(); // output --> "C::Test()"

 Console.ReadKey();
 }
 }
}

GOLDEN WORDS: Từ khóa ảo được sử dụng để sửa đổi một phương thức, thuộc tính, trình chỉ mục hoặc sự kiện được khai báo trong lớp cơ sở và cho phép nó được ghi đè trong lớp dẫn xuất.

Từ khóa ghi đè được sử dụng để mở rộng hoặc sửa đổi phương thức, thuộc tính, trình chỉ mục hoặc sự kiện ảo / trừu tượng của lớp cơ sở thành lớp dẫn xuất.

Từ khóa mới được sử dụng để ẩn một phương thức, thuộc tính, trình chỉ mục hoặc sự kiện của lớp cơ sở vào lớp dẫn xuất.

THƯỞNG THỨC :-)


-1

Liên kết này sẽ giúp bạn hiểu rõ hơn với ví dụ rất dễ hiểu https://stackoverflow.com/a/2392656/3373865


1
Đây không chỉ là câu trả lời chỉ dành cho liên kết (vui lòng bao gồm nội dung của bài đăng được liên kết trong câu trả lời của bạn, trong trường hợp liên kết bị hỏng), mà còn về C ++, không phải C #. Các nguyên tắc tương tự có áp dụng ở đây không?
Marks Polakovs 23/03/18
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.