我想知道两种约定之间的区别:
- 使用抽象方法创建一个抽象基类,该方法稍后将在派生类上实现。
- 创建一个没有抽象方法的抽象基类,
但稍后在派生类的级别添加相关方法。
有什么区别?
我想知道两种约定之间的区别:
有什么区别?
与接口非常相似,抽象类旨在为您的类型表达一组已知操作。然而,与接口不同的是,抽象类允许您实现任何派生类型都可以使用的通用/共享功能。例如:
public abstract class LoggerBase
{
public abstract void Write(object item);
protected virtual object FormatObject(object item)
{
return item;
}
}
在上面这个非常基本的例子中,我基本上做了两件事:
鉴于我知道任何派生类型LoggerBase
都会有一个Write
方法,我可以调用它。上述作为接口的等价物可能是:
public interface ILogger
{
void Write(object item);
}
作为一个抽象类,我可以提供一个可以选择覆盖的附加服务FormatObject
,比如我正在编写一个ConsoleLogger
,例如:
public class ConsoleLogger : LoggerBase
{
public override void Write(object item)
{
Console.WriteLine(FormatObject(item));
}
}
通过将方法标记FormatObject
为虚拟,这意味着我可以提供一个共享的实现。我也可以覆盖它:
public class ConsoleLogger : LoggerBase
{
public override void Write(object item)
{
Console.WriteLine(FormatObject(item));
}
protected override object FormatObject(object item)
{
return item.ToString().ToUpper();
}
}
所以,关键部分是:
abstract
类必须被继承。abstract
方法必须在派生类型中实现。virtual
方法可以在派生类型中被覆盖。在第二种情况下,因为您不会向抽象基类添加功能,所以在直接处理基类的实例时无法调用该方法。例如,如果我实现ConsoleLogger.WriteSomethingElse
了 ,我不能从LoggerBase.WriteSomethingElse
.
将抽象方法放在基类中然后在子类中实现它们的想法是,您可以使用父类型而不是任何特定的子类。例如,假设您要对数组进行排序。您可以将基类定义为
abstract class Sorter {
public abstract Array sort(Array arr);
}
然后可以在子类中实现快速排序、归并排序、堆排序等各种算法。
class QuickSorter {
public Array sort(Array arr) { ... }
}
class MergeSorter {
public Array sort(Array arr) { ... }
}
您通过选择一种算法来创建一个排序对象,
Sorter sorter = QuickSorter();
现在您可以sorter
四处走动,而不会暴露引擎盖下它是快速排序的事实。要对你说的数组进行排序
Array sortedArray = sorter.sort(someArray);
通过这种方式,实现的细节(您使用哪种算法)从接口到对象(它对数组进行排序的事实)解耦。
一个具体的优势是,如果在某些时候您想要一种不同的排序算法,那么您可以QuickSort()
在MergeSort
这一行中进行更改,而无需在其他任何地方进行更改。如果在父级中不包含方法,则每次调用时sort()
都必须向下转换,然后更改算法将更加困难。QuickSorter
sort()
在情况 1) 中,您可以在不知道确切类型的情况下从抽象基类型访问这些方法(抽象方法是虚拟方法)。
抽象类的重点通常是在基类上定义一些契约,然后由派生类实现(在这种情况下,重要的是要认识到接口是某种“纯抽象类”)。
嗯,好吧,区别在于基类会知道前者,而不是后者。
换句话说,使用基类中的抽象方法,您可以在基类中调用该抽象方法的其他方法中编写代码。
显然,如果基类没有这些方法......你不能调用它们......
抽象函数可以没有功能。你基本上是在说,任何子类都必须给出他们自己的这个方法的版本,但是它太笼统了,甚至无法在父类中实现。一个虚函数,基本上是说看,这里的功能可能对子类来说可能不够好,也可能不够好。所以如果足够好,就用这个方法,如果不行,那就覆盖我,提供你自己的功能……
当然,如果你重写了一个虚方法,你总是可以通过调用来引用父方法base.myVirtualMethod()
好的,当你看到这样的方法时:
A.Foo();
你真正拥有的(在幕后)是这样的签名。
Foo(A x);
当您调用时,A.Foo()
您实际上是在调用Foo(this)
wherethis
是对 A 类型对象的引用。
现在,有时您希望Foo(A|B|C|D...)
whereFoo
有一个方法可以采用A、B、C或D 类型。但是您不想担心要传递的类型,您只需要它根据传入的类型做一些不同的事情。抽象方法让你这样做,这是他们唯一的目的。