C#之继承实现
作者:Ruby_Lu 发布时间:2023-04-10 16:51:41
一.继承的类型
在面向对象的编程中,有两种截然不同继承类型:实现继承和接口继承
1.实现继承和接口继承
*实现继承:表示一个类型派生于基类型,它拥有该基类型的所有成员字段和函数。在实现继承中,派生类型采用基类型的每个函数的实现代码,除非在派生类型的定义中指定某个函数的实现代码。在需要给现有的类型添加功能,或许多相关的类型共享一组重要的公共功能时,可以使用这种类型的继承。
*接口继承:表示一个类型只继承了函数的签名,没有继承任何的代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。
2.多重继承
C#不支持多重继承,但C#允许类型派生自多个接口————多重接口继承。这说明,C#类可以派生自另一个类和任意多个接口。更准确的说,因为System.Object是一个公共的基类,所以每个C#(除Object之外)都有一个基类,还可以有任意多个接口。
3.结构的继承
使用结构的一个限制是结构不支持实现继承,但每个结构都自动派生自System.ValueType。不能编码实现类型层次的结构,但结构可以实现接口。
二.继承的实现
语法:
class MyDreved:BaseClass
{
}
如果类或结构也派生自接口,则用逗号分隔列表中的基类和接口:
class MyDreved:BaseClass,IIntenface1,IIntenface2
{
}
如果在类定义中没有指定基类,C#编译器就假定System.Object是基类。
1.虚方法
把一个基类函数声明为virtual,就可以在任何派生类中重写(override)该函数:
class BaseClass
{
public virtual void VirtualMethod()
{
//
}
}
也可以把属性声明为virtual。对于虚属性或重写属性,语法与非虚属性相同,但要在定义中添加virtual关键字:
public virtual string Name
{
get;set;
}
C#中虚函数的概念与标准OOP的概念相同:可以在派生类中重写虚函数。在调用方法时,会调用该派生类的合适方法。在C#中,函数默认情况下不是虚的,但(除了构造函数)可以显式的声明为virtual。
在派生类中重写一个函数时,要使用override关键字显示声明:
class MyDreved: BaseClass
{
public override void VirtualMethod()
{
//
}
}
成员字段和静态函数都不能声明为virtual,因为这个概念只对类中的实例函数成员有意义。
2.隐藏方法
如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和override,派生类方法就会隐藏基类方法。
class A
{
public void a()
{
Console.WriteLine('CLASS is A');
}
}
class B:A
{
public void a()
{
Console.WriteLine('CLASS is B');
}
}
class client
{
static void main()
{
B b=new B();
A a=b;
a.a();
b.a();
}
}
/*输出
CLASS IS A
CLASS IS B
*/
在大多数情况下,是要重写方法,而不是隐藏方法,因为隐藏方法会造成对于给定类的实例调用错误的方法。但是,C#语法会在编译时收到这个潜在错误的警告。
在C#中,要隐藏一个方法应使用new 关键字声明,这样在编译时就不会发出警告:
class A
{
public void a()
{
Console.WriteLine('CLASS is A');
}
}
class B:A
{
public new void a()
{
Console.WriteLine('CLASS is B');
}
}
3.调用函数的基类版本
C#可以从派生类中调用方法的基本版本:base.<MethodName>()
class MyDreved: BaseClass
{
public override void VirtualMethod()
{
base.VirtualMethod();
}
}
可以使用base.<MethodName>()语法调用基类中的任何方法,不必从同一方法的重载中调用它。
4.抽象类和抽象函数
C#允许把类和函数声明为abstract.抽象类不能实例化,而抽象不能直接实现,必须在非抽象的派生类中重写。显然抽象函数也是虚拟的(尽管不需要提供virtual,实际上,也不能提供该关键字)。
如果类包含抽象函数,则该类也是抽象的,也必须声明为抽象的:
abstract class Building
{
public abstract void Cal();
}
抽象类中不能声明非抽象方法,但可以声明其它的非抽象成员。
5.密封类和密封方法
C#允许把类和方法声明为sealed。对于类,这表示不能继承该类;对于方法,表示不能重写该方法。
sealed class A
{
}
class B:A //报错
{
}
如果基类上不希望有重写的方法和属性,就不要把它声明为virtual.
6.派生类的构造函数
假定没有为任何类定义任何显示的构造函数,编译器就会为所有的类提供默认的初始化构造函数,在后台编译器可以很好的解决类的层次结构中的问题,每个类中的每个字段都会初始化为对应的默认值。
在创建派生类的实例时,实际上会有多个构造函数起作用。要实例化的类的构造函数本身不能初始化类,还必须调用基类中的构造函数。
构造函数的调用顺序是先调用Object,在按照层次结构调用基类的构造函数,由基类到父类,直到到达要实例化的类为止。在这个过程中,每个构造函数都初始化它自己的类中的字段。因为最先调用的总是基类的构造函数,所以派生类在执行过程中可以访问任何基类的成员,因为基类已经构造出来了,其字段也初始化了。
*在层次结构中添加无参数的构造函数 在层次结构中添加一个无参数的构造函数会替换默认的构造函数,所以在执行过程中,会默认调用基类中添加的无参数的构造函数。其它方面不变。
*在层次结构中添加带参数的构造函数 在层次结构中要调用这个带参数的构造函数,需要在父类的构造函数中显示调用:
public abstract class GenericCustomer
{
private string name;
public GenericCustomer()
{
name = "<no name>";
}
public GenericCustomer(string name)
{
this.name = name;
}
public string Name
{
get {return name;}
set {name = value;}
}
}
public class Nevermore60Customer : GenericCustomer
{
private string referrerName;
private uint highCostMinutesUsed;
ublic Nevermore60Customer(string name) : this(name, " <None>")
{
}
public Nevermore60Customer(string name, string referrerName) : base(name)
{
this.referrerName = referrerName;
}
public string ReferrerName
{
get {return referrerName;}
set {referrerName = value;}
}
}
三. 修饰符
修饰符可以指定方法的可见性:如public或private,还可以指定一项的本质,如方法是virtual或abstract.
1.可见性修饰符
修饰符 | 应用于 | 说明 |
---|---|---|
public | 所有类和成员 | 任何代码可以访问 |
protected | 类的成员和内嵌类 | 只有在类内部和派生类中访问 |
internal | 所有类和成员 | 只有在类内部和包含它的程序集中访问 |
private | 类的成员和内嵌类 | 只有在类内部访问 |
protected internal | 类的成员和内嵌类 | 只有在类内部,派生类中和包含它的程序集中访问 |
不能把类定义为protected,private,protected internal,因为这些修饰符对于包含在名称空间中的类型没有意义。因此这些修饰符只能应用于成员。但是可以用这些修饰符定义嵌套的类(内嵌类,包含在其它类中的类),因为在这种情况下,类也具有成员的状态:
public class OuterClass
{
protected class InnerClass
{
}
}
2.其它修饰符
修饰符 | 应用于 | 说明 |
---|---|---|
new | 函数 | 隐藏函数 |
static | 所有成员 | 静态 |
virtual | 函数 | 成员可以由派生类重写 |
abstract | 类,函数 | 抽象 |
override | 函数 | 重写虚拟和抽象的成员 |
sealed | 类,方法,属性 | 不能继承和重写 |
extern | 仅静态方法 | 成员在外部用另一种语言实现 |
四.接口
public interface IDisposable
{
void Dispose();
}
声明接口在语法上和声明抽象类完全相同,但不允许提供任何成员的实现方式。抽象类可以提供除方法之外的其它成员的实现方式,比如属性。
一般情况下,接口只能包含方法,属性,索引器和事件的声明。
不能实例化接口,接口即不能有构造函数,也不能有字段。接口定义也不允许包含运算符重载。
在接口中不允许声明关于成员的修饰符。接口成员总是公有的,不能声明为虚拟和静态。如果需要,在实现的类中声明。
实现接口的类必须实现接口的所有成员。
接口可以彼此继承,其方式与类的继承方式相同。
来源:https://www.cnblogs.com/afei-24/p/6722417.html


猜你喜欢
- 本文实例为大家分享了Android自定义View实现滑动回弹的具体代码,供大家参考,具体内容如下前言Android 页面滑动的时候的回弹效果
- 页眉位于文档中每个页面的顶部区域,常用于显示文档的附加信息,可以插入时间、图形、公司微标、文档标题、文件名或作者姓名等;页脚位于文档中每个页
- Dubbo作为国内最出名的分布式服务框架,是Java程序员必备必会的框架之一,更是中高级测试面试过程中经常会问的技术,无论你是否用过,你都必
- 基于Java语言实现Socket通信由于近日项目需求,需要在服务器中增加Socket通信的功能,接收硬件设备发送的心跳包和相关数据,因此又重
- JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在
- 本文实例为大家分享了C++实现希尔排序的具体代码,供大家参考,具体内容如下一、思路:希尔排序:又称缩小增量排序,是一种改进的插入排序算法,是
- 要说哪门后端语言的语法优雅,那就不得不提C#,而在我看来,LINQ语法可以说是其优雅的重要一环!通常后端从数据库查询出来的数据并不是直接返回
- namespace ConsoleApplication1{ using System; &n
- /// 构造随机数 种子static int GetRandomSeed(){ byte[] byt
- VelocityTracker顾名思义即速度跟踪,在android中主要应用于touch even。Velocit
- java中实现list或set转map的方法在开发中我们有时需要将list或set转换为map(比如对象属性中的唯一键作为map的key,对
- Android 4.0 系统定义了一系列的高效导航方式 (Effective Navigation), 主要包括标签、下拉列表、以及向上和返
- 试用了Overt.Core.Grpc, 把 GRPC 的使用改造得像 WCF, 性能测试也非常不错, 非常推荐各位使用.但已有项目大多是 h
- Android webview在默认情况下是不支持网页中的文件上传功能的;如果在网页中有<input type="file&
- Android的Camera相关应用开发中,有一个必须搞清楚的知识点,就是Camera的预览方向和拍照方向图像的Sensor方向:手机Cam
- Selenium.WebDriverSelenium WebDriver 是一组开源 API,用于自动测试 Web 应用程序,利用它可以通过
- 首先介绍一些乐观锁与悲观锁:悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个
- 前言最近在学习Spring Boot结合Redis时看了一些网上的教程,发现这些教程要么比较老,要么不知道从哪抄得,运行起来有问题。这里分享
- 大致流程客户端根据远程服务的地址,客户端发送请求至服务端,服务端解析信息并找到对应的实现类,进行方法调用,之后将调用结果原路返回,客户端解析
- 前言一个说难不难,说简单竟看不出来是哪里问题的一个bug。是的 可能自己能力和经验尚浅无法识别,下面你们能否用火眼金睛一眼让bug原形毕露(