Answers:
抽象函数不能具有功能。您基本上是在说,任何子类都必须提供自己的该方法的版本,但是它太笼统了,甚至无法尝试在父类中实现。
虚函数基本上是在说看,这里的功能对于子类可能足够好,也可能不够好。因此,如果足够好,请使用此方法;否则,请覆盖我并提供您自己的功能。
抽象函数没有实现,只能在抽象类上声明。这将强制派生类提供实现。
虚函数提供了默认实现,它可以存在于抽象类或非抽象类上。
因此,例如:
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();
}
MyBase
类不必以某种方式实现抽象类吗?我不经常这样做,所以我可能会误会。我在您的示例中看不到。
abstract
班级可以有abstract
成员。abstract
类从继承abstract
类必须 override
的abstract
成员。abstract
成员是隐式virtual
。abstract
成员不能提供任何实现(abstract
被称为pure virtual
在某些语言)。virtual
或非virtual
。一个abstract
成员(即抽象属性,抽象方法)就像是一个虚拟的方法,即可以覆盖它,但它不与自身携带的默认实现。
您必须始终重写抽象函数。
从而:
抽象方法:当类包含抽象方法时,必须将该类声明为抽象方法。抽象方法没有实现,因此,从该抽象类派生的类必须为该抽象方法提供一个实现。
虚方法:类可以具有虚方法。虚拟方法有一个实现。当您从具有虚拟方法的类继承时,可以覆盖该虚拟方法并提供其他逻辑,或者将逻辑替换为自己的实现。
何时使用什么:在某些情况下,您知道某些类型应具有特定的方法,但是您不知道此方法应具有的实现。
在这种情况下,您可以创建一个包含带有此签名的方法的接口。但是,如果遇到这种情况,但您知道该接口的实现者还将有另一个通用方法(您已经可以为其提供实现),则可以创建一个抽象类。然后,该抽象类包含抽象方法(必须重写)和另一个包含“公共”逻辑的方法。
如果您有一个可以直接使用的类,但是您希望继承者能够更改某些行为,但不是必须的,则应使用虚拟方法。
说明:类推。希望它将对您有所帮助。
语境
我在建筑物的21楼工作。我对火灾抱有偏执。时不时地,在世界某个地方,大火烧毁了刮板。但幸运的是,在这里,我们有一份说明手册,介绍发生火灾时的处理方法:
火灾逃生()
这基本上是一个称为FireEscape()的虚拟方法
虚方法
该计划在99%的情况下都非常好。这是可行的基本计划。但是只有1%的可能性防火通道会被阻塞或损坏,在这种情况下,您将完全被拧紧,除非采取严厉措施,否则您将变成烤面包。使用虚方法,您可以做到这一点:您可以使用自己的计划版本覆盖基本的FireEscape()计划:
换句话说,虚拟方法提供了一个基本计划,如果需要,可以将其覆盖。如果程序员认为合适,子类可以覆盖父类的虚拟方法。
抽象方法
并非所有组织都进行了深入研究。一些组织不进行消防演习。他们没有整体的逃生政策。每个人都是他自己。管理层仅对现有这样的政策感兴趣。
换句话说,每个人被迫发展自己的FireEscape()方法。一个人会走出防火梯。另一个家伙会降落伞。另一个家伙将使用火箭推进技术从建筑物上飞走。另一个家伙会逃走。只要您有基本的FireEscape()计划,管理层就不会在意您如何逃脱-如果没有,您可以保证OHS会像一吨砖一样落在组织上。这就是抽象方法的含义。
两者又有什么区别?
抽象方法:子类被强制实现自己的FireEscape方法。使用虚拟方法,您有一个基本的计划在等着您,但是如果不够好,可以选择实施自己的计划。
现在不是那么难吗?
抽象方法是必须实现以构成具体类的方法。该声明在抽象类中(任何具有抽象方法的类都必须是抽象类),并且必须在具体类中实现。
虚方法是一种可以使用覆盖在派生类中覆盖,替换超类中的行为的方法。如果不覆盖,则会得到原始行为。如果这样做,您总是会得到新的行为。这与非虚拟方法相反,后者不能被覆盖但可以隐藏原始方法。这是使用new
修饰符完成的。
请参见以下示例:
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
并调用SayHello
或时SayGoodbye
,我得到“嗨,那里”和“以后见”。如果我致电HelloGoodbye
,我会收到“你好”和“稍后见”。这是因为它SayGoodbye
是虚拟的,可以用派生类代替。SayHello
只是隐藏的,所以当我从基类中调用它时,便得到了原始方法。
抽象方法是隐式虚拟的。它们定义了必须存在的行为,更像是界面。
通过对以下类进行一些改进(通过其他答案),我使此过程变得更简单:
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\n");
}
public virtual void SayGoodbye()
{
Console.WriteLine("Goodbye\n");
}
public void HelloGoodbye()
{
this.SayHello();
this.SayGoodbye();
}
}
public abstract class AbstractClass
{
public void SayHello()
{
Console.WriteLine("Hello\n");
}
//public virtual void SayGoodbye()
//{
// Console.WriteLine("Goodbye\n");
//}
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
:
“亲爱的程序员,请通过做自己最擅长的工作:将适当的代码绑定到该名称上:发明 ”
为了完整性,重载是指:
“亲爱的编译器,请执行您最擅长的工作:将适当的代码绑定到该名称:排序 ”。
虚方法:
虚拟意味着我们可以覆盖它。
虚函数有一个实现。当我们继承该类时,我们可以覆盖虚函数并提供我们自己的逻辑。
抽象方法
抽象意味着我们必须重写它。
抽象函数没有实现,必须在抽象类中。
它只能被声明。这将强制派生类提供其实现。
抽象成员是隐式虚拟的。在某些语言中,抽象可以称为纯虚拟的。
public abstract class BaseClass
{
protected abstract void xAbstractMethod();
public virtual void xVirtualMethod()
{
var x = 3 + 4;
}
}
我在某些地方看到了抽象方法的定义如下。**
“必须在子类中实现抽象方法”
**我感觉就像。
如果子类也是abstract,则不必在子类中实现抽象方法。
1)抽象方法不能是私有方法。2)不能在同一抽象类中实现Abstract方法。
我要说的是..如果我们要实现一个抽象类,则必须重写基本抽象类中的抽象方法。因为..实现抽象方法是使用覆盖关键字。类似于Virtual方法。
不必在继承的类中实现虚拟方法。
----------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();
}
}
上面的大多数示例都使用代码-而且非常好。我无需多说,但以下是使用类比而不是代码/技术术语的简单说明。
简单说明-使用类比说明
抽象方法
想想乔治·W·布什。他对士兵说:“去伊拉克打仗”。就是这样。他所指定的只是必须打架。他没有具体说明将如何发生。但是我的意思是,你不能只是出去战斗而已:这到底是什么意思?我要和B-52或我的杀人狂战斗吗?这些具体细节留给别人。这是一种抽象方法。
虚方法
大卫·彼得雷乌斯(David Petraeus)参军。他定义了战斗的含义:
问题在于这是一种非常通用的方法。这是一个行之有效的好方法,但有时不够具体。对彼得雷乌斯来说,好事是他的命令有余地和范围-他允许其他人根据他们的特殊要求更改他对“战斗”的定义。
Private Job Bloggs阅读Petraeus的命令,并根据他的特殊要求获准实施自己的战斗版本:
努里·马利基(Nouri al Maliki)也收到彼得雷乌斯(Petraeus)的同样命令。他也要战斗。但是他是政客,而不是步兵。显然,他不能四处射击他的政客敌人。由于彼得雷乌斯为他提供了一种虚拟方法,因此马利基可以根据自己的具体情况实施自己的战斗方法版本:
换句话说,虚拟方法提供样板指令-但这只是一般性指令,军队中的各个阶层的人可以根据自己的具体情况使这些指令更加具体。
两者的区别
乔治·布什(George Bush)没有证明任何实施细节。这必须由其他人提供。这是一种抽象方法。
另一方面, Petraeus 确实提供了实现细节,但他已允许他的下属使用他们自己的版本覆盖其命令,如果他们能提出更好的建议。
希望能有所帮助。
抽象函数不能具有主体,并且必须由子类覆盖
虚函数将具有主体,并且可能会或可能不会被子类覆盖
从一般面向对象的角度来看:
关于抽象方法:实际上,当您将抽象方法放在父类中时,您对子类说的是:嘿,请注意您具有这样的方法签名。如果您想使用它,则应该自己实现!
关于虚函数:在父类中放置虚方法时,您对派生类说的是:嘿,这里有一项功能可以为您做一些事情。如果这对您有用,请使用它。如果没有,重写它并实现您的代码,即使您可以在代码中使用我的实现!
这是关于通用OO中这两个概念之间差异的一些哲学
C#中没有调用虚拟类的内容。
对于功能
您可以根据自己的要求决定。
抽象方法没有实现,它是在父类中声明的。子类可用于实现该方法。
虚方法应该在父类中具有一个实现,并且它方便子类选择是使用父类的该实现还是为子类中的该方法为其自身提供新的实现。
抽象函数或方法是类公开的公共“操作名称”,其目的与抽象类一起,主要是在对象设计中针对对象必须实现的结构提供了一种约束形式。
实际上,从其抽象类继承的类必须为此方法提供实现,通常,编译器在不这样做时会引发错误。
使用抽象类和方法非常重要,这可以避免在设计类时只关注实现细节,从而使类结构与实现过于相关,从而在相互协作的类之间建立依赖关系和耦合。
虚函数或方法只是为类的公共行为建模的方法,但是我们可以自由地在继承链中对其进行修改,因为我们认为子类可能需要对该行为进行某些特定的扩展。
它们都代表了面向对象范式中的多态性形式。
我们可以一起使用抽象方法和虚函数来支持良好的继承模型。
我们设计了解决方案主要对象的良好抽象结构,然后通过查找更易于进一步专业化的对象来创建基本实现,并将这些实现设为虚拟,最后,我们对基本实现进行了专业化,最终“覆盖”了继承的虚拟实现。
在这里,我正在编写一些示例代码,希望这可能是一个非常实际的示例,以便在非常基本的级别上查看接口,抽象类和普通类的行为。如果要使用它作为演示,也可以在github中找到该代码作为项目:https : //github.com/usavas/JavaAbstractAndInterfaceDemo
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(){
//
// }
}
}