抽象方法使用vs常规方法

时间:2011-04-08 22:00:41

标签: c# abstract-class abstract-methods

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

  1. 使用抽象方法创建抽象基类 稍后将在派生类上实现。
  2. 创建没有抽象方法的抽象基类 但稍后在派生类的级别上添加相关方法。
  3. 有什么区别?

6 个答案:

答案 0 :(得分:9)

与接口非常相似,抽象类旨在表达您的类型的一组已知操作。但是,与接口不同,抽象类允许您实现可由任何派生类型使用的公共/共享功能。 E.g:

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

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

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

  1. 定义了我的派生类型符合的合同。
  2. 提供一些默认功能,如果需要,可以覆盖。
  3. 鉴于我知道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方法可以在派生类型中覆盖。
    4. 在第二种情况下,因为您不会将功能添加到抽象基类,所以在直接处理基类的实例时无法调用该方法。例如,如果我实施ConsoleLogger.WriteSomethingElse,我就无法从LoggerBase.WriteSomethingElse调用它。

答案 1 :(得分:4)

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

的类
abstract class Sorter {
    public abstract Array sort(Array arr);
}

然后,您可以在子类中实现各种算法,例如quicksort,mergesort,heapsort。

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(),然后更改算法将更加困难。

答案 2 :(得分:2)

在案例1)中,您可以从抽象基类型访问这些方法,而无需知道确切的类型(抽象方法是虚方法)。

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

答案 3 :(得分:0)

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

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

显然,如果基类没有那些方法......你就不能称之为......

答案 4 :(得分:0)

抽象函数不具备任何功能。你基本上是在说,任何一个子类必须给出他们自己的这个方法的版本,但是它太普遍甚至不能尝试在父类中实现。一个虚函数,基本上就是说看,这里的功能对于子类来说可能是也可能不够好。所以,如果它足够好,请使用此方法,如果没有,则覆盖我,并提供您自己的功能......

当然,如果你覆盖虚方法,你总是可以通过调用base.myVirtualMethod()来引用父方法

答案 5 :(得分:-1)

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

A.Foo();

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

Foo(A x);

当你致电A.Foo()时,你真的在​​呼叫Foo(this),其中this是对A类型对象的引用。

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