封装
访问修饰符
public
:完全公开private
:只有类内部能够访问internal
:同项目中能够访问protected
:类和派生类可以访问protected internal
:internal和protected二者的结合
封装
封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。
意义
封装的意义在于保护或者防止代码(数据)被我们无意中破坏。在面向对象程序设计中数据被看作是一个中心的元素并且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。
封装提供了一个有效的途径来保护数据不被意外的破坏。相比我们将数据(用域来实现)在程序中定义为公用的( public
)我们将它们( fields
)定义为私有的( privat
)在很多方面会更好。私有的数据可以用两种方式来间接的控制。
第一种方法,我们使用传统的存、取方法。第二种方法我们用属性(property)。使用属性不仅可以控制存取数据的合法性,同时也提供了“读写”、“只读”、“只写”灵活的操作方法。继承:继承主要实现重用代码,节省开发时间
继承
意义
继承主要实现重用代码,节省开发时间。
继承的规则
继承是可传递的。如果 C
从 B
中派生, B
又从 A
中派生,那么 C
不仅继承了 B
中声明的成员,同样也继承了 A
中的成员。 object
类作为所有类的基类。
派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。
构造函数和析构函数不能被继承。除此之外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。
派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。
类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性。
关键字
- 重载:继承中的重载和类内的成员方法的重载是一样的,只要在子类中新建了和父类中同名的但参数列表不同的成员方法就重载了父类的成员方法,但前题是要能继承该成员方法。
- 重写:是在子类中重新改写从父类中继承过来的某个方法,用新的方法代替原有的方法,这里要用关键字virtual override。
- 隐藏:也是在子类中重新改写从父类中继承过来的某个方法,但不是把父类的方法替换掉,而是隐藏起来,要用关键字new。
- base:可以调用父类的成员方法,除了构造函数和析构函数,派生类将隐式的继承了继承基类的所有成员。也可以显示的调用父类的构造函数来构造子类的成员数据。
- this:引用类当前的实例,还用于将对象传递到属于其他类的方法。
多态
new
的用法
当派生类与基类型相同的方法使用 new
修饰时,派生类对象转换后,调用的是基类的方法。其实可以理解为,使用 new
关键字后,使得派生类中的方法和基类中的方法成为毫不相关的两个方法,只是它们的名字碰巧相同而已。所以,基类中的方法不管用还是不用 virtual
修饰,也不管访问权限如何,或者是没有,都不会对派生类的方法产生什么影响(只是因为使用了 new
关键字,如果派生类没用 new
关键字从基类继承同名方法,编译器会输出警告)。这可能是设计者有意这么设计的,因为有时候我们就是要达到这种效果。严格的说,不能说通过使用 new
来实现多态,只能说在某些特定的时候碰巧实现了多态的效果。
override
实现多态
真正的多态使用 override
来实现的,在基类中将方法用virtual标记为虚拟方法,再在派生类中用 override
对需要重写的方法修饰进行重写,就可以很简单的实现多态。需要注意的是,要对一个类中一个方法用 override
修饰,该类必须从父类中继承了一个对应的用 virtual
修饰的虚拟方法,否则编译器将报错。如果父类方法用 override
修饰,如果子类继承了该方法,也可以用 override
修饰,多层继承中的多态就是这样实现的。要想终止这种重写,只需重写方法时用 sealed
关键字进行修饰即可。
abstract-override
实现多态
abstract
修饰的抽象方法只是对方法进行了定义,而没有实现,如果一个类包含了抽象方法,那么该类也必须用 abstract
声明为抽象类,一个抽象类是不能被实例化的。对于类中的抽象方法,可以再其派生类中用 override
进行重写,如果不重写,其派生类也要被声明为抽象类。
abstract-override
可以和 virtual-override
一样地实现多态,包括多层继承也是一样的。不同之处在于,包含虚拟方法的类可以被实例化,而包含抽象方法的类不能被实例化。
接口实现多态
接口存在的意义就是为了实现多态,另外接口解决了类的多继承问题,解决了类继承以后体积庞大的问题,接口之间可以实现多继承。接口中的成员必须不能有实现,接口不能实例化,成员不能有访问修饰符(隐式公开),接口中可以有属性,方法,索引器等(属性,索引器本质上也是方法),但不能有字段,接口中的所有方法必须被子类中全部实现(除非子类是抽象类,把接口中的成员标记为抽象的)。一个类只能继承一个父类,但是可以实现多个接口,接口只能决定能干什么,怎么干由类来实现。
注意
- 接口是一种规范。为了多态。
- 接口不能被实例化。
- 接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为
public
,不能修改。(默认为public
) - 接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。
- 接口中只能有方法、属性、索引器、事件,不能有“字段”。
- 接口与接口之间可以继承,并且可以多继承。
- 实现接口的子类必须实现该接口的全部成员。
- 一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类
A
,并实现了接口IA
,那么语法上A必须写在IA
的前面。class MyClass:A,IA{}
,因为类是单继承的。 - 当一个抽象类实现接口的时候,如果不想把接口中的成员实现,可以把该成员实现为
abstract
。(抽象类也能实现接口,用abstrac
标记) - “显示实现接口”,只能通过接口变量来调用 (因为显示实现接口后成员为
private
)。
接口和抽象类的区别
- 抽象类适用于同一系列,并且有需要继承的成员。
- 接口适用于不同系列的类具有相同的动作(行为、动作、方法)。对于不是相同的系列,但具有相同的行为,这个就考虑使用接口。
- 接口解决了类不能多继承问题。
- 接口定义类的公共行为,抽象类定义类的公共实现。
- 一个类只能继承自一个类(抽象类),但是可以同时实现多个接口。
- 接口中不能有实现,抽象类中可以有未实现成员也可以有实现的成员。
- 接口中未实现的方法必须在子类中实现,抽象类中未实现的成员必须在子类中重写。
接口和类的异同
- 不同点:不能直接实例化接口。接口不包含方法的实现。接口可以多继承,类只能单继承。类定义可在不同的源文件之间进行拆分。
- 相同点:接口、类和结构都可以从多个接口继承。接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。接口和类都可以包含事件、索引器、方法和属性。
C#支持多重继承么?
类之间不支持,接口之间支持。类对接口叫做实现,不叫继承。类是父亲、接口是能力,能有多个能力,但不能有多个父亲。