【所见即所得】抽象类、接口的一点费解
一、问题引入:
看一些开源项目源码的时候,经常看到如下的结构设计:
我表示很费解。
二、问题分析:
为了分析问题,我写了几个测试的类和接口,如下:
接口:
1 2 3 4 5 6 | public interface A { string Test1(); void Test2(); } |
抽象类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public abstract class B :A{ #region A 成员 public virtual string Test1() { Console.WriteLine( "abstract B Test1" ); return "B Test1" ; } public virtual void Test2() { Console.WriteLine( "abstract B Test2" ); } #endregion } |
同时继承抽象类和实现接口的具体类:
1 2 | public class C :B,A{ } |
只继承抽象类的具体类:
1 2 | public class D :B{ } |
测试也下各种用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | A a1 = new C(); A a2 = new D(); B b1 = new C(); B b2 = new D(); a1.Test1(); a2.Test1(); Console.WriteLine(a1.GetType().GetInterfaces()[0]); Console.WriteLine(a2.GetType().GetInterfaces()[0]); Console.WriteLine(a1.GetType().BaseType); Console.WriteLine(a2.GetType().BaseType); Console.WriteLine( "-----------------------------------------------" ); Console.WriteLine(b1.GetType().GetInterfaces()[0]); Console.WriteLine(b2.GetType().GetInterfaces()[0]); Console.WriteLine(b1.GetType().BaseType); Console.WriteLine(b2.GetType().BaseType); |
结果如下:
也就是这两个具体类从表面上(运行结果)看没有什么不同。
我仔细的查看了一下他们的IL代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | . class public auto ansi beforefieldinit D extends FMS_Refacting.interfaceAndabsctract.B { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void FMS_Refacting.interfaceAndabsctract.B::.ctor() L_0006: ret } } |
1 2 | . class public auto ansi beforefieldinit C extends FMS_Refacting.interfaceAndabsctract.B |
implements FMS_Refacting.interfaceAndabsctract.A
1 2 3 4 5 6 7 8 9 10 11 12 | { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void FMS_Refacting.interfaceAndabsctract.B::.ctor() L_0006: ret } } |
唯一的不同就是红色部分标出。但这不会影响什么,因为根据继承的定义,D是通过B来implements了A的,这种传递性和上面的没有什么区别。所以从这里看也没什么不同。
也就是说,这个两种形式是一样的!!!
我们知道:
接口:是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值。接口方法不能包含任何实现,CLR允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数。但是注意:C#中不能包含任何静态成员。一个类可以实现多个接口,当一个类继承某个接口时,它不仅要实现该接口定义的所有方法,还要实现该接口从其他接口中继承的所有方法。
抽象类:提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法,因此对抽象类不能使用new关键字,也不能被密封。如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。另外,实现抽象方法由override方法来实现。
抽象类和接口比起来,有一个好处,就是某些函数可以实现具体的方法,而并不一定是声明抽象的方法,而接口只能声明抽象方法(并且强制子类实现),所以用一个抽象类来实现某个接口可以实现一些通用的方法,而这些具体实现的方法里还可以调用抽象方法,所以减少了子类中的重复代码。
所以,D的继承形式我觉着是比较常用的,但是为什么又出现C继承形式了呢?我的猜想:
- 1、应该是框架上的需要便于扩展?
- 2、与设计模式有关?
- 3、美观?
三、最后
我还是不能确定为什么要这么设计,有什么特殊的原因是我没有想到的么?如果你知道请告诉我,我也会一直的寻找原因。不胜感激。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库