当前位置:首页 > 技术分析 > 正文内容

C# 中抽象类与接口的选择

ruisui883个月前 (03-11)技术分析19

在设计应用程序时,了解何时使用抽象类和何时使用接口是至关重要的。尽管抽象类和接口在某些方面看起来相似,但它们之间存在关键差异,这些差异将决定哪种选择最适合您要实现的目标。

抽象类与接口的基本区别

抽象类

抽象类是一种特殊类型的类,不能被实例化。抽象类旨在被子类继承,这些子类可以实现或重写其方法。换句话说,抽象类可以是部分实现的,也可以完全不实现。抽象类可以包含具体方法和抽象方法,并且可以有构造函数。

public abstract class Animal
{
    public abstract void MakeSound();
    
    public void Sleep()
    {
        Console.WriteLine("Sleeping...");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

接口

接口基本上是一个契约,它不包含任何实现。接口只能包含方法声明,不能包含方法定义,也不能包含成员数据。实现接口的类必须实现接口中的所有成员。

public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

抽象类与接口的相似之处

  1. 行为定义而非实现:C# 提供了接口和抽象类来定义行为而不实现。可以利用接口或抽象类来定义一个契约,必须由扩展或实现该接口或抽象类的类型遵循。
  2. 不可实例化:抽象类和接口都不能被实例化,必须由其他类型实现或扩展。
  3. 支持多态性:可以使用接口和抽象类实现多态性。
  4. 访问修饰符:接口和抽象类中的方法都可以包含访问修饰符。

抽象类与接口的关键区别

  1. 默认实现:抽象类可以有默认实现的方法,而接口直到 C# 8.0 才支持其成员的默认实现。
  2. 成员类型:抽象类可以有字段、构造函数、析构函数和静态成员,而接口不能有这些成员。
  3. 状态信息:抽象类可以有状态信息(即字段),而接口不能有实例字段。
  4. 继承:一个类只能继承一个抽象类,但可以实现多个接口。

何时使用抽象类

状态管理

如果需要管理状态信息,可以使用抽象类。抽象类可以包含字段,子类可以访问这些字段。

public abstract class Animal
{
    protected string name;
    
    public Animal(string name)
    {
        this.name = name;
    }
    
    public abstract void MakeSound();
}

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    
    public override void MakeSound()
    {
        Console.WriteLine($"{name} says: Bark");
    }
}

访问修饰符

抽象类可以使用访问修饰符来提供对其成员的细粒度控制。

public abstract class Animal
{
    protected string name;
    
    public Animal(string name)
    {
        this.name = name;
    }
    
    public abstract void MakeSound();
    
    protected void Sleep()
    {
        Console.WriteLine("Sleeping...");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    
    public override void MakeSound()
    {
        Console.WriteLine($"{name} says: Bark");
    }
}

构造函数和析构函数

抽象类可以有构造函数和析构函数,允许初始化其成员并编写清理逻辑的代码。

public abstract class Animal
{
    protected string name;
    
    public Animal(string name)
    {
        this.name = name;
    }
    
    public abstract void MakeSound();
    
    ~Animal()
    {
        Console.WriteLine($"{name} is being destroyed");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    
    public override void MakeSound()
    {
        Console.WriteLine($"{name} says: Bark");
    }
}

何时使用接口

多重实现

当需要在应用程序中建立契约而不强制直接继承时,接口是首选。一个类可以实现多个接口,从而允许它同时符合多个契约。

public interface IAnimal
{
    void MakeSound();
}

public interface IPet
{
    void Play();
}

public class Dog : IAnimal, IPet
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
    
    public void Play()
    {
        Console.WriteLine("Playing...");
    }
}

模块化

接口促进了一种设计,在这种设计中,定义了一个契约,任何实现该契约的类都必须遵循,从而促进了模块化、可扩展和灵活的架构。

public interface IRepository
{
    void Add(T item);
    T Get(int id);
}

public class Repository : IRepository
{
    public void Add(T item)
    {
        // Implementation
    }
    
    public T Get(int id)
    {
        // Implementation
        return default(T);
    }
}

模拟

在编写单元测试时,可以使用接口创建模拟对象。因为可以在接口中定义行为而不实现,所以可以使用接口创建不依赖于具体类实现的单元测试。

public interface IAnimal
{
    void MakeSound();
}

public class AnimalMock : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Mock sound");
    }
}

设计灵活性

可以利用接口创建具有演变设计的应用程序,通过编程接口而不是其实现,可以处理抽象而不是具体类,从而促进组件的可互换性。

public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

public class AnimalService
{
    private readonly IAnimal _animal;
    
    public AnimalService(IAnimal animal)
    {
        _animal = animal;
    }
    
    public void MakeAnimalSound()
    {
        _animal.MakeSound();
    }
}

总结

抽象类和接口各有优缺点,选择哪一个取决于具体的需求。如果需要定义状态信息并指定字段的初始化方式,应该使用抽象类。如果需要定义一个行为契约而不强制继承关系,应该使用接口。通过理解它们的区别和应用场景,可以设计出松耦合和可扩展的应用程序。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/2698.html

标签: ts void
分享给朋友:

“C# 中抽象类与接口的选择” 的相关文章

10款超牛Vim插件,爱不释手了

我是一个忠实的Vim编辑器用户,从事开发工作多年,我一直都非常喜欢使用Vim。轻量、便捷,而且,熟悉了Vim相关的快捷键之后,效率能够成倍的提升。除了这些之外,Vim像很多知名的IDE、编辑器一样,也支持插件配置,通过这些插件,可以实现更多高级、高效的操作。今天,就来给大家分享10个我特别喜欢的Vi...

分享15个基于Vue3.0全家桶的优秀开源项目

大家好,我是 Echa。今天来分享 15 个基于 Vue3.0 全家桶的优秀开源项目!1. Vue Admin Bettergithub : https://github.com/chuzhixin/vue-admin-bettervue admin better 对比其他来源 admin 框架有如...

企业微信自建应用和消息发送配置对接系统指南

本文介绍企业微信应用创建、消息提醒、自动回复、自定义菜单和服务端接口对接过程。企业微信登录:https://work.weixin.qq.com/企业微信接口对接,应用授权和发送消息代码:https://www.easywechat.com/docs/5.x/wework/oauth一、创建自建应用...

微信正开发“应用号”取代手机应用

长江商报消息用户只需关注公众号,不必下载APP就可获得相同体验本报讯(记者 陈妮希)昨日,2016微信公开课PRO版在广州举行,腾讯公司高级执行副总裁、微信事业群总裁张小龙首次公开演讲,并透露微信正在开发“应用号”,将应用和订阅号相结合。现场,微信团队还首次发布了腾讯生物识别标准“TENCENTSO...

同步电机和异步电机竟然有这么大区别,看完就理解了

同步电机和异步电机的主要区别是:同步电机能与其定子磁场旋转达到同步转速,异步电机转速达不到定子磁场的同步转速。电机大致分成三种,同步机,异步机(以上两种多与电网相连),还有个直流电机。下面的内容是一个过渡,只作为对电机(同步机、异步机)原理性的知识进行形象的讲解(懂电机的可跳过)。同步机和异步机,这...

能者多劳!让NVMe固态硬盘做系统盘的同时,加速SATA数据盘

不知不觉当中,固态硬盘已经取代机械硬盘成为主流。越来越多的玩家已经淘汰机械盘,使用NVMe+SATA的固态硬盘高低搭配。既然是高低搭配,就一定会有性能差距,是否能从NVMe固态硬盘中划分出一小部分空间来给SATA固态硬盘加速,实现更好地整机性能呢?答案是肯定的,而且这一功能早已隐藏在英特尔Z170、...