c#:抽象函数和虚函数的区别是什么?

抽象函数和虚函数的区别是什么?在哪些情况下建议使用虚拟或抽象?哪一个是最好的方法?


抽象函数不能具有功能性。您基本上是说,任何子类都必须提供自己版本的这个方法,但是它太通用了,甚至不能在父类中实现。

一个虚函数,基本上是说,看,这里的功能对于子类来说可能不够好,也可能不够好。因此,如果它足够好,那么使用这个方法,如果不是,那么覆盖我,并提供您自己的功能。


抽象函数没有实现,只能在抽象类上声明。这将强制派生类提供实现。虚函数提供默认实现,它可以存在于抽象类或非抽象类中。举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class myBase
{
    //If you derive from this class you must implement this method. notice we have no method body here either
    public abstract void YouMustImplement();

    //If you derive from this class you can change the behavior but are not required to
    public virtual void YouCanOverride()
    {
    }
}

public class MyBase
{
   //This will not compile because you cannot have an abstract method in a non-abstract class
    public abstract void YouMustImplement();
}


只有abstract类可以有abstract成员。继承自abstract类的非abstract类必须overrideabstract成员。abstract成员是隐式的virtualabstract成员不能提供任何实现(在某些语言中,abstract被称为pure virtual)。


您必须始终覆盖抽象函数。

因此:

抽象函数——当继承程序必须提供自己的实现时虚拟-当这是由继承人决定


摘要函数:

它只能在抽象类中声明。它只包含方法声明而不是抽象类中的实现。它必须在派生类中重写。

虚函数:

它既可以在抽象类中声明,也可以在非抽象类中声明。它包含方法实现。它可能被覆盖。


抽象方法:当类包含抽象方法时,必须将该类声明为抽象。抽象方法没有实现,因此派生自该抽象类的类必须为该抽象方法提供实现。

虚拟方法:一个类可以有一个虚方法。虚拟方法有一个实现。当从具有虚拟方法的类继承时,可以覆盖虚拟方法并提供附加逻辑,或者用自己的实现替换逻辑。

何时使用什么:在某些情况下,您知道某些类型应该具有特定的方法,但是,您不知道该方法应该具有什么实现。在这种情况下,可以创建包含具有此签名的方法的接口。但是,如果您有这样的情况,但是您知道该接口的实现者也有另一个公共方法(您已经可以为其提供实现),那么您可以创建一个抽象类。然后这个抽象类包含抽象方法(必须覆盖)和另一个包含"公共"逻辑的方法。

如果您有一个类可以直接使用,但是您希望继承程序能够更改某些行为(尽管不是强制的),则应该使用虚拟方法。


解释:类比。希望它能帮助你。

上下文

我在一栋大楼的21层工作。我对火很偏执。世界上时不时地会有一场大火烧毁摩天大楼。但幸运的是,我们在这里的某个地方有一本说明书,告诉我们在发生火灾时该怎么做:

FireEscape ()

不收集物品步行到防火梯走出大楼

这是一个名为FireEscape()的虚拟方法

虚方法

这个计划在99%的情况下都很好。这是一个行之有效的基本计划。但有1%的可能性,消防通道被堵塞或损坏,在这种情况下,你完全完蛋了,你会成为吐司,除非你采取一些激烈的行动。使用虚拟方法,您可以这样做:您可以用自己的计划版本覆盖基本的FireEscape()计划:

跑到窗口跳出窗外安全降落到底部

换句话说,虚拟方法提供了一个基本的计划,如果需要,可以覆盖它。如果程序员认为合适,子类可以覆盖父类的虚方法。

抽象方法

并不是所有的组织都训练有素。有些组织不做消防演习。他们没有全面的逃跑政策。各人为己。管理层只对现有的这种策略感兴趣。

换句话说,每个人都必须开发自己的FireEscape()方法。一个人会从消防通道里走出来。另一个人会跳伞。另一个人将使用火箭推进技术飞离大楼。另一个人会掉下去。管理层并不关心你如何逃生,只要你有一个基本的防火逃生计划()——如果他们没有,你可以保证OHS会像砖头一样砸向组织。这就是抽象方法的含义。

这两者有什么区别?

抽象方法:子类被迫实现自己的FireEscape方法。使用虚拟方法,您有一个基本的计划等着您,但是如果它不够好,您可以选择实现自己的计划。

这并不难,对吧?


抽象方法是一种必须实现才能生成具体类的方法。声明在抽象类中(任何具有抽象方法的类都必须是抽象类),并且必须在具体类中实现。

虚方法是一个可以在派生类中使用重写(替换超类中的行为)重写的方法。如果不覆盖,就会得到原始行为。如果你这样做了,你总是会得到新的行为。这与不能重写但可以隐藏原始方法的非虚方法相反。这是使用new修饰符完成的。

请看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class BaseClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }


    public virtual void SayGoodbye()
    {
        Console.WriteLine("Goodbye");
    }

    public void HelloGoodbye()
    {
        this.SayHello();
        this.SayGoodbye();
    }
}


public class DerivedClass : BaseClass
{
    public new void SayHello()
    {
        Console.WriteLine("Hi There");
    }


    public override void SayGoodbye()
    {
        Console.WriteLine("See you later");
    }
}

当我实例化DerivedClass并调用SayHelloSayGoodbye时,我得到"Hi There"和"See you later"。如果我打电话给HelloGoodbye,我会得到"Hello"和"See you later"。这是因为SayGoodbye是虚拟的,可以用派生类替换。SayHello只是隐藏的,所以当我从基类中调用它时,我得到了我的原始方法。

抽象方法是隐式虚方法。它们定义了必须呈现的行为,更像是接口所做的。


抽象方法总是虚拟的。它们不能有实现。

这是主要的区别。

基本上,如果您拥有虚方法的"默认"实现,并且希望允许后代更改其行为,那么您将使用虚方法。

使用抽象方法,您可以强制后代提供实现。


通过对以下类(来自其他答案)做一些改进,我使这个问题变得更简单了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestOO
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClass _base = new BaseClass();
            Console.WriteLine("Calling virtual method directly");
            _base.SayHello();
            Console.WriteLine("Calling single method directly");
            _base.SayGoodbye();

            DerivedClass _derived = new DerivedClass();
            Console.WriteLine("Calling new method from derived class");
            _derived.SayHello();
            Console.WriteLine("Calling overrided method from derived class");
            _derived.SayGoodbye();

            DerivedClass2 _derived2 = new DerivedClass2();
            Console.WriteLine("Calling new method from derived2 class");
            _derived2.SayHello();
            Console.WriteLine("Calling overrided method from derived2 class");
            _derived2.SayGoodbye();
            Console.ReadLine();
        }
    }


    public class BaseClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello
"
);
        }
        public virtual void SayGoodbye()
        {
            Console.WriteLine("Goodbye
"
);
        }

        public void HelloGoodbye()
        {
            this.SayHello();
            this.SayGoodbye();
        }
    }


    public abstract class AbstractClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello
"
);
        }


        //public virtual void SayGoodbye()
        //{
        //    Console.WriteLine("Goodbye
");
        //}
        public abstract void SayGoodbye();
    }


    public class DerivedClass : BaseClass
    {
        public new void SayHello()
        {
            Console.WriteLine("
Hi There");
        }

        public override void SayGoodbye()
        {
            Console.WriteLine("
See you later");
        }
    }

    public class DerivedClass2 : AbstractClass
    {
        public new void SayHello()
        {
            Console.WriteLine("
Hi There");
        }
        // We should use the override keyword with abstract types
        //public new void SayGoodbye()
        //{
        //    Console.WriteLine("
See you later2");
        //}
        public override void SayGoodbye()
        {
            Console.WriteLine("
See you later");
        }
    }
}

绑定是将名称映射到代码单元的过程。

延迟绑定意味着我们使用名称,但延迟映射。换句话说,我们首先创建/提到这个名称,然后让一些后续流程处理代码到该名称的映射。

现在考虑:

与人类相比,机器确实擅长搜索和排序与机器相比,人类真的擅长发明和创新

因此,简单的答案是:virtual是机器(运行时)的晚期绑定指令,而abstract是人类(程序员)的晚期绑定指令。

换句话说,virtual的意思是:

"亲爱的运行时,做你最擅长的事情:搜索,把合适的代码绑定到这个名字上。"

abstract的意思是:

"亲爱的程序员,请将适当的代码绑定到这个名称,做你最擅长的事情:发明"

为完整性考虑,重载意味着:

"亲爱的编译器,做你最擅长的事情:排序,把合适的代码绑定到这个名字上。"


当您希望继承程序扩展功能(如果它们愿意)时,您基本上使用虚拟方法。

当您希望继承者实现功能时,您使用抽象方法(在本例中,他们没有选择)


我在一些地方看到抽象方法定义如下。* *

"An Abstract Method must have to implement in the child class"

* *我觉得就像。

如果子类也是抽象的,则不必在子类中实现抽象方法。

抽象方法不能是私有方法。抽象方法不能在同一个抽象类中实现。

我会说…如果我们要实现抽象类,则必须重写基抽象类中的抽象方法。因为. .实现抽象方法是使用覆盖关键字。类似于虚方法。

没有必要在继承的类中实现虚方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
                                 ----------CODE--------------

public abstract class BaseClass
{
    public int MyProperty { get; set; }
    protected abstract void MyAbstractMethod();

    public virtual void MyVirtualMethod()
    {
        var x = 3 + 4;
    }

}
public abstract class myClassA : BaseClass
{
    public int MyProperty { get; set; }
    //not necessary to implement an abstract method if the child class is also abstract.

    protected override void MyAbstractMethod()
    {
        throw new NotImplementedException();
    }
}
public class myClassB : BaseClass
{
    public int MyProperty { get; set; }
    //You must have to implement the abstract method since this class is not an abstract class.

    protected override void MyAbstractMethod()
    {
        throw new NotImplementedException();
    }
}

虚拟方法:

虚拟意味着我们可以覆盖它。

虚函数有一个实现。当我们继承类时,我们可以覆盖虚拟函数并提供我们自己的逻辑。

方法实现时,可以更改虚函数的返回类型函数的子类(这可以说是一个概念跟踪)。

抽象方法

抽象意味着我们必须覆盖它。

抽象函数没有实现,必须在抽象类中。

它只能被声明。这迫使派生类提供它的实现。

抽象成员是隐式虚的。在某些语言中,抽象可以称为纯虚。

1
2
3
4
5
6
7
8
9
public abstract class BaseClass
{
    protected abstract void xAbstractMethod();

    public virtual void xVirtualMethod()
    {
        var x = 3 + 4;
    }
}


上面的大多数例子都使用代码——它们非常非常好。我不需要添加到他们所说的内容中,但是下面是一个简单的解释,它使用类比而不是代码/技术术语。

简单解释-使用类比解释

抽象方法

想想乔治?W?布什(George W Bush)。他对他的士兵说:"去伊拉克战斗吧。"就是这样。他所指定的是必须进行战斗。他没有具体说明具体将如何实现。但我的意思是,你不能只是出去"战斗":那到底是什么意思?我是用B-52还是derringer?那些具体的细节留给别人去处理。这是一个抽象方法。

虚方法

大卫·彼得雷乌斯在军队中地位很高。他定义了战斗的含义:

发现敌人中和他。饭后喝杯啤酒

问题是这是一种非常普遍的方法。这是一个行之有效的好方法,但有时不够具体。对彼得雷乌斯来说,好消息是他的命令有回旋余地和范围——他允许其他人根据他们的特殊要求改变他对"战斗"的定义。

私人任务布洛格斯阅读彼得雷乌斯的命令,并根据他的特殊要求,被允许执行他自己版本的战斗:

发现敌人。朝他头部开一枪。回家有啤酒。

马利基也从彼得雷乌斯那里接到同样的命令。他也要战斗。但他是个政客,不是步兵。很明显,他不能到处射杀他的政敌。因为彼得雷乌斯给了他一个虚拟的方法,马利基就可以根据自己的具体情况,实施自己版本的作战方法:

发现敌人。用捏造的罪名逮捕他。回家有啤酒。

换句话说,虚拟方法提供了样板指令——但这些都是通用指令,可以根据具体情况,由军队的各个层级的人制定得更加具体。

两者之间的区别

乔治布什没有证明任何实施细节。这必须由其他人提供。这是一个抽象方法。

另一方面,彼得雷乌斯确实提供了实施细节,但他允许下属用自己的版本推翻他的命令,如果他们能想出更好的办法的话。

希望有帮助。


抽象函数(方法):

●抽象方法是用abstract关键字声明的方法。

●它没有身体。

●它应该由派生类实现。

●如果方法是抽象的,那么类应该是抽象的。

虚函数(方法):

●虚方法是用关键字virtual声明的方法,派生类方法可以使用override关键字重写虚方法。

●是否覆盖它取决于派生类。


抽象函数不能具有主体,必须由子类覆盖

虚函数将有一个主体,可能被子类覆盖,也可能不被子类覆盖


答案已经提供了很多次,但是关于何时使用它们的问题是设计时的决策。我认为将公共方法定义捆绑到不同的接口中,并将它们放到适当抽象级别的类中,这是一个很好的实践。当最好定义一个实现一组简洁接口的非抽象类时,将一组公共抽象和虚拟方法定义转储到类中会使类成为unistanable。与往常一样,这取决于什么最适合您的应用程序的特定需求。


在c#中没有调用虚类。

函数的

抽象函数只有签名,驱动类应该用功能覆盖。虚拟函数将包含驱动器类可能覆盖的部分功能,也可能不覆盖这部分功能

你可以根据你的需要来决定。


从一般面向对象的角度来看:

关于抽象方法:当你在父类中放入一个抽象方法时,实际上你是在对子类说:嘿,注意你有一个这样的方法签名。如果你想使用它,你应该实现你自己的!

关于虚函数:当您在父类中放入虚方法时,您是在对派生类说:嘿,这里有一个功能可以为您做一些事情。如果这对你有用,就用它。如果没有,覆盖它并实现您的代码,甚至您可以在您的代码中使用我的实现!

这是关于这两个概念在OO中的不同的一些哲学


抽象方法没有实现。它在父类中声明。子类负责实现该方法。

虚方法应该在父类中有一个实现,它可以帮助子类选择是使用父类的实现,还是在子类中为该方法拥有一个新的实现。


抽象函数"只是"一个签名,没有实现。它在接口中用于声明如何使用该类。它必须在派生类中实现。

虚函数(实际上是方法)也是一个声明的函数,应该在继承层次结构类中实现。

该类的继承实例也继承实现,除非您在较低层次结构的类中实现它。


在这里,我正在编写一些示例代码,希望这是一个非常实际的示例,可以在非常基本的层次上查看接口、抽象类和普通类的行为。如果您想将此代码用作演示,还可以在github中找到它作为项目:https://github.com/usavas/JavaAbstractAndInterfaceDemo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public interface ExampleInterface {

//    public void MethodBodyInInterfaceNotPossible(){
//    }
    void MethodInInterface();

}

public abstract class AbstractClass {
    public abstract void AbstractMethod();

    //    public abstract void AbstractMethodWithBodyNotPossible(){
    //
    //    };

    //Standard Method CAN be declared in AbstractClass
    public void StandardMethod(){
        System.out.println("Standard Method in AbstractClass (super) runs");
    }
}

public class ConcreteClass
    extends AbstractClass
    implements ExampleInterface{

    //Abstract Method HAS TO be IMPLEMENTED in child class. Implemented by ConcreteClass
    @Override
    public void AbstractMethod() {
        System.out.println("AbstractMethod overridden runs");
    }

    //Standard Method CAN be OVERRIDDEN.
    @Override
    public void StandardMethod() {
        super.StandardMethod();
        System.out.println("StandardMethod overridden in ConcreteClass runs");
    }

    public void ConcreteMethod(){
        System.out.println("Concrete method runs");
    }

    //A method in interface HAS TO be IMPLEMENTED in implementer class.
    @Override
    public void MethodInInterface() {
        System.out.println("MethodInInterface Implemented by ConcreteClass runs");

    //    Cannot declare abstract method in a concrete class
    //    public abstract void AbstractMethodDeclarationInConcreteClassNotPossible(){
    //
    //    }
    }
}

在c++背景下,c#虚函数对应c++虚函数,而c#抽象方法对应c++纯虚函数


抽象函数或方法是类公开的公共"操作名",其目标和抽象类一起,主要是在对象设计中针对对象必须实现的结构提供一种约束形式。

实际上,继承自其抽象类的类必须为该方法提供实现,通常编译器在不提供实现时就会引发错误。

使用抽象类和方法非常重要,主要是为了避免在设计类时关注实现细节,从而导致类结构与实现过于相关,从而在相互协作的类之间创建依赖关系和耦合。

虚函数或方法只是对类的公共行为建模的方法,但是我们可以在继承链中自由地修改它,因为我们认为子类可能需要为该行为实现一些特定的扩展。

它们都代表了面向对象范式中的一种多形性。

我们可以同时使用抽象方法和虚拟函数来支持一个好的继承模型。

我们为解决方案的主要对象设计了一个良好的抽象结构,然后通过定位那些更倾向于进一步专门化的对象来创建基本实现,并将这些实现作为虚拟实现,最后专门化我们的基本实现,最终"覆盖"继承的虚拟实现。


我的理解:

抽象方法:

只有抽象类才能包含抽象方法。派生类还需要实现该方法,该类中没有提供任何实现。

虚拟方法:

类可以声明它们,也可以提供它们的实现。派生类还需要实现方法来覆盖它。