首页 > 代码库 > 工作中能用到的基础知识总结(二)
工作中能用到的基础知识总结(二)
简介
继承、封装和多态是面向对象编程的重要特性。要想运用好,就必须熟悉这三种特性,本篇说说我对封装、继承和多态相关的知识总结。
知识点
一、访问修饰符
C#中类及类型成员修饰符有五类:public,private,protected,internal,protected internal。
- public:类及类型成员的修饰符(任何地方该类都可以被访问到);
- private:类型成员的修饰符(只能在同一个类或方法中使用,即使是类的实例也不能访问它的私有成员);
- protected:类型成员的修饰符(可以被子类继承,并可被访问,类的实例不能访问它的私有成员);
- internal:类及类型成员的修饰符(只有在同一程序集内,才可以被访问到);
- protected internal:同一程序集或继承的子类可访问(protected or internal)。
可以放在class前面修饰类的修饰符关键字只有public和internal,如果没写任何关键字,默认是internal。类型成员前如果没写任何关键字,默认为private。
二、封装详解
面向对象编程,最主要的就是对象。每个对象就是一个封装,封装就是对象的可变部分信息进行隐藏,只是提供稳定的部分的访问接口,使它的使用者无法看到对象的具体信息,
而对象本身包含能进行操作所需的所有信息,不必依赖其他对象来完成自己的操作。eg:在用类实现某个逻辑的时候,类就是一个单独功能块,实现功能的具体代码就是可变的部分,
而public的方法或者属性则是稳定的部分。
封装好处
- 使用者只需要了解如何通过类的接口使用类,而不用关心类的内部数据结构和数据组织方法。
- 良好的封装能够减少耦合。
- 类内部的实现可以自由的修改,而对外接口不改变情况下不影响其他类。
- 类具有了简洁清晰的对外接口,降低了使用者的学习过程。
封装实现
通过对属性的读和写来保护类中的域。属性具有两种操作get和set。get用来返回属性域的值。set通过value这个变量来给属性域赋值。属性可以设为只读的(read-only),
只需属性只具有一个set操作;属性也可以是只写的(write-only),只需属性只具有一个get操作。使用属性的好处在于对象的使用者可以用一条语句来操作内部的数据。
三、继承相关的关键字
与继承相关的关键字有:base,virtual,override,abstract,new,sealed。
- base 关键字
可从子类中访问基类中非private关键字修饰的非抽象成员;
指定创建子类实例时调用带有相应参数的基类的构造函数(将先调用基类的相同参数的构造函数);
不能在静态方法中使用base关键字。
- virtual关键字
在基类中表示指定一个虚方法(有方法体)或属性,该方法或属性可以在子类中被override重写,new弃用,sealed隐藏或者直接继承。
- abstract关键字
在基类中表示指定抽象类或抽象方法(没有实现体的虚方法)。
如果是抽象方法方法,必须被子类用override关键字来覆写,抽象类不能够实例化;如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含一般方法。
- override关键字
在子类中重写父类的虚方法( 被virtual或 override 修饰)或抽象方法(被abstract或override修饰)。
- new关键字
显式隐藏从基类继承的成员,在子类中修饰基类中有或无virtual声明的方法。
如果在基类中有与子类同名同参的方法时,会隐式在子类前添加一个new关键字;当当前子类再派生孙子类的时候,派生的孙子类继承子类new关键字修饰的方法(非孙子类实例化赋值给爷爷类情况);new还可以与virtual一起使用。
使用 new 关键字时,调用的是新的类成员而不是已被替换的基类成员。如果需要调用隐藏类成员,可以将派生类的实例强制转换为基类的实例。
- sealed关键字
密封类/方法/属性。
修饰方法的时候,子类不能重写父类该方法,且只能够密封需重写的方法;修饰类的时候,其他类无法再继承该类;修饰属性的时候,类的密封属性不允许在派生类中被继承,密封属性的访问器同样也是密封的。
四、继承详解
对象初始的化顺序
方便后续理解,在谈继承之前,先说说对象初始的化顺序。在一个继承链中,对象初始化顺序为:先静态化后实例化,先变量后构造函数,先子类后父类。eg:比如C类派生自B类,B类派生自A类。
那么当实例化对象C的时候,实例化顺序为C的静态变量-》C的静态构造函数-》C的变量-》B的静态变量-》B的静态构造函数-》B的变量-》A的静态变量-》A的静态构造函数-》A的变量-》
A的非静态构造函数-》B的非静态构造函数-》C的非静态构造函数。如下图:
继承就是在类之间建立一种相交关系,使得新定义的派生类的实例可以继承已有的基类的成员:方法、域、属性、事件、索引指示器。除了构造函数和析构函数,派生类隐式地继承了直接基类的所有成员,
还可以加入新的特性或修改已有的特性建立起类的新层次。继承是面向对象程序设计的主要特性之一,它使得所有子类的公共部分都在放在父类,使得代码得到共享,避免了重复编码;使得修改和扩展继承而来的实现较为方便。
但是要注意的是,继承是强耦合关系的,更改父类,其子类都要随之更改。在设计程序的时候,父类公共方法一定要设计好,劲量避免后期更改父类。
继承遵循的规则
- 继承是可传递的。如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object 类作为所有类的基类。
- 派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。
- 构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。
- 派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。
- 类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性。
- 派生类只能从一个类中继承,可以通过接口实现多重继承。
继承实现方式
继承实现方式分为三种:实现继承、接口继承和可视继承。
- 实现继承:指使用基类的属性和方法而无需额外编码的能力。
- 可视继承:指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
- 接口继承:指仅使用属性和方法的名称、但是子类必须提供实现的能力。C#不支持多重继承,但是客观世界出现多重继承的情况又比较多。为了避免传统的多重继承给程序带来的复杂性等问题,
C# 提出了接口的概念。通过接口可以实现多重继承的功能。
五、多态详解
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。通俗点讲,就是:子类以父类的身份出现;子类在工作时以自己的方式实现父类功能;
子类以父类的身份出现时,子类特有的属性和方法不可以使用。 eg:从某个基类继承出多个对象,其基类有一个虚方法virtualFunc,然后其子类也有这个方法,但行为不同,然后这些子对象中的任何一个可以赋给其基类对象的引用,
这样其基类的对象就可以执行不同的操作了。实际上是在通过其基类来访问其子对象的,要做的就是一个赋值操作。
注:当方法被调用时,无论对象是否被转换成其父类,都只有位于对象继承链最末端的方法实现被调用。也就是说,虚方法是按照其运行时类型而非编译时类型进行动态绑定调用的。
支持的类型
多态是基于继承的,继承的多层次变化就是多态的一种表现形式。C#中支持两种不同类型的多态:编译时的多态性和运行时的多态性。
编译时的多态性:通过重载(方法名必须相同;参数列表必须不相同;返回值类型可以不相同)来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
运行时的多态性:直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现,即使用新的派生成员替换基成员或者可以重写虚拟的基成员。
使用新的派生成员替换基类的成员需要使用 new 关键字。如果基类定义了一个方法、字段或属性,则 new 关键字用于在派生类中创建该方法、字段或属性的新定义。重写虚拟基类成员是通过使用override关键字来实现覆写。
编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。
实现方式(摘计算机世界网)
接口多态性: 多个类可实现相同的“接口”,而单个类可以实现一个或多个接口。接口本质上是类需要如何响应的定义。接口描述类需要实现的方法、属性和事件,以及每个成员需要接收和返回的参数类型,
但将这些成员的特定实现留给实现类去完成。 补充了C#不能多重继承缺点。
继承实现的多态性 :多个类可以从单个基类“继承”。通过继承,类在基类所在的同一实现中接收基类的所有方法、属性和事件。这样,便可根据需要来实现附加成员,而且可以重写基成员以提供不同的实现。C# 通过继承提供多态性。
对于小规模开发任务而言,这是一个功能强大的机制,但对于大规模系统,通常证明会存在问题。过分强调继承驱动的多态性一般会导致资源大规模地从编码转移到设计,这对于缩短总的开发时间没有任何帮助。 何时使用继承驱动的多态性呢?
使用继承首先是为了向现有基类添加功能。若从经过完全调试的基类框架开始,则程序员的工作效率将大大提高,方法可以增量地添加到基类而不中断版本。当应用程序设计包含多个相关类,而对于某些通用函数,
这些相关类必须共享同样的实现时,您也可能希望使用继承。重叠功能可以在基类中实现,应用程序中使用的类可以从该基类中派生。抽象类合并继承和实现的功能,这在需要二者之一的元素时可能很有用。
抽象类实现的多态性 :抽象类同时提供继承和接口的元素。抽象类本身不能实例化,它必须被继承。该类的部分或全部成员可能未实现,该实现由继承类提供。已实现的成员仍可被重写,并且继承类仍可以实现附加接口或其他功能。
抽象类提供继承和接口实现的功能。抽象类不能示例化,必须在继承类中实现。它可以包含已实现的方法和属性,但也可以包含未实现的过程,这些未实现过程必须在继承类中实现。这使您得以在类的某些方法中提供不变级功能,
同时为其他过程保持灵活性选项打开。抽象类的另一个好处是:当要求组件的新版本时,可根据需要将附加方法添加到基类,但接口必须保持不变。 何时使用抽象类呢?当需要一组相关组件来包含一组具有相同功能的方法,
但同时要求在其他方法实现中具有灵活性时,可以使用抽象类。当预料可能出现版本问题时,抽象类也具有价值,因为基类比较灵活并易于被修改。
工作中能用到的基础知识总结(二)