5

我想知道两种约定之间的区别:

  1. 使用抽象方法创建一个抽象基类,该方法稍后将在派生类上实现。
  2. 创建一个没有抽象方法的抽象基类,
    但稍后在派生类的级别添加相关方法。

有什么区别?

4

6 回答 6

10

与接口非常相似,抽象类旨在为您的类型表达一组已知操作。然而,与接口不同的是,抽象类允许您实现任何派生类型都可以使用的通用/共享功能。例如:

public abstract class LoggerBase
{
  public abstract void Write(object item);

  protected virtual object FormatObject(object item)
  {
    return item;
  }
}

在上面这个非常基本的例子中,我基本上做了两件事:

  1. 定义了我的派生类型将遵守的合同。
  2. 提供一些可以根据需要覆盖的默认功能。

鉴于我知道任何派生类型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();
  }
}

所以,关键部分是:

  1. abstract类必须被继承。
  2. abstract方法必须在派生类型中实现。
  3. virtual方法可以在派生类型中被覆盖。

在第二种情况下,因为您不会向抽象基类添加功能,所以在直接处理基类的实例时无法调用该方法。例如,如果我实现ConsoleLogger.WriteSomethingElse了 ,我不能从LoggerBase.WriteSomethingElse.

于 2011-04-08T22:16:34.210 回答
5

将抽象方法放在基类中然后在子类中实现它们的想法是,您可以使用父类型而不是任何特定的子类。例如,假设您要对数组进行排序。您可以将基类定义为

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()都必须向下转换,然后更改算法将更加困难。QuickSortersort()

于 2011-04-08T22:11:38.553 回答
2

在情况 1) 中,您可以在不知道确切类型的情况下从抽象基类型访问这些方法(抽象方法是虚拟方法)。

抽象类的重点通常是在基类上定义一些契约,然后由派生类实现(在这种情况下,重要的是要认识到接口是某种“纯抽象类”)。

于 2011-04-08T22:03:17.497 回答
0

嗯,好吧,区别在于基类会知道前者,而不是后者。

换句话说,使用基类中的抽象方法,您可以在基类中调用该抽象方法的其他方法中编写代码。

显然,如果基类没有这些方法......你不能调用它们......

于 2011-04-08T22:02:49.043 回答
0

抽象函数可以没有功能。你基本上是在说,任何子类都必须给出他们自己的这个方法的版本,但是它太笼统了,甚至无法在父类中实现。一个虚函数,基本上是说看,这里的功能可能对子类来说可能不够好,也可能不够好。所以如果足够好,就用这个方法,如果不行,那就覆盖我,提供你自己的功能……

当然,如果你重写了一个虚方法,你总是可以通过调用来引用父方法base.myVirtualMethod()

于 2012-02-16T12:28:56.817 回答
-1

好的,当你看到这样的方法时:

A.Foo();

你真正拥有的(在幕后)是这样的签名。

Foo(A x);

当您调用时,A.Foo()您实际上是在调用Foo(this)wherethis是对 A 类型对象的引用。

现在,有时您希望Foo(A|B|C|D...)whereFoo有一个方法可以采用A、B、CD 类型。但是您不想担心要传递的类型,您只需要它根据传入的类型做一些不同的事情。抽象方法让你这样做,这是他们唯一的目的。

于 2011-04-08T23:53:17.553 回答