类的继承、sealed关键字总结

第五周总结

类的继承

继承是面向对象编程的一个重要特性,任何类都可以从另一个类中继承,这就是说,这个类拥有它继承的类的所有成员。在c#中,被继承的类称为父类(也称为基类)。

注意,C#中的对象仅能直接派生于一个基类,当然基类也可以有自己的基类。

其次类的继承一个重要作用是子类可重用父类的代码。这样就可以在一个地方集中维护一份代码,避免了很多的重复的代码。

基类的初始化

派生类继承了基类的成员变量和成员方法。因此父类对象应在子类对象创建之前被创建。您可以在成员初始化列表中进行父类的初始化。

注意:C# 不支持多重继承。但是,可以使用接口来实现多重继承。

继承规则:

1、派生类自动包含基类的所有成员。但对于基类的私有成员,派生类虽然继承了,但是不能在派生类中访问。

2、所有的类都是按照继承链从顶层基类开始向下顺序构造。最顶层的基类是System.Object类,所有的类都隐式派生于它。只要记住这条规则,就能理解派生类在实例化时对构造函数的调用过程。

隐藏基类的方法

隐藏基类方法和重写基类方法的一点区别,二者都是在派生类中定义了与基类中相同的方法,相同点派生类对象将执行各自的派生类中的方法,不同点,在向上转型后,重写基类方法用的是派生类的方法,而隐藏基类调用的是基类的方法,

装箱与拆箱

装箱与拆箱:在实际开发过程中,某些方法的参数类型为引用类型,但是如果传入的是值类型,此时需要进行装箱操作。同样当一个方法的返回值类型为值类型,但实际方法返回值是引用类型,那么就需要进行拆箱操作。简单来说,装箱就是将值类型

一、为什么需要装箱(为何要将值类型转为引用类型?)

一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。

另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。

二、装箱和拆箱的内部操作是什么样的?

.NET中,数据类型划分为值类型和引用(不等同于C++的指针)类型,与此对应,内存分配被分成了两种方式,一为栈,二为堆,注意:是托管堆。 值类型只会在栈中分配。 引用类型分配内存与托管堆。(托管堆对应于垃圾回收。) 装箱操作:

1:首先从托管堆中为新生成的引用对象分配内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。

2:然后将值类型的数据拷贝到刚刚分配的内存中。

3:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。

可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。

拆箱操作:

1、首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱。

2、将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。

经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。

三、装箱/拆箱对执行效率的影响

显然,从原理上可以看出,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。 那该如何做呢?

首先,应该尽量避免装箱。

有两种情况,在第一种情况下,可以通过重载函数来避免。第二种情况,则可以通过泛型来避免。

当然,凡事并不能绝对,假设你想改造的代码为第三方程序集,你无法更改,那你只能是装箱了。

对于装箱/拆箱代码的优化,由于C#中对装箱和拆箱都是隐式的,所以,根本的方法是对代码进行分析,而分析最直接的方式是了解原理结何查看反编译的IL代码。比如:在循环体中可能存在多余的装箱,你可以简单采用提前装箱方式进行优化。

Sealed关键字

Sealed关键字:在c#中,使用sealed关键字修饰的类不可以继承,也就是说不能派生子类,这样的类通常被称为密封类,在哪些场合下使用密封类呢?密封类可以阻止其它程序员在无意中继承该类。而且密封类可以起到运行时优化的效果。实际上,密封类中不可能有派生类。如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。

C#中,sealed关键字有两个作用

1. 为了确保其他类不可以派生于某一个类,可以使用sealed关键字密封该类,防止其他类继承自该类;

注意: 密封类中不能包含虚方法(Virtual)和抽象方法(abstract),因为在密封的类没有为派生类提供实现其虚方法和抽象方法的机会。

2.限制其他派生类重写在当前类中提供的方法实现

posted @ 2019-03-25 18:16  里昂IT  阅读(434)  评论(0编辑  收藏  举报