C# 中的接口与抽象类:有什么区别以及何时使用它们

作者:微信公众号:【架构师老卢】
9-25 20:5
212

在 C# 中,接口抽象类都是面向对象编程的基础支柱,使我们能够为类定义协定和共享行为。但是什么时候应该使用接口,什么时候抽象类是更好的选择呢?了解这两个概念的细微差别是编写灵活、可维护且可扩展的 C# 代码的关键。在本文中,我们将探讨接口抽象类之间的区别,并为您提供有关如何有效使用它们的实用见解。

什么是接口?

接口的核心是定义任何实现类都必须遵循的协定。这是一种指定类应该执行哪些操作的方法,而无需规定如何实现这些操作。将其视为类的蓝图。

接口的语法

public interface IShape  
{  
    double CalculateArea();  
    double CalculatePerimeter();  
}

此处,接口定义了两个方法,即 .实现此接口的任何类都需要为这些方法提供自己的实现。IShapeCalculateAreaCalculatePerimeter

接口的主要特点:

  • 无实现:接口仅声明方法和属性;它们不提供任何实现。
  • 多重继承:一个类可以实现多个接口,从而实现更大的灵活性和可扩展性。
  • No Fields(无字段):接口不能包含字段。它们仅通过方法和属性签名来描述行为。

接口的用法示例:

public class Circle : IShape  
{  
    public double Radius { get; set; }   
   
    public double CalculateArea()  
    {  
        return Math.PI * Math.Pow(Radius, 2);  
    }    
    
   public double CalculatePerimeter()  
    {  
        return 2 * Math.PI * Radius;  
    }  
}

该类实现接口并提供自己的 and 版本。如果我们稍后添加一个类,它可以实现相同的接口,但具有不同的实现。CircleIShapeCalculateAreaCalculatePerimeterRectangle

什么是抽象类?

抽象类允许我们定义派生类的实现和契约。它可以包含抽象方法 (无 implementation) 和非抽象方法 (有 implementation)。当您想在不同类之间共享一些通用功能,但仍为特定实现留出空间时,这使其成为理想的选择。

抽象类的语法

public abstract class Shape  
{  
    public abstract double CalculateArea();  
      
    public void DisplayShapeType()  
    {  
        Console.WriteLine("This is a shape.");  
    }  
}

在这里,抽象类声明了一个抽象方法,该方法必须由任何派生类实现。它还提供了一个完全实现的方法,可供所有子类使用。ShapeCalculateAreaDisplayShapeType

抽象类的主要功能:

  • 部分实现:抽象类可以同时具有抽象方法(无实现)和具体方法(有实现)。
  • 单一继承:一个类只能继承自一个抽象类,但可以实现多个接口。
  • 字段和属性:抽象类可以包含字段和属性,从而允许在子类之间共享数据。

抽象类的示例用法:

public class Rectangle : Shape  
{  
    public double Length { get; set; }  
    public double Width { get; set; }  
  
    public override double CalculateArea()  
    {  
        return Length * Width;  
    }  
}

在这里,该类派生自抽象类,并且是实现该方法所必需的。但是,它无需重新实现即可继承该方法。RectangleShapeCalculateAreaDisplayShapeType

接口和抽象类之间的主要区别

现在,让我们分解一下接口和抽象类之间的一些关键区别,帮助您确定何时使用每个接口:

  1. 目的
  • 接口用于定义必须遵循的协定。它就像一个类应该执行的操作列表,但不规定如何执行。
  • 另一方面,抽象类允许您定义常见行为和协定。它允许在多个类之间共享代码,同时仍确保每个派生类实现特定方法。

2. 实施

  • 接口中,不能提供任何方法实现。由 implementation 类提供详细信息。
  • 使用抽象类,您可以为方法提供一些默认实现,同时将其他抽象实现留给子类实现。

3. 继承

  • 一个类可以实现多个接口,这允许更大的灵活性和不同合约的混合。
  • 但是,一个类只能从一个抽象类继承,因为 C# 不支持类的多重继承。

4. 字段和属性

  • 接口不能包含字段或构造函数。它们纯粹是为了定义行为。
  • 抽象类可以具有字段、属性甚至构造函数,这意味着它们可以存储公共数据或提供共享的初始化逻辑。

何时使用接口

  1. 多个实现:如果您的类需要遵循多个 Contract,那么 interfaces 是您的不二之选。由于 C# 不支持多重继承,因此接口允许您创建灵活的系统。
  • 示例:一个类可以实现 both 和 interfaces,以遵循既是设备又具有可打印属性的协定。PrinterIDeviceIPrintable

2. 不相关的类:当类不属于同一继承层次结构但需要执行类似的操作时,接口有助于将它们统一在一个公共契约下。

  • 示例:两者 和 都可以实现 ,即使它们是根本不同的类。DogPrinterIRunnable

何时使用抽象类

  1. 共享代码:如果要为派生类提供一些共享实现(但不是全部),则抽象类是一个不错的选择。它允许您定义常见行为,同时确保子类仍覆盖 key 方法。
  • 示例:如果所有形状都有一个计算面积的方法,但也共享一个方法,则可以将通用功能放在一个抽象类中。DisplayShapeType

2. 基本功能:当您需要一个基类来提供基础功能,同时仍然强制执行某些方法覆盖时。

  • 示例:可以是提供类似 .VehicleStartEngineCalculateFuelEfficiency

组合接口和抽象类

将抽象类和接口组合在一起以获得两者的好处也很常见。您可以定义一个实现一个或多个接口的抽象类,然后具体类可以从抽象类派生,同时仍然遵守接口协定。

public interface IMovable  
{  
    void Move();  
}  
public abstract class Vehicle : IMovable  
{  
    public void Move()  
    {  
        Console.WriteLine("The vehicle is moving.");  
    }  
  
    public abstract void Refuel();  
}  
  
public class Car : Vehicle  
{  
    public override void Refuel()  
    {  
        Console.WriteLine("Refueling the car.");  
    }  
}

在这里,抽象类实现了接口,但也定义了自己的抽象方法 。该类必须实现,但免费获取该方法。VehicleIMovableRefuelCarRefuelMove

在 C# 中的接口和抽象类之间进行选择归根结底是要了解设计的具体要求。如果你正在创建一个多个不相关的类应该遵守的 Contract,请选择 interfaces。如果您需要在相关类之间共享通用功能,同时仍然强制执行一些类似 Contract 的行为,那么抽象类是您的朋友。

相关留言评论
昵称:
邮箱:
阅读排行